summary refs log tree commit diff stats
path: root/lib/system
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system')
-rw-r--r--lib/system/alloc.nim186
-rw-r--r--lib/system/atomics.nim20
-rw-r--r--lib/system/chcks.nim1
-rw-r--r--lib/system/debugger.nim6
-rw-r--r--lib/system/endb.nim2
-rw-r--r--lib/system/excpt.nim120
-rw-r--r--lib/system/gc.nim25
-rw-r--r--lib/system/gc2.nim10
-rw-r--r--lib/system/gc_ms.nim6
-rw-r--r--lib/system/genodealloc.nim114
-rw-r--r--lib/system/jssys.nim12
-rw-r--r--lib/system/mmdisp.nim17
-rw-r--r--lib/system/nimscript.nim2
-rw-r--r--lib/system/osalloc.nim14
-rw-r--r--lib/system/repr.nim4
-rw-r--r--lib/system/reprjs.nim36
-rw-r--r--lib/system/sysio.nim16
-rw-r--r--lib/system/sysspawn.nim6
-rw-r--r--lib/system/sysstr.nim89
-rw-r--r--lib/system/threads.nim6
20 files changed, 503 insertions, 189 deletions
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 78db96e77..e274e8e0c 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -8,8 +8,6 @@
 #
 
 # Low level allocator for Nim. Has been designed to support the GC.
-# TODO:
-# - make searching for block O(1)
 {.push profiler:off.}
 
 include osalloc
@@ -19,14 +17,17 @@ template track(op, address, size) =
     memTrackerOp(op, address, size)
 
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
-# Each chunk starts at an address that is divisible by the page size. Chunks
-# that are bigger than ``ChunkOsReturn`` are returned back to the operating
-# system immediately.
+# Each chunk starts at an address that is divisible by the page size.
 
 const
-  ChunkOsReturn = 256 * PageSize # 1 MB
-  InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
+  InitialMemoryRequest = 128 * PageSize # 0.5 MB
   SmallChunkSize = PageSize
+  MaxFli = 30
+  MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
+                 # everywhere!
+  MaxSli = 1 shl MaxLog2Sli
+  FliOffset = 6
+  RealFli = MaxFli - FliOffset
 
 type
   PTrunk = ptr Trunk
@@ -99,10 +100,12 @@ type
   MemRegion = object
     minLargeObj, maxLargeObj: int
     freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
+    flBitmap: uint32
+    slBitmap: array[RealFli, uint32]
+    matrix: array[RealFli, array[MaxSli, PBigChunk]]
     llmem: PLLChunk
     currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
     lastSize: int # needed for the case that OS gives us pages linearly
-    freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
     chunkStarts: IntSet
     root, deleted, last, freeAvlNodes: PAvlNode
     locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
@@ -110,7 +113,109 @@ type
     bottomData: AvlNode
     heapLinks: HeapLinks
 
-{.deprecated: [TMemRegion: MemRegion].}
+const
+  fsLookupTable: array[byte, int8] = [
+    -1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+    5, 5, 5, 5, 5, 5, 5, 5,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7
+  ]
+
+proc msbit(x: uint32): int {.inline.} =
+  let a = if x <= 0xff_ff:
+            (if x <= 0xff: 0 else: 8)
+          else:
+            (if x <= 0xff_ff_ff: 16 else: 24)
+  result = int(fsLookupTable[byte(x shr a)]) + a
+
+proc lsbit(x: uint32): int {.inline.} =
+  msbit(x and ((not x) + 1))
+
+proc setBit(nr: int; dest: var uint32) {.inline.} =
+  dest = dest or (1u32 shl (nr and 0x1f))
+
+proc clearBit(nr: int; dest: var uint32) {.inline.} =
+  dest = dest and not (1u32 shl (nr and 0x1f))
+
+proc mappingSearch(r, fl, sl: var int) {.inline.} =
+  #let t = (1 shl (msbit(uint32 r) - MaxLog2Sli)) - 1
+  # This diverges from the standard TLSF algorithm because we need to ensure
+  # PageSize alignment:
+  let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
+  r = r + t
+  fl = msbit(uint32 r)
+  sl = (r shr (fl - MaxLog2Sli)) - MaxSli
+  dec fl, FliOffset
+  r = r and not t
+  sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned")
+
+# See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of
+# this algorithm.
+
+proc mappingInsert(r: int): tuple[fl, sl: int] {.inline.} =
+  sysAssert((r and PageMask) == 0, "mappingInsert: still not aligned")
+  result.fl = msbit(uint32 r)
+  result.sl = (r shr (result.fl - MaxLog2Sli)) - MaxSli
+  dec result.fl, FliOffset
+
+template mat(): untyped = a.matrix[fl][sl]
+
+proc findSuitableBlock(a: MemRegion; fl, sl: var int): PBigChunk {.inline.} =
+  let tmp = a.slBitmap[fl] and (not 0u32 shl sl)
+  result = nil
+  if tmp != 0:
+    sl = lsbit(tmp)
+    result = mat()
+  else:
+    fl = lsbit(a.flBitmap and (not 0u32 shl (fl + 1)))
+    if fl > 0:
+      sl = lsbit(a.slBitmap[fl])
+      result = mat()
+
+template clearBits(sl, fl) =
+  clearBit(sl, a.slBitmap[fl])
+  if a.slBitmap[fl] == 0u32:
+    # do not forget to cascade:
+    clearBit(fl, a.flBitmap)
+
+proc removeChunkFromMatrix(a: var MemRegion; b: PBigChunk) =
+  let (fl, sl) = mappingInsert(b.size)
+  if b.next != nil: b.next.prev = b.prev
+  if b.prev != nil: b.prev.next = b.next
+  if mat() == b:
+    mat() = b.next
+    if mat() == nil:
+      clearBits(sl, fl)
+  b.prev = nil
+  b.next = nil
+
+proc removeChunkFromMatrix2(a: var MemRegion; b: PBigChunk; fl, sl: int) =
+  mat() = b.next
+  if mat() != nil:
+    mat().prev = nil
+  else:
+    clearBits(sl, fl)
+  b.prev = nil
+  b.next = nil
+
+proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
+  let (fl, sl) = mappingInsert(b.size)
+  b.prev = nil
+  b.next = mat()
+  if mat() != nil:
+    mat().prev = b
+  mat() = b
+  setBit(sl, a.slBitmap[fl])
+  setBit(fl, a.flBitmap)
 
 {.push stack_trace: off.}
 proc initAllocator() = discard "nothing to do anymore"
