summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/parser.nim9
-rw-r--r--compiler/pretty.nim12
-rw-r--r--examples/talk/dsl.nim33
-rw-r--r--examples/talk/formatoptimizer.nim55
-rw-r--r--examples/talk/hoisting.nim23
-rw-r--r--examples/talk/lazyeval.nim12
-rw-r--r--examples/talk/quasiquote.nim11
-rw-r--r--examples/talk/tags.nim8
-rw-r--r--lib/pure/sockets.nim4
-rw-r--r--lib/system.nim21
-rw-r--r--lib/system/gc.nim136
-rw-r--r--lib/system/mmdisp.nim2
-rw-r--r--tests/run/tindent1.nim (renamed from tests/compile/tindent1.nim)18
-rw-r--r--tests/tester.nim1
-rw-r--r--todo.txt3
-rw-r--r--tools/niminst/niminst.nim13
16 files changed, 314 insertions, 47 deletions
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 6e2d0867b..e8439466a 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1798,7 +1798,9 @@ proc parseStmt(p: var TParser): PNode =
         if p.tok.indent == p.currInd:
           nil
         elif p.tok.tokType == tkSemicolon:
-          while p.tok.tokType == tkSemicolon: getTok(p)
+          getTok(p)
+          if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
+          else: break
         else:
           if p.tok.indent > p.currInd:
             parMessage(p, errInvalidIndentation)
@@ -1851,7 +1853,10 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       else: parMessage(p, errInvalidIndentation)
     p.firstTok = false
     case p.tok.tokType
-    of tkSemicolon: getTok(p)
+    of tkSemicolon:
+      getTok(p)
+      if p.tok.indent <= 0: discard
+      else: parMessage(p, errInvalidIndentation)
     of tkEof: break
     else:
       result = complexOrSimpleStmt(p)
diff --git a/compiler/pretty.nim b/compiler/pretty.nim
index 4545e1c55..5036a16a3 100644
--- a/compiler/pretty.nim
+++ b/compiler/pretty.nim
@@ -55,6 +55,10 @@ proc overwriteFiles*() =
     except EIO:
       rawMessage(errCannotOpenFile, newFile)
 
+proc `=~`(s: string, a: openArray[string]): bool =
+  for x in a:
+    if s.startsWith(x): return true
+
 proc beautifyName(s: string, k: TSymKind): string =
   result = newStringOfCap(s.len)
   var i = 0
@@ -64,7 +68,13 @@ proc beautifyName(s: string, k: TSymKind): string =
     when removeTP:
       if s[0] == 'T' and s[1] in {'A'..'Z'}:
         i = 1
-    result.add toUpper(s[i])
+    if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
+             "char", "byte", "bool", "openArray", "seq", "array", "void",
+             "pointer", "float", "csize", "cdouble", "cchar", "cschar",
+             "cshort", "cu"]:
+      result.add s[i]
+    else:
+      result.add toUpper(s[i])
   of skConst, skEnumField:
     # for 'const' we keep how it's spelt; either upper case or lower case:
     result.add s[0]