@@ -203,6 +308,7 @@ proc llDeallocAll(a: var MemRegion) =
     var next = it.next
     osDeallocPages(it, PageSize)
     it = next
+  a.llmem = nil
 
 proc intSetGet(t: IntSet, key: int): PTrunk =
   var it = t.data[key and high(t.data)]
@@ -301,13 +407,14 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
   result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
   #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
 
-proc writeFreeList(a: MemRegion) =
-  var it = a.freeChunksList
-  c_fprintf(stdout, "freeChunksList: %p\n", it)
-  while it != nil:
-    c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
-              it, it.next, it.prev, it.size)
-    it = it.next
+when false:
+  proc writeFreeList(a: MemRegion) =
+    var it = a.freeChunksList
+    c_fprintf(stdout, "freeChunksList: %p\n", it)
+    while it != nil:
+      c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
+                it, it.next, it.prev, it.size)
+      it = it.next
 
 const nimMaxHeap {.intdefine.} = 0
 
@@ -368,6 +475,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
     result.prevSize = 0 or (result.prevSize and 1) # unknown
     # but do not overwrite 'used' field
   a.lastSize = size # for next request
+  sysAssert((cast[int](result) and PageMask) == 0, "requestOschunks: unaligned chunk")
 
 proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
   result = contains(a.chunkStarts, pageIndex(p))
@@ -418,7 +526,7 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
     if isAccessible(a, ri) and chunkUnused(ri):
       sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
       if not isSmallChunk(ri):
-        listRemove(a.freeChunksList, cast[PBigChunk](ri))
+        removeChunkFromMatrix(a, cast[PBigChunk](ri))
         inc(c.size, ri.size)
         excl(a.chunkStarts, pageIndex(ri))
   when coalescLeft:
@@ -429,49 +537,44 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
       if isAccessible(a, le) and chunkUnused(le):
         sysAssert(not isSmallChunk(le), "freeBigChunk 5")
         if not isSmallChunk(le):
-          listRemove(a.freeChunksList, cast[PBigChunk](le))
+          removeChunkFromMatrix(a, cast[PBigChunk](le))
           inc(le.size, c.size)
           excl(a.chunkStarts, pageIndex(c))
           c = cast[PBigChunk](le)
 
   incl(a, a.chunkStarts, pageIndex(c))
   updatePrevSize(a, c, c.size)
-  listAdd(a.freeChunksList, c)
+  addChunkToMatrix(a, c)
   # set 'used' to false:
   c.prevSize = c.prevSize and not 1
 
 proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
   var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
-  sysAssert(rest notin a.freeChunksList, "splitChunk")
   rest.size = c.size - size
   track("rest.origSize", addr rest.origSize, sizeof(int))
+  # XXX check if these two nil assignments are dead code given
+  # addChunkToMatrix's implementation:
   rest.next = nil
   rest.prev = nil
-  # size and not used
+  # size and not used:
   rest.prevSize = size
   sysAssert((size and 1) == 0, "splitChunk 2")
+  sysAssert((size and PageMask) == 0,
+      "splitChunk: size is not a multiple of the PageSize")
   updatePrevSize(a, c, rest.size)
   c.size = size
   incl(a, a.chunkStarts, pageIndex(rest))
-  listAdd(a.freeChunksList, rest)
+  addChunkToMatrix(a, rest)
 
 proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   # use first fit for now:
-  sysAssert((size and PageMask) == 0, "getBigChunk 1")
   sysAssert(size > 0, "getBigChunk 2")
-  result = a.freeChunksList
-  block search:
-    while result != nil:
-      sysAssert chunkUnused(result), "getBigChunk 3"
-      if result.size == size:
-        listRemove(a.freeChunksList, result)
-        break search
-      elif result.size > size:
-        listRemove(a.freeChunksList, result)
-        splitChunk(a, result, size)
-        break search
-      result = result.next
-      sysAssert result != a.freeChunksList, "getBigChunk 4"
+  var size = size # roundup(size, PageSize)
+  var fl, sl: int
+  mappingSearch(size, fl, sl)
+  sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
+  result = findSuitableBlock(a, fl, sl)
+  if result == nil:
     if size < InitialMemoryRequest:
       result = requestOsChunks(a, InitialMemoryRequest)
       splitChunk(a, result, size)
@@ -480,7 +583,10 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
       # if we over allocated split the chunk:
       if result.size > size:
         splitChunk(a, result, size)
-
+  else:
+    removeChunkFromMatrix2(a, result, fl, sl)
+    if result.size >= size + PageSize:
+      splitChunk(a, result, size)
   # set 'used' to to true:
   result.prevSize = 1
   track("setUsedToFalse", addr result.origSize, sizeof(int))
@@ -571,14 +677,14 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
                size == 0, "rawAlloc 21")
     sysAssert(allocInv(a), "rawAlloc: end small size")
   else:
-    size = roundup(requestedSize+bigChunkOverhead(), PageSize)
+    size = requestedSize + bigChunkOverhead() #  roundup(requestedSize+bigChunkOverhead(), PageSize)
     # allocate a large block
     var c = getBigChunk(a, size)
     sysAssert c.prev == nil, "rawAlloc 10"
     sysAssert c.next == nil, "rawAlloc 11"
-    sysAssert c.size == size, "rawAlloc 12"
     result = addr(c.data)
-    sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
     if a.root == nil: a.root = getBottom(a)
     add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
   sysAssert(isAccessible(a, result), "rawAlloc 14")
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index 8c3801687..afc435638 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
     result = memLoc
 
 when defined(vcc):
-  proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-    {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
-  proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-    {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
-  proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-    {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+  when defined(cpp):
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
+  else:
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
 
   proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
     when sizeof(T) == 8:
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 1520f231e..69b680dbd 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
   while x != subclass:
     if x == nil:
       sysFatal(ObjectConversionError, "invalid object conversion")
-      break
     x = x.base
 
 proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim
index cc6919d36..937c0d6f0 100644
--- a/lib/system/debugger.nim
+++ b/lib/system/debugger.nim
@@ -127,7 +127,7 @@ proc fileMatches(c, bp: cstring): bool =
 
 proc canonFilename*(filename: cstring): cstring =
   ## returns 'nil' if the filename cannot be found.
-  for i in 0 .. <dbgFilenameLen:
+  for i in 0 .. dbgFilenameLen-1:
     result = dbgFilenames[i]
     if fileMatches(result, filename): return result
   result = nil
@@ -261,7 +261,7 @@ proc genericHash(dest: pointer, mt: PNimType): int =
 proc dbgRegisterWatchpoint(address: pointer, name: cstring,
                            typ: PNimType) {.compilerproc.} =
   let L = watchPointsLen
-  for i in 0.. <L:
+  for i in 0 .. pred(L):
     if watchPoints[i].name == name:
       # address may have changed:
       watchPoints[i].address = address
@@ -288,7 +288,7 @@ var
 
 proc checkWatchpoints =
   let L = watchPointsLen
-  for i in 0.. <L:
+  for i in 0 .. pred(L):
     let newHash = genericHash(watchPoints[i].address, watchPoints[i].typ)
     if newHash != watchPoints[i].oldValue:
       dbgWatchpointHook(watchPoints[i].name)
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
index 35d8f25c4..d51ae29df 100644
--- a/lib/system/endb.nim
+++ b/lib/system/endb.nim
@@ -370,7 +370,7 @@ proc commandPrompt() =
     if dbgUser.len == 0: dbgUser.len = oldLen
     # now look what we have to do:
     var i = scanWord(addr dbgUser.data, dbgTemp, 0)
-    template `?`(x: expr): expr = dbgTemp == cstring(x)
+    template `?`(x: untyped): untyped = dbgTemp == cstring(x)
     if ?"s" or ?"step":
       dbgState = dbStepInto
       again = false
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 950981227..8e42ea468 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -38,20 +38,29 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
 proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
 proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
 
+type
+  GcFrame = ptr GcFrameHeader
+  GcFrameHeader {.compilerproc.} = object
+    len: int
+    prev: ptr GcFrameHeader
+
 var
   framePtr {.threadvar.}: PFrame
   excHandler {.threadvar.}: PSafePoint
     # list of exception handlers
     # a global variable for the root of all try blocks
   currException {.threadvar.}: ref Exception
+  gcFramePtr {.threadvar.}: GcFrame
 
 type
-  FrameState = tuple[framePtr: PFrame, excHandler: PSafePoint, currException: ref Exception]
+  FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
+                     excHandler: PSafePoint, currException: ref Exception]
 
 proc getFrameState*(): FrameState {.compilerRtl, inl.} =
-  return (framePtr, excHandler, currException)
+  return (gcFramePtr, framePtr, excHandler, currException)
 
 proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
+  gcFramePtr = state.gcFramePtr
   framePtr = state.framePtr
   excHandler = state.excHandler
   currException = state.currException
@@ -61,9 +70,29 @@ proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
 proc popFrame {.compilerRtl, inl.} =
   framePtr = framePtr.prev
 
+when false:
+  proc popFrameOfAddr(s: PFrame) {.compilerRtl.} =
+    var it = framePtr
+    if it == s:
+      framePtr = framePtr.prev
+    else:
+      while it != nil:
+        if it == s:
+          framePtr = it.prev
+          break
+        it = it.prev
+
 proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
   framePtr = s
 
+proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
+proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
+proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
+proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
+  s.prev = gcFramePtr
+  zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
+  gcFramePtr = s
+
 proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
   s.hasRaiseAction = false
   s.prev = excHandler
@@ -138,6 +167,52 @@ when not hasThreadSupport:
   var
     tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
 
+const
+  reraisedFromBegin = -10
+  reraisedFromEnd = -100
+
+template reraisedFrom(z): untyped =
+  StackTraceEntry(procname: nil, line: z, filename: nil)
+
+proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
+  var
+    it = f
+    i = 0
+  while it != nil:
+    inc(i)
+    it = it.prev
+  var last = i-1
+  if s.isNil:
+    s = newSeq[StackTraceEntry](i)
+  else:
+    last = s.len + i - 1
+    s.setLen(last+1)
+  it = f
+  while it != nil:
+    s[last] = StackTraceEntry(procname: it.procname,
+                              line: it.line,
+                              filename: it.filename)
+    it = it.prev
+    dec last
+
+template addFrameEntry(s, f: untyped) =
+  var oldLen = s.len
+  add(s, f.filename)
+  if f.line > 0:
+    add(s, '(')
+    add(s, $f.line)
+    add(s, ')')
+  for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
+  add(s, f.procname)
+  add(s, "\n")
+
+proc `$`(s: seq[StackTraceEntry]): string =
+  result = newStringOfCap(2000)
+  for i in 0 .. s.len-1:
+    if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
+    elif s[i].line == reraisedFromEnd: result.add "]]\n"
+    else: addFrameEntry(result, s[i])
+
 proc auxWriteStackTrace(f: PFrame, s: var string) =
   when hasThreadSupport:
     var
@@ -177,17 +252,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
     if tempFrames[j] == nil:
       add(s, "(")
       add(s, $skipped)
-      add(s, " calls omitted) ...")
+      add(s, " calls omitted) ...\n")
     else:
-      var oldLen = s.len
-      add(s, tempFrames[j].filename)
-      if tempFrames[j].line > 0:
-        add(s, '(')
-        add(s, $tempFrames[j].line)
-        add(s, ')')
-      for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
-      add(s, tempFrames[j].procname)
-    add(s, "\n")
+      addFrameEntry(s, tempFrames[j])
 
 proc stackTraceAvailable*(): bool
 
@@ -204,6 +271,13 @@ when hasSomeStackTrace:
       auxWriteStackTraceWithBacktrace(s)
     else:
       add(s, "No stack traceback available\n")
+
+  proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
+    when NimStackTrace:
+      auxWriteStackTrace(framePtr, s)
+    else:
+      s = nil
+
   proc stackTraceAvailable(): bool =
     when NimStackTrace:
       if framePtr == nil:
@@ -223,12 +297,6 @@ proc quitOrDebug() {.inline.} =
   else:
     endbStep() # call the debugger
 
-when false:
-  proc rawRaise*(e: ref Exception) =
-    ## undocumented. Do not use.
-    pushCurrentException(e)
-    c_longjmp(excHandler.context, 1)
-
 var onUnhandledException*: (proc (errorMsg: string) {.
   nimcall.}) ## set this error \
   ## handler to override the existing behaviour on an unhandled exception.
@@ -265,7 +333,7 @@ proc raiseExceptionAux(e: ref Exception) =
       when hasSomeStackTrace:
         var buf = newStringOfCap(2000)
         if isNil(e.trace): rawWriteStackTrace(buf)
-        else: add(buf, e.trace)
+        else: add(buf, $e.trace)
         add(buf, "Error: unhandled exception: ")
         if not isNil(e.msg): add(buf, e.msg)
         add(buf, " [")
@@ -301,12 +369,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
   if e.name.isNil: e.name = ename
   when hasSomeStackTrace:
     if e.trace.isNil:
-      e.trace = ""
       rawWriteStackTrace(e.trace)
     elif framePtr != nil:
-      e.trace.add "[[reraised from:\n"
+      e.trace.add reraisedFrom(reraisedFromBegin)
       auxWriteStackTrace(framePtr, e.trace)
-      e.trace.add "]]\n"
+      e.trace.add reraisedFrom(reraisedFromEnd)
   raiseExceptionAux(e)
 
 proc reraiseException() {.compilerRtl.} =
@@ -332,10 +399,15 @@ proc getStackTrace(): string =
 
 proc getStackTrace(e: ref Exception): string =
   if not isNil(e) and not isNil(e.trace):
-    result = e.trace
+    result = $e.trace
   else:
     result = ""
 
+proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
+  ## Returns the attached stack trace to the exception ``e`` as
+  ## a ``seq``. This is not yet available for the JS backend.
+  shallowCopy(result, e.trace)
+
 when defined(nimRequiresNimFrame):
   proc stackOverflow() {.noinline.} =
     writeStackTrace()
@@ -357,7 +429,7 @@ when defined(endb):
   var
     dbgAborting: bool # whether the debugger wants to abort
 
-when not defined(noSignalHandler):
+when not defined(noSignalHandler) and not defined(useNimRtl):
   proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} =
     template processSignal(s, action: untyped) {.dirty.} =
       if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index a2ff72a30..dac06119d 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -155,14 +155,15 @@ template setColor(c, col) =
   else:
     c.refcount = c.refcount and not colorMask or col
 
-proc writeCell(msg: cstring, c: PCell) =
-  var kind = -1
-  var typName: cstring = "nil"
-  if c.typ != nil:
-    kind = ord(c.typ.kind)
-    when defined(nimTypeNames):
-      if not c.typ.name.isNil:
-        typName = c.typ.name
+when defined(logGC):
+  proc writeCell(msg: cstring, c: PCell) =
+    var kind = -1
+    var typName: cstring = "nil"
+    if c.typ != nil:
+      kind = ord(c.typ.kind)
+      when defined(nimTypeNames):
+        if not c.typ.name.isNil:
+          typName = c.typ.name
 
   when leakDetector:
     c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
@@ -317,7 +318,7 @@ proc initGC() =
       init(gch.marked)
       init(gch.additionalRoots)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 when useMarkForDebug or useBackupGc:
   type
@@ -642,9 +643,9 @@ when useMarkForDebug or useBackupGc:
         forAllChildren(d, waMarkPrecise)
 
   proc markGlobals(gch: var GcHeap) =
-    for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
     let d = gch.additionalRoots.d
-    for i in 0 .. < gch.additionalRoots.len: markS(gch, d[i])
+    for i in 0 .. gch.additionalRoots.len-1: markS(gch, d[i])
 
 when logGC:
   var
@@ -652,7 +653,7 @@ when logGC:
     cycleCheckALen = 0
 
   proc alreadySeen(c: PCell): bool =
-    for i in 0 .. <cycleCheckALen:
+    for i in 0 .. cycleCheckALen-1:
       if cycleCheckA[i] == c: return true
     if cycleCheckALen == len(cycleCheckA):
       gcAssert(false, "cycle detection overflow")
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 6dffc323e..d57a01dc7 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -133,7 +133,7 @@ proc initGC() =
     init(gch.additionalRoots)
     init(gch.greyStack)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 # Which color to use for new objects is tricky: When we're marking,
 # they have to be *white* so that everything is marked that is only
@@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
 when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
-template color(c): expr = c.refCount and colorMask
+template color(c): untyped = c.refCount and colorMask
 template setColor(c, col) =
   c.refcount = c.refcount and not colorMask or col
 
@@ -487,12 +487,12 @@ proc GC_dumpHeap*(file: File) =
   var spaceIter: ObjectSpaceIter
   when false:
     var d = gch.decStack.d
-    for i in 0 .. < gch.decStack.len:
+    for i in 0 .. gch.decStack.len-1:
       if isAllocatedPtr(gch.region, d[i]):
         c_fprintf(file, "onstack %p\n", d[i])
       else:
         c_fprintf(file, "onstack_invalid %p\n", d[i])
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
   while true:
     let x = allObjectsAsProc(gch.region, addr spaceIter)
     if spaceIter.state < 0: break
@@ -579,7 +579,7 @@ proc markIncremental(gch: var GcHeap): bool =
   result = true
 
 proc markGlobals(gch: var GcHeap) =
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
 
 proc doOperation(p: pointer, op: WalkOp) =
   if p == nil: return
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index cfc0dfa8a..5fc48d848 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -233,7 +233,7 @@ proc initGC() =
       init(gch.allocated)
       init(gch.marked)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   var d = cast[ByteAddress](dest)
@@ -450,9 +450,9 @@ when false:
           quit 1
 
 proc markGlobals(gch: var GcHeap) =
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
   let d = gch.additionalRoots.d
-  for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i])
+  for i in 0 .. gch.additionalRoots.len-1: mark(gch, d[i])
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
diff --git a/lib/system/genodealloc.nim b/lib/system/genodealloc.nim
new file mode 100644
index 000000000..3646a842d
--- /dev/null
+++ b/lib/system/genodealloc.nim
@@ -0,0 +1,114 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Low level dataspace allocator for Genode.
+
+when not defined(genode):
+  {.error: "Genode only module".}
+
+type DataspaceCapability {.
+  importcpp: "Genode::Dataspace_capability", pure.} = object
+
+type
+  Map = object
+    attachment: pointer
+    size: int
+    ds: DataspaceCapability
+
+  SlabMeta = object
+    next: ptr MapSlab
+    ds: DataspaceCapability
+
+  MapSlab = object
+    meta: SlabMeta
+    maps: array[1,Map]
+
+const SlabBackendSize = 4096
+
+proc ramAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_ram().value".}
+  ## Return number of bytes available for allocation.
+
+proc capsAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_caps().value".}
+  ## Return the number of available capabilities.
+  ## Each dataspace allocation consumes a capability.
+
+proc allocDataspace(size: int): DataspaceCapability {.
+  importcpp: "genodeEnv->pd().alloc(@)".}
+  ## Allocate a dataspace and its capability.
+
+proc attachDataspace(ds: DataspaceCapability): pointer {.
+  importcpp: "genodeEnv->rm().attach(@)".}
+  ## Attach a dataspace into the component address-space.
+
+proc detachAddress(p: pointer) {.
+  importcpp: "genodeEnv->rm().detach(@)".}
+  ## Detach a dataspace from the component address-space.
+
+proc freeDataspace(ds: DataspaceCapability) {.
+  importcpp: "genodeEnv->pd().free(@)".}
+  ## Free a dataspace.
+
+proc newMapSlab(): ptr MapSlab =
+  let
+    ds = allocDataspace SlabBackendSize
+    p = attachDataspace ds
+  result = cast[ptr MapSlab](p)
+  result.meta.ds = ds
+
+iterator items(s: ptr MapSlab): ptr Map =
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  for i in 0 .. <mapCount:
+    yield s.maps[i].addr
+
+var slabs: ptr MapSlab
+
+proc osAllocPages(size: int): pointer =
+  if slabs.isNil:
+    slabs = newMapSlab()
+  var
+    slab = slabs
+    map: ptr Map
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  block findFreeMap:
+    while true:
+      # lookup first free spot in slabs
+      for m in slab.items:
+        if m.attachment.isNil:
+          map = m
+          break findFreeMap
+      if slab.meta.next.isNil:
+        slab.meta.next = newMapSlab()
+          # tack a new slab on the tail
+      slab = slab.meta.next
+        # move to next slab in linked list
+  map.ds = allocDataspace size
+  map.size = size
+  map.attachment = attachDataspace map.ds
+  result = map.attachment
+
+proc osTryAllocPages(size: int): pointer =
+  if ramAvail() >= size and capsAvail() > 1:
+    result = osAllocPages size
+
+proc osDeallocPages(p: pointer; size: int) =
+  var slab = slabs
+  while not slab.isNil:
+    # lookup first free spot in slabs
+    for m in slab.items:
+      if m.attachment == p:
+        if m.size != size:
+          echo "cannot partially detach dataspace"
+          quit -1
+        detachAddress m.attachment
+        freeDataspace m.ds
+        m[] = Map()
+        return
+    slab = slab.meta.next
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 24093a310..8065f2255 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -531,13 +531,13 @@ proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
 proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   when defined(nimphp):
     asm """
-      return floor(`a` / `b`);
+      return trunc(`a` / `b`);
     """
   else:
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.floor(`a` / `b`);
+      return Math.trunc(`a` / `b`);
     """
 
 proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -549,7 +549,7 @@ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.floor(`a` % `b`);
+      return Math.trunc(`a` % `b`);
     """
 
 proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -594,13 +594,13 @@ proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
 proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   when defined(nimphp):
     asm """
-      return floor(`a` / `b`);
+      return trunc(`a` / `b`);
     """
   else:
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.floor(`a` / `b`);
+      return Math.trunc(`a` / `b`);
     """
 
 proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -612,7 +612,7 @@ proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.floor(`a` % `b`);
+      return Math.trunc(`a` % `b`);
     """
 
 proc negInt(a: int): int {.compilerproc.} =
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 824934966..45e0c74c0 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -42,7 +42,8 @@ type
 # Page size of the system; in most cases 4096 bytes. For exotic OS or
 # CPU this needs to be changed:
 const