diff --git a/examples/talk/dsl.nim b/examples/talk/dsl.nim
new file mode 100644
index 000000000..4dfab5cd7
--- /dev/null
+++ b/examples/talk/dsl.nim
@@ -0,0 +1,33 @@
+
+import strutils
+
+template html(name: expr, matter: stmt) {.immediate.} =
+  proc name(): string =
+    result = "<html>"
+    matter
+    result.add("</html>")
+
+template nestedTag(tag: expr) {.immediate.} =
+  template tag(matter: stmt) {.immediate.} =
+    result.add("<" & astToStr(tag) & ">")
+    matter
+    result.add("</" & astToStr(tag) & ">")
+
+template simpleTag(tag: expr) {.immediate.} =
+  template tag(matter: expr) {.immediate.} =
+    result.add("<$1>$2</$1>" % [astToStr(tag), matter])
+
+nestedTag body
+nestedTag head
+nestedTag ul
+simpleTag title
+simpleTag li
+
+html mainPage:
+  head:
+    title "now look at this"
+  body:
+    ul:
+      li "Nimrod is quite capable"
+
+echo mainPage()
diff --git a/examples/talk/formatoptimizer.nim b/examples/talk/formatoptimizer.nim
new file mode 100644
index 000000000..db11d112d
--- /dev/null
+++ b/examples/talk/formatoptimizer.nim
@@ -0,0 +1,55 @@
+## This is the example that optimizes a modified "hello world"
+
+import macros
+
+proc invalidFormatString() =
+  echo "invalidFormatString"
+
+template formatImpl(handleChar: expr) =
+  var i = 0
+  while i < f.len:
+    if f[i] == '$':
+      case f[i+1]
+      of '1'..'9':
+        var j = 0
+        i += 1
+        while f[i] in {'0'..'9'}:
+          j = j * 10 + ord(f[i]) - ord('0')
+          i += 1
+        result.add(a[j-1])
+      else:
+        invalidFormatString()
+    else:
+      result.add(handleChar(f[i]))
+      i += 1
+
+proc `%`*(f: string, a: openArray[string]): string =
+  template identity(x: expr): expr = x
+  result = ""
+  formatImpl(identity)
+
+macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): expr =
+  result = newNimNode(nnkBracket)
+  #newCall("&")
+  let f = f.strVal
+  formatImpl(newLit)
+  result = nestList(!"&", result)
+
+template optAdd1{x = y; add(x, z)}(x, y, z: string) =
+  x = y & z
+
+#template optAdd2{x.add(y); x.add(z)}(x, y, z: string) =
+#  x.add(y & z)
+
+proc `/&` [T: object](x: T): string =
+  result = "("
+  for name, value in fieldPairs(x):
+    result.add("$1: $2\n" % [name, $value])
+  result.add(")")
+
+type
+  MyObject = object
+    a, b: int
+    s: string
+let obj = MyObject(a: 3, b: 4, s: "abc")
+echo(/&obj)
diff --git a/examples/talk/hoisting.nim b/examples/talk/hoisting.nim
new file mode 100644
index 000000000..df13ba2cb
--- /dev/null
+++ b/examples/talk/hoisting.nim
@@ -0,0 +1,23 @@
+type
+  Regex = distinct string
+
+const maxSubpatterns = 10
+
+proc re(x: string): Regex =
+  result = Regex(x)
+
+proc match(s: string, pattern: Regex, captures: var openArray[string]): bool =
+  true
+
+template optRe{re(x)}(x: string{lit}): Regex =
+  var g {.global.} = re(x)
+  g
+
+template `=~`(s: string, pattern: Regex): bool =
+  when not definedInScope(matches):
+    var matches {.inject.}: array[maxSubPatterns, string]
+  match(s, pattern, matches)
+
+for line in lines("input.txt"):
+  if line =~ re"(\w+)=(\w+)":
+    echo "key-value pair; key: ", matches[0], " value: ", matches[1]
diff --git a/examples/talk/lazyeval.nim b/examples/talk/lazyeval.nim
new file mode 100644
index 000000000..77d963834
--- /dev/null
+++ b/examples/talk/lazyeval.nim
@@ -0,0 +1,12 @@
+
+const
+  debug = true
+
+template log(msg: string) =
+  if debug:
+    echo msg
+var
+  x = 1
+  y = 2
+
+log("x: " & $x & ", y: " & $y)
diff --git a/examples/talk/quasiquote.nim b/examples/talk/quasiquote.nim
new file mode 100644
index 000000000..df4003e6e
--- /dev/null
+++ b/examples/talk/quasiquote.nim
@@ -0,0 +1,11 @@
+
+import macros
+
+macro check(ex: expr): stmt =
+  var info = ex.lineinfo
+  var expString = ex.toStrLit
+  result = quote do:
+    if not `ex`:
+      echo `info`, ": Check failed: ", `expString`
+
+check 1 < 2
diff --git a/examples/talk/tags.nim b/examples/talk/tags.nim
new file mode 100644
index 000000000..d47b09e07
--- /dev/null
+++ b/examples/talk/tags.nim
@@ -0,0 +1,8 @@
+
+template htmlTag(tag: expr) {.immediate.} =
+  proc tag(): string = "<" & astToStr(tag) & ">"
+  
+htmlTag(br)
+htmlTag(html)
+
+echo br()
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index 73a189fee..fe4d3c2a4 100644
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -11,11 +11,11 @@
 ## of sockets. Sockets are buffered by default meaning that data will be
 ## received in ``BufferSize`` (4000) sized chunks, buffering
 ## behaviour can be disabled by setting the ``buffered`` parameter when calling