-  PageShift = when defined(cpu16): 8 else: 12
+  PageShift = when defined(cpu16): 8 else: 12 # \
+    # my tests showed no improvments for using larger page sizes.
   PageSize = 1 shl PageShift
   PageMask = PageSize-1
 
@@ -108,7 +109,6 @@ when defined(boehmgc):
       if result == nil: raiseOutOfMem()
     proc alloc0(size: Natural): pointer =
       result = alloc(size)
-      zeroMem(result, size)
     proc realloc(p: pointer, newsize: Natural): pointer =
       result = boehmRealloc(p, newsize)
       if result == nil: raiseOutOfMem()
@@ -118,8 +118,7 @@ when defined(boehmgc):
       result = boehmAlloc(size)
       if result == nil: raiseOutOfMem()
     proc allocShared0(size: Natural): pointer =
-      result = alloc(size)
-      zeroMem(result, size)
+      result = allocShared(size)
     proc reallocShared(p: pointer, newsize: Natural): pointer =
       result = boehmRealloc(p, newsize)
       if result == nil: raiseOutOfMem()
@@ -343,7 +342,6 @@ elif defined(gogc):
 
   const goFlagNoZero: uint32 = 1 shl 3
   proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
-  proc goFree(v: pointer) {.importc: "__go_free", dynlib: goLib.}
 
   proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
 
@@ -376,7 +374,6 @@ elif defined(gogc):
     result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
     copyMem(result, old, oldsize)
     zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
-    goFree(old)
 
   proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
   proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
@@ -573,3 +570,11 @@ when not declared(nimNewSeqOfCap):
     cast[PGenericSeq](result).reserved = cap
 
 {.pop.}
+
+when not declared(ForeignCell):
+  type ForeignCell* = object
+    data*: pointer
+
+  proc protect*(x: pointer): ForeignCell = ForeignCell(data: x)
+  proc dispose*(x: ForeignCell) = discard
+  proc isNotForeign*(x: ForeignCell): bool = false
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index f5b9cf3ed..f91dae41e 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -106,7 +106,7 @@ proc cmpic*(a, b: string): int =
   ## Compares `a` and `b` ignoring case.
   cmpIgnoreCase(a, b)
 
-proc getEnv*(key: string): string {.tags: [ReadIOEffect].} =
+proc getEnv*(key: string; default = ""): string {.tags: [ReadIOEffect].} =
   ## Retrieves the environment variable of name `key`.
   builtin
 
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 65a057772..1ad4cf695 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -78,17 +78,7 @@ when defined(emscripten):
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
 
 elif defined(genode):
-
-  proc osAllocPages(size: int): pointer {.
-   importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".}
-
-  proc osTryAllocPages(size: int): pointer =
-    {.emit: """try {""".}
-    result = osAllocPages size
-    {.emit: """} catch (...) { }""".}
-
-  proc osDeallocPages(p: pointer, size: int) {.
-    importcpp: "genodeEnv->rm().detach(#)".}
+  include genodealloc # osAllocPages, osTryAllocPages, osDeallocPages
 
 elif defined(posix):
   const
@@ -166,7 +156,7 @@ elif defined(windows):
     # space heavily, so we now treat Windows as a strange unmap target.
     when reallyOsDealloc:
       if virtualFree(p, 0, MEM_RELEASE) == 0:
-        cprintf "yes, failing!"
+        cprintf "virtualFree failing!"
         quit 1
     #VirtualFree(p, size, MEM_DECOMMIT)
 
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 19fa564fb..982b07467 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -41,14 +41,14 @@ proc `$`(x: uint64): string =
 
     let half = i div 2
     # Reverse
-    for t in 0 .. < half: swap(result[t], result[i-t-1])
+    for t in 0 .. half-1: swap(result[t], result[i-t-1])
 
 proc reprStrAux(result: var string, s: cstring; len: int) =
   if cast[pointer](s) == nil:
     add result, "nil"
     return
   add result, reprPointer(cast[pointer](s)) & "\""
-  for i in 0.. <len:
+  for i in 0 .. pred(len):
     let c = s[i]
     case c
     of '"': add result, "\\\""
diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim
index 5c265a891..658220c11 100644
--- a/lib/system/reprjs.nim
+++ b/lib/system/reprjs.nim
@@ -9,13 +9,13 @@
 # The generic ``repr`` procedure for the javascript backend.
 
 proc reprInt(x: int64): string {.compilerproc.} = return $x
-proc reprFloat(x: float): string {.compilerproc.} = 
+proc reprFloat(x: float): string {.compilerproc.} =
   # Js toString doesn't differentiate between 1.0 and 1,
   # but we do.
   if $x == $(x.int): $x & ".0"
   else: $x
 
-proc reprPointer(p: pointer): string {.compilerproc.} = 
+proc reprPointer(p: pointer): string {.compilerproc.} =
   # Do we need to generate the full 8bytes ? In js a pointer is an int anyway
   var tmp: int
   {. emit: """
@@ -38,7 +38,7 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
     result = $typ.node.sons[e].name
   else:
     result = $e & " (invalid data!)"
-  
+
 proc reprChar(x: char): string {.compilerRtl.} =
   result = "\'"
   case x
@@ -50,7 +50,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
 
 proc reprStrAux(result: var string, s: cstring, len: int) =
   add(result, "\"")
-  for i in 0 .. <len:
+  for i in 0 .. len-1:
     let c = s[i]
     case c
     of '"': add(result, "\\\"")
@@ -67,7 +67,7 @@ proc reprStr(s: string): string {.compilerRtl.} =
   if cast[pointer](s).isNil:
     # Handle nil strings here because they don't have a length field in js
     # TODO: check for null/undefined before generating call to length in js?
-    # Also: c backend repr of a nil string is <pointer>"", but repr of an 
+    # Also: c backend repr of a nil string is <pointer>"", but repr of an
     # array of string that is not initialized is [nil, nil, ...] ??
     add(result, "nil")
   else:
@@ -86,7 +86,7 @@ proc addSetElem(result: var string, elem: int, typ: PNimType) =
 
 iterator setKeys(s: int): int {.inline.} =
   # The type of s is a lie, but it's expected to be a set.
-  # Iterate over the JS object representing a set 
+  # Iterate over the JS object representing a set
   # and returns the keys as int.
   var len: int
   var yieldRes: int
@@ -124,16 +124,16 @@ proc initReprClosure(cl: var ReprClosure) =
   cl.recDepth = -1 # default is to display everything!
   cl.indent = 0
 
-proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure) 
+proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
 
-proc reprArray(a: pointer, typ: PNimType, 
+proc reprArray(a: pointer, typ: PNimType,
               cl: var ReprClosure): string {.compilerRtl.} =
   var isNilArrayOrSeq: bool
   # isnil is not enough here as it would try to deref `a` without knowing what's inside
   {. emit: """
-    if (`a` == null) { 
+    if (`a` == null) {
       `isNilArrayOrSeq` = true;
-    } else if (`a`[0] == null) { 
+    } else if (`a`[0] == null) {
       `isNilArrayOrSeq` = true;
     } else {
       `isNilArrayOrSeq` = false;
@@ -146,19 +146,19 @@ proc reprArray(a: pointer, typ: PNimType,
   result = if typ.kind == tySequence: "@[" else: "["
   var len: int = 0
   var i: int = 0
-    
+
   {. emit: "`len` = `a`.length;\n" .}
   var dereffed: pointer = a
-  for i in 0 .. < len:
+  for i in 0 .. len-1:
     if i > 0 :
       add(result, ", ")
     # advance pointer and point to element at index
     {. emit: """
-    `dereffed`_Idx = `i`; 
+    `dereffed`_Idx = `i`;
     `dereffed` = `a`[`dereffed`_Idx];
     """ .}
     reprAux(result, dereffed, typ.base, cl)
-  
+
   add(result, "]")
 
 proc isPointedToNil(p: pointer): bool {.inline.}=
@@ -181,7 +181,7 @@ proc reprRef(result: var string, p: pointer, typ: PNimType,
 
 proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
   add(result, "[")
-  
+
   var first: bool = true
   var val: pointer = o
   if typ.node.len == 0:
@@ -192,7 +192,7 @@ proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprCl
     reprAux(result, val, typ.node.typ, cl)
   else:
     # if the object has more than one field, sons is not nil and contains the fields.
-    for i in 0 .. <typ.node.len:
+    for i in 0 .. typ.node.len-1:
       if first: first = false
       else: add(result, ",\n")
 
@@ -214,11 +214,11 @@ proc reprJSONStringify(p: int): string {.compilerRtl.} =
   {. emit: "`tmp` = JSON.stringify(`p`);\n" .}
   result = $tmp
 
-proc reprAux(result: var string, p: pointer, typ: PNimType, 
+proc reprAux(result: var string, p: pointer, typ: PNimType,
             cl: var ReprClosure) =
   if cl.recDepth == 0:
     add(result, "...")
-    return 
+    return
   dec(cl.recDepth)
   case typ.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 4f266e0ae..4348ffbb5 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -192,7 +192,7 @@ proc write(f: File, r: float32) =
 proc write(f: File, r: BiggestFloat) =
   if c_fprintf(f, "%g", r) < 0: checkErr(f)
 
-proc write(f: File, c: char) = discard c_putc(ord(c), f)
+proc write(f: File, c: char) = discard c_putc(cint(c), f)
 proc write(f: File, a: varargs[string, `$`]) =
   for x in items(a): write(f, x)
 
@@ -404,4 +404,18 @@ proc setStdIoUnbuffered() =
   when declared(stdin):
     discard c_setvbuf(stdin, nil, IONBF, 0)
 
+when declared(stdout):
+  proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
+    when not defined(windows):
+      proc flockfile(f: File) {.importc, noDecl.}
+      proc funlockfile(f: File) {.importc, noDecl.}
+      flockfile(stdout)
+    for s in args:
+      discard c_fwrite(s.cstring, s.len, 1, stdout)
+    const linefeed = "\n" # can be 1 or more chars
+    discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
+    discard c_fflush(stdout)
+    when not defined(windows):
+      funlockfile(stdout)
+
 {.pop.}
diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim
index 7da45b4dd..dc2d13578 100644
--- a/lib/system/sysspawn.nim
+++ b/lib/system/sysspawn.nim
@@ -142,7 +142,7 @@ var
   workersData: array[NumThreads, Worker]
 
 proc setup() =
-  for i in 0.. <NumThreads:
+  for i in 0 ..< NumThreads:
     workersData[i].taskArrived = createCondVar()
     workersData[i].taskStarted = createFastCondVar()
     createThread(workers[i], slave, addr(workersData[i]))
@@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
   ## it is not necessary to call this directly; use 'spawnX' instead.
   result = gSomeReady.event
 
-proc spawn*(call: stmt) {.magic: "Spawn".}
+proc spawn*(call: typed) {.magic: "Spawn".}
   ## always spawns a new task, so that the 'call' is never executed on
   ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has 'void' as the return type.
 
-template spawnX*(call: stmt) =
+template spawnX*(call: typed) =
   ## spawns a new task if a CPU core is ready, otherwise executes the
   ## call in the calling thread. Usually it is advised to
   ## use 'spawn' in order to not block the producer for an unknown
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 43b5a0292..4c5f3d9a1 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
   if a == nil: return -1
   if b == nil: return 1
-  when defined(nimNoArrayToCstringConversion):
-    return c_strcmp(addr a.data, addr b.data)
-  else:
-    return c_strcmp(a.data, b.data)
+  let minlen = min(a.len, b.len)
+  result = c_memcmp(addr a.data, addr b.data, minlen.csize)
+  if result == 0:
+    result = a.len - b.len
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true
@@ -259,7 +259,7 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
     result.reserved = r
 
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
-    compilerRtl.} =
+    compilerRtl, inl.} =
   result = seq
   if result.space < newLen:
     let r = max(resize(result.space), newLen)
@@ -278,14 +278,15 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
                             GenericSeqSize +% (i*%elemSize)),
                             extGetCellType(result).base, waPush)
           let len1 = gch.tempStack.len
-          for i in len0 .. <len1:
+          for i in len0 ..< len1:
             doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
           gch.tempStack.len = len0
       else:
-        for i in newLen..result.len-1:
-          forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
-                            GenericSeqSize +% (i*%elemSize)),
-                            extGetCellType(result).base, waZctDecRef)
+        if ntfNoRefs notin extGetCellType(result).base.flags:
+          for i in newLen..result.len-1:
+            forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
+                              GenericSeqSize +% (i*%elemSize)),
+                              extGetCellType(result).base, waZctDecRef)
 
     # XXX: zeroing out the memory can still result in crashes if a wiped-out
     # cell is aliased by another pointer (ie proc parameter or a let variable).