-## the ``socket`` function to `False`. Be aware that some functions may not yet
+## the ``socket`` function to `false`. Be aware that some functions may not yet
 ## support buffered sockets (mainly the recvFrom function).
 ##
 ## Most procedures raise EOS on error, but some may return ``-1`` or a boolean
-## ``False``.
+## ``false``.
 ##
 ## SSL is supported through the OpenSSL library. This support can be activated
 ## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
diff --git a/lib/system.nim b/lib/system.nim
index 4ff1f1577..51f325997 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -77,17 +77,6 @@ type
 
   TNumber* = TInteger|TReal
     ## type class matching all number types
-
-type
-  ## helper types for writing implicitly generic procs
-  T1* = expr
-  T2* = expr
-  T3* = expr
-  T4* = expr
-  T5* = expr
-  type1* = typedesc
-  type2* = typedesc
-  type3* = typedesc
   
 proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
   ## Special compile-time procedure that checks whether `x` is
@@ -2509,7 +2498,7 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
   ## converts the AST of `x` into a string representation. This is very useful
   ## for debugging.
   
-proc InstantiationInfo*(index = -1, fullPaths = false): tuple[
+proc instantiationInfo*(index = -1, fullPaths = false): tuple[
   filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.}
   ## provides access to the compiler's instantiation stack line information.
   ##
@@ -2540,7 +2529,7 @@ proc InstantiationInfo*(index = -1, fullPaths = false): tuple[
   ##     testException(EInvalidIndex, tester(1))
   ##     # --> Test failure at example.nim:20 with 'tester(1)'
 
-template CurrentSourcePath*: string = InstantiationInfo(-1, true).filename
+template currentSourcePath*: string = instantiationInfo(-1, true).filename
   ## returns the full file-system path of the current source
 
 proc raiseAssert*(msg: string) {.noinline.} =
@@ -2560,7 +2549,7 @@ template assert*(cond: bool, msg = "") =
   ## raises an ``EAssertionFailure`` exception. However, the compiler may
   ## not generate any code at all for ``assert`` if it is advised to do so.
   ## Use ``assert`` for debugging purposes only.
-  bind InstantiationInfo, hiddenRaiseAssert
+  bind instantiationInfo, hiddenRaiseAssert
   when compileOption("assertions"):
     {.line.}:
       if not cond:
@@ -2569,8 +2558,8 @@ template assert*(cond: bool, msg = "") =
 template doAssert*(cond: bool, msg = "") =
   ## same as `assert` but is always turned on and not affected by the
   ## ``--assertions`` command line switch.
-  bind InstantiationInfo
-  {.line: InstantiationInfo().}:
+  bind instantiationInfo
+  {.line: instantiationInfo().}:
     if not cond:
       raiseAssert(astToStr(cond) & ' ' & msg)
 
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index f5b68b9db..c5d4d2aa2 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -25,6 +25,10 @@ const
                       # reaches this threshold
                       # this seems to be a good value
   withRealTime = defined(useRealtimeGC)
+  useMarkForDebug = defined(gcGenerational)
+  useBackupGc = false                     # use a simple M&S GC to collect
+                                          # cycles instead of the complex
+                                          # algorithm
 
 when withRealTime and not defined(getTicks):
   include "system/timers"
@@ -42,8 +46,10 @@ const
   colorMask = 0b011
 type
   TWalkOp = enum
+    waMarkGlobal,    # part of the backup/debug mark&sweep
+    waMarkPrecise,   # part of the backup/debug mark&sweep
     waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, 
-    waCollectWhite
+    waCollectWhite,
 
   TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
     # A ref type can have a finalizer that is called before the object's
@@ -71,6 +77,8 @@ type
       maxPause: TNanos       # max allowed pause in nanoseconds; active if > 0
     region: TMemRegion       # garbage collected region
     stat: TGcStat
+    when useMarkForDebug or useBackupGc:
+      marked: TCellSet
 
 var
   gch {.rtlThreadVar.}: TGcHeap
@@ -80,7 +88,7 @@ when not defined(useNimRtl):
 
 template acquire(gch: TGcHeap) = 
   when hasThreadSupport and hasSharedHeap:
-    AcquireSys(HeapLock)
+    acquireSys(HeapLock)
 
 template release(gch: TGcHeap) = 
   when hasThreadSupport and hasSharedHeap:
@@ -128,7 +136,7 @@ template setColor(c, col) =
   else:
     c.refcount = c.refCount and not colorMask or col
 
-proc writeCell(msg: CString, c: PCell) =
+proc writeCell(msg: cstring, c: PCell) =
   var kind = -1
   if c.typ != nil: kind = ord(c.typ.kind)
   when leakDetector:
@@ -159,6 +167,8 @@ else:
   template `++`(x: expr): stmt = Inc(x, rcIncrement)
 
 proc prepareDealloc(cell: PCell) =
+  when useMarkForDebug:
+    gcAssert(cell notin gch.marked, "Cell still alive!")
   if cell.typ.finalizer != nil:
     # the finalizer could invoke something that
     # allocates memory; this could trigger a garbage
@@ -172,21 +182,21 @@ proc prepareDealloc(cell: PCell) =
 proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = 
   # we MUST access gch as a global here, because this crosses DLL boundaries!
   when hasThreadSupport and hasSharedHeap:
-    AcquireSys(HeapLock)
+    acquireSys(HeapLock)
   when cycleGC:
     if c.color != rcPurple:
       c.setColor(rcPurple)
       incl(gch.cycleRoots, c)
   when hasThreadSupport and hasSharedHeap:
-    ReleaseSys(HeapLock)
+    releaseSys(HeapLock)
 
 proc rtlAddZCT(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
   when hasThreadSupport and hasSharedHeap:
-    AcquireSys(HeapLock)
+    acquireSys(HeapLock)
   addZCT(gch.zct, c)
   when hasThreadSupport and hasSharedHeap:
-    ReleaseSys(HeapLock)
+    releaseSys(HeapLock)
 
 proc decRef(c: PCell) {.inline.} =
   gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
@@ -249,7 +259,7 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
   # unsureAsgnRef updates the reference counters only if dest is not on the
   # stack. It is used by the code generator if it cannot decide wether a
   # reference is in the stack or not (this can happen for var parameters).
-  if not IsOnStack(dest):
+  if not isOnStack(dest):
     if src != nil: incRef(usrToCell(src))
     # XXX finally use assembler for the stack checking instead!
     # the test for '!= nil' is correct, but I got tired of the segfaults
@@ -277,6 +287,27 @@ proc initGC() =
     init(gch.tempStack)
     init(gch.cycleRoots)
     init(gch.decStack)
+    when useMarkForDebug or useBackupGc:
+      init(gch.marked)
+
+when useMarkForDebug or useBackupGc:
+  type
+    TGlobalMarkerProc = proc () {.nimcall.}
+  var
+    globalMarkersLen: int
+    globalMarkers: array[0.. 7_000, TGlobalMarkerProc]
+
+  proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} =
+    if globalMarkersLen <= high(globalMarkers):
+      globalMarkers[globalMarkersLen] = markerProc
+      inc globalMarkersLen
+    else:
+      echo "[GC] cannot register global variable; too many global variables"
+      quit 1
+
+proc cellsetReset(s: var TCellSet) = 
+  deinit(s)
+  init(s)
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
   var d = cast[TAddress](dest)
@@ -534,7 +565,7 @@ proc collectWhite(s: PCell) =
     forAllChildren(s, waCollectWhite)
     freeCyclicCell(gch, s)
 
-proc MarkRoots(gch: var TGcHeap) =
+proc markRoots(gch: var TGcHeap) =
   var tabSize = 0
   for s in elements(gch.cycleRoots):
     #writeCell("markRoot", s)
@@ -548,6 +579,38 @@ proc MarkRoots(gch: var TGcHeap) =
         freeCyclicCell(gch, s)
   gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
 
+when useBackupGc:
+  proc sweep(gch: var TGcHeap) =
+    for x in allObjects(gch.region):
+      if isCell(x):
+        # cast to PCell is correct here:
+        var c = cast[PCell](x)
+        if c notin gch.marked: freeCyclicCell(gch, c)
+
+when useMarkForDebug or useBackupGc:
+  proc markS(gch: var TGcHeap, c: PCell) =
+    incl(gch.marked, c)
+    gcAssert gch.tempStack.len == 0, "stack not empty!"
+    forAllChildren(c, waMarkPrecise)
+    while gch.tempStack.len > 0:
+      dec gch.tempStack.len
+      var d = gch.tempStack.d[gch.tempStack.len]
+      if not containsOrIncl(gch.marked, d):
+        forAllChildren(d, waMarkPrecise)
+
+  proc markGlobals(gch: var TGcHeap) =
+    for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+
+  proc stackMarkS(gch: var TGcHeap, p: pointer) {.inline.} =
+    # the addresses are not as cells on the stack, so turn them to cells:
+    var cell = usrToCell(p)
+    var c = cast[TAddress](cell)
+    if c >% PageSize:
+      # fast check: does it look like a cell?
+      var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
+      if objStart != nil:
+        markS(gch, objStart)
+
 proc doOperation(p: pointer, op: TWalkOp) =
   if p == nil: return
   var c: PCell = usrToCell(p)
@@ -580,12 +643,26 @@ proc doOperation(p: pointer, op: TWalkOp) =
     if c.color != rcBlack:
       scanBlack(c)
   of waCollectWhite: collectWhite(c)
+  of waMarkGlobal:
+    when useMarkForDebug or useBackupGc:
+      when hasThreadSupport:
+        # could point to a cell which we don't own and don't want to touch/trace
+        if isAllocatedPtr(gch.region, c):
+          markS(gch, c)
+      else:
+        markS(gch, c)
+  of waMarkPrecise:
+    when useMarkForDebug or useBackupGc:
+      add(gch.tempStack, c)
 
 proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
   doOperation(d, TWalkOp(op))
 
 proc CollectZCT(gch: var TGcHeap): bool
 
+when useMarkForDebug or useBackupGc:
+  proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl.}
+
 proc collectRoots(gch: var TGcHeap) =
   for s in elements(gch.cycleRoots):
     excl(gch.cycleRoots, s)
@@ -594,13 +671,18 @@ proc collectRoots(gch: var TGcHeap) =
 proc collectCycles(gch: var TGcHeap) =
   # ensure the ZCT 'color' is not used:
   while gch.zct.len > 0: discard collectZCT(gch)
-  markRoots(gch)
-  # scanRoots:
-  for s in elements(gch.cycleRoots): scan(s)
-  collectRoots(gch)
+  when useBackupGc:
+    cellsetReset(gch.marked)
+    markStackAndRegistersForSweep(gch)
+    markGlobals(gch)
+    sweep(gch)
+  else:
+    markRoots(gch)
+    # scanRoots:
+    for s in elements(gch.cycleRoots): scan(s)
+    collectRoots(gch)
 
-  Deinit(gch.cycleRoots)
-  Init(gch.cycleRoots)
+    cellsetReset(gch.cycleRoots)
   # alive cycles need to be kept in 'cycleRoots' if they are referenced
   # from the stack; otherwise the write barrier will add the cycle root again
   # anyway:
@@ -695,7 +777,7 @@ when defined(sparc): # For SPARC architecture.
     var x = cast[TAddress](p)
     result = a <=% x and x <=% b
 
-  proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
+  template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
     when defined(sparcv9):
       asm  """"flushw \n" """
     else:
@@ -731,7 +813,7 @@ elif stackIncreases:
       # a little hack to get the size of a TJmpBuf in the generated C code
       # in a platform independant way
 
-  proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
+  template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
     var registers: C_JmpBuf
     if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
       var max = cast[TAddress](gch.stackBottom)
@@ -754,7 +836,7 @@ else:
     var x = cast[TAddress](p)
     result = a <=% x and x <=% b
 
-  proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
+  template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
     # We use a jmp_buf buffer that is in the C stack.
     # Used to traverse the stack and registers assuming
     # that 'setjmp' will save registers in the C stack.
@@ -778,12 +860,19 @@ else:
       while sp <=% max:
         gcMark(gch, cast[ppointer](sp)[])
         sp = sp +% sizeof(pointer)
+    
+proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
+  forEachStackSlot(gch, gcMark)
+
+when useMarkForDebug or useBackupGc:
+  proc markStackAndRegistersForSweep(gch: var TGcHeap) =
+    forEachStackSlot(gch, stackMarkS)
 
 # ----------------------------------------------------------------------------
 # end of non-portable code
 # ----------------------------------------------------------------------------
 
-proc CollectZCT(gch: var TGcHeap): bool =
+proc collectZCT(gch: var TGcHeap): bool =
   # Note: Freeing may add child objects to the ZCT! So essentially we do 
   # deep freeing, which is bad for incremental operation. In order to 
   # avoid a deep stack, we move objects to keep the ZCT small.
@@ -880,10 +969,19 @@ proc collectCTBody(gch: var TGcHeap) =
       if gch.maxPause > 0 and duration > gch.maxPause:
         c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
 
+when useMarkForDebug or useBackupGc:
+  proc markForDebug(gch: var TGcHeap) =
+    markStackAndRegistersForSweep(gch)
+    markGlobals(gch)
+
 proc collectCT(gch: var TGcHeap) =
   if (gch.zct.len >= ZctThreshold or (cycleGC and
       getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and 
       gch.recGcLock == 0:
+    when useMarkForDebug:
+      prepareForInteriorPointerChecking(gch.region)
+      cellsetReset(gch.marked)
+      markForDebug(gch)
     collectCTBody(gch)
 
 when withRealtime:
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 9f37e95c1..c9801abad 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -313,7 +313,7 @@ else:
     # XXX use 'compileOption' here
     include "system/gc_ms"
   elif defined(gcGenerational):
-    include "system/gc_genms"
+    include "system/gc"
   else:
     include "system/gc"
   
diff --git a/tests/compile/tindent1.nim b/tests/run/tindent1.nim
index 0527b6e57..78a303783 100644
--- a/tests/compile/tindent1.nim
+++ b/tests/run/tindent1.nim
@@ -1,3 +1,6 @@
+discard """
+  output: '''Success'''
+"""
 
 const romanNumbers1 =
     [
@@ -22,3 +25,18 @@ const romanNumbers4 = [
     ]
 
 
+proc main =
+  var j = 0
+  while j < 10:
+    inc(j);
+
+  if j == 5: doAssert false
+
+var j = 0
+while j < 10:
+  inc(j);
+
+if j == 5: doAssert false
+
+main()
+echo "Success"
diff --git a/tests/tester.nim b/tests/tester.nim
index e10b89761..fe21fc9ee 100644
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -402,6 +402,7 @@ proc main() =
     compileExample(r, "lib/pure/*.nim", p.cmdLineRest.string)
     compileExample(r, "examples/*.nim", p.cmdLineRest.string)
     compileExample(r, "examples/gtk/*.nim", p.cmdLineRest.string)
+    compileExample(r, "examples/talk/*.nim", p.cmdLineRest.string)
     compileSpecialTests(r, p.cmdLineRest.string)
     writeResults(compileJson, r)
   of "run":
diff --git a/todo.txt b/todo.txt
index 302a5eeaf..da7585500 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,13 +1,11 @@
 version 0.9.4
 =============
 
-- add the code from the talk to examples or run tests
 - new VM:
   - implement the glue to replace evals.nim
   - implement missing magics
   - implement overflow checking
   - implement the FFI
-  - implement on the fly CSE
 
 - make 'bind' default for templates and introduce 'mixin'
 - special rule for ``[]=``
@@ -160,3 +158,4 @@ Optimizations
   even further write barrier specialization
 - inlining of first class functions
 - proc specialization in the code gen for write barrier specialization
+- VM/optimizer: implement on the fly CSE
diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim
index 0feac6de8..e93a895e5 100644
--- a/tools/niminst/niminst.nim
+++ b/tools/niminst/niminst.nim
@@ -244,6 +244,7 @@ proc parseIniFile(c: var TConfigData) =
   var
     p: TCfgParser
     section = ""
+    hasCpuOs = false
   var input = newFileStream(c.infile, fmRead)
   if input != nil:
     open(p, input, c.infile)
@@ -265,16 +266,18 @@ proc parseIniFile(c: var TConfigData) =
           of "version": c.version = v
           of "os": 
             c.oses = split(v, {';'})
+            hasCpuOs = true
             if c.explicitPlatforms:
               quit(errorStr(p, "you cannot have both 'platforms' and 'os'"))
           of "cpu": 
             c.cpus = split(v, {';'})
+            hasCpuOs = true
             if c.explicitPlatforms:
               quit(errorStr(p, "you cannot have both 'platforms' and 'cpu'"))
           of "platforms": 
             platforms(c, v)
             c.explicitPlatforms = true
-            if c.cpus.len > 0 or c.oses.len > 0:
+            if hasCpuOs:
               quit(errorStr(p, "you cannot have both 'platforms' and 'os'"))
           of "authors": c.authors = split(v, {';'})
           of "description": c.description = v
@@ -407,6 +410,8 @@ proc writeFile(filename, content, newline: string) =
 proc removeDuplicateFiles(c: var TConfigData) =
   for osA in countdown(c.oses.len, 1):
     for cpuA in countdown(c.cpus.len, 1):
+      if c.cfiles[osA][cpuA].isNil: c.cfiles[osA][cpuA] = @[]
+      if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
       for i in 0..c.cfiles[osA][cpuA].len-1:
         var dup = c.cfiles[osA][cpuA][i]
         var f = extractFilename(dup)
@@ -437,12 +442,12 @@ proc srcdist(c: var TConfigData) =
   var intel64Index = -1
   for osA in 1..c.oses.len:
     let osname = c.oses[osA-1]
-    if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA-1
+    if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA
     for cpuA in 1..c.cpus.len:
       if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
       let cpuname = c.cpus[cpuA-1]
-      if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA-1
-      elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA-1
+      if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA
+      elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA
       var dir = getOutputDir(c) / buildDir(osA, cpuA)
       if existsDir(dir): removeDir(dir)
       createDir(dir)