@@ -322,36 +323,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
   result.add x
 
 proc add*(result: var string; x: float) =
-  var buf: array[0..64, char]
-  when defined(nimNoArrayToCstringConversion):
-    var n: int = c_sprintf(addr buf, "%.16g", x)
+  when nimvm:
+    result.add $x
   else:
-    var n: int = c_sprintf(buf, "%.16g", x)
-  var hasDot = false
-  for i in 0..n-1:
-    if buf[i] == ',':
-      buf[i] = '.'
-      hasDot = true
-    elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
-      hasDot = true
-  if not hasDot:
-    buf[n] = '.'
-    buf[n+1] = '0'
-    buf[n+2] = '\0'
-  # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
-  # We want to get rid of these here:
-  if buf[n-1] in {'n', 'N'}:
-    result.add "nan"
-  elif buf[n-1] == 'F':
-    if buf[0] == '-':
-      result.add "-inf"
+    var buf: array[0..64, char]
+    when defined(nimNoArrayToCstringConversion):
+      var n: int = c_sprintf(addr buf, "%.16g", x)
     else:
-      result.add "inf"
-  else:
-    var i = 0
-    while buf[i] != '\0':
-      result.add buf[i]
-      inc i
+      var n: int = c_sprintf(buf, "%.16g", x)
+    var hasDot = false
+    for i in 0..n-1:
+      if buf[i] == ',':
+        buf[i] = '.'
+        hasDot = true
+      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+        hasDot = true
+    if not hasDot:
+      buf[n] = '.'
+      buf[n+1] = '0'
+      buf[n+2] = '\0'
+    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
+    # of '-1.#IND' are produced.
+    # We want to get rid of these here:
+    if buf[n-1] in {'n', 'N', 'D', 'd'}:
+      result.add "nan"
+    elif buf[n-1] == 'F':
+      if buf[0] == '-':
+        result.add "-inf"
+      else:
+        result.add "inf"
+    else:
+      var i = 0
+      while buf[i] != '\0':
+        result.add buf[i]
+        inc i
 
 proc nimFloatToStr(f: float): string {.compilerproc.} =
   result = newStringOfCap(8)
@@ -362,9 +367,9 @@ proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
 
 const
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
-  powtens =   [ 1e0,   1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,
-                1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-                1e20, 1e21, 1e22]
+  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+              1e20, 1e21, 1e22]
 
 proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
                           start = 0): int {.compilerProc.} =
@@ -508,7 +513,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # insert exponent
   t[ti] = 'E'; inc(ti)
-  t[ti] = if exp_negative: '-' else: '+'; inc(ti)
+  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
   inc(ti, 3)
 
   # insert adjusted exponent
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 96c045e6b..f61cc4280 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -255,9 +255,9 @@ when emulatedThreadVars:
   proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
 
 # we preallocate a fixed size for thread local storage, so that no heap
-# allocations are needed. Currently less than 7K are used on a 64bit machine.
+# allocations are needed. Currently less than 16K are used on a 64bit machine.
 # We use ``float`` for proper alignment:
-const nimTlsSize {.intdefine.} = 8000
+const nimTlsSize {.intdefine.} = 16000
 type
   ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
 
@@ -398,7 +398,7 @@ template afterThreadRuns() =
     threadDestructionHandlers[i]()
 
 when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
-  proc deallocOsPages()
+  proc deallocOsPages() {.rtl.}
 
 when defined(boehmgc):
   type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}