summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2016-03-12 13:29:27 +0100
committerAndreas Rumpf <rumpf_a@web.de>2016-03-12 13:29:27 +0100
commit75b03188d09ff865ceb9c43892eceb22b2fd6d8f (patch)
tree8fea4b5a9c709e2f71fb1ca10bd381e08cc53e0f
parentad7e2191174d551e5787667bedf8aae72bb4e882 (diff)
downloadNim-75b03188d09ff865ceb9c43892eceb22b2fd6d8f.tar.gz
beginnings of --gc:stack
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/options.nim3
-rw-r--r--lib/system/alloc.nim148
-rw-r--r--lib/system/mmdisp.nim3
-rw-r--r--lib/system/osalloc.nim171
5 files changed, 181 insertions, 148 deletions
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 2622d64f4..86bc1c205 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -205,6 +205,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
     of "generational": result = gSelectedGC == gcGenerational
     of "go":           result = gSelectedGC == gcGo
     of "none":         result = gSelectedGC == gcNone
+    of "stack":        result = gSelectedGC == gcStack
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
   of "opt":
     case arg.normalize
@@ -394,6 +395,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     of "none":
       gSelectedGC = gcNone
       defineSymbol("nogc")
+    of "stack":
+      gSelectedGC= gcStack
+      defineSymbol("gcstack")
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
   of "warnings", "w":
     if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
diff --git a/compiler/options.nim b/compiler/options.nim
index 29cdd96fb..2716a98d3 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -86,7 +86,8 @@ type                          # please make sure we have under 32 options
     cmdRun                    # run the project via TCC backend
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
-    gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
+    gcNone, gcBoehm, gcGo, gcStack, gcMarkAndSweep, gcRefc,
+    gcV2, gcGenerational
 
   IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 67d380391..e0fd53b7b 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -13,153 +13,7 @@
 # - make searching for block O(1)
 {.push profiler:off.}
 
-proc roundup(x, v: int): int {.inline.} =
-  result = (x + (v-1)) and not (v-1)
-  sysAssert(result >= x, "roundup: result < x")
-  #return ((-x) and (v-1)) +% x
-
-sysAssert(roundup(14, PageSize) == PageSize, "invalid PageSize")
-sysAssert(roundup(15, 8) == 16, "roundup broken")
-sysAssert(roundup(65, 8) == 72, "roundup broken 2")
-
-# ------------ platform specific chunk allocation code -----------------------
-
-# some platforms have really weird unmap behaviour: unmap(blockStart, PageSize)
-# really frees the whole block. Happens for Linux/PowerPC for example. Amd64
-# and x86 are safe though; Windows is special because MEM_RELEASE can only be
-# used with a size of 0. We also allow unmapping to be turned off with
-# -d:nimAllocNoUnmap:
-const doNotUnmap = not (defined(amd64) or defined(i386)) or
-                   defined(windows) or defined(nimAllocNoUnmap)
-
-
-when defined(emscripten):
-  const
-    PROT_READ  = 1             # page can be read
-    PROT_WRITE = 2             # page can be written
-    MAP_PRIVATE = 2'i32        # Changes are private
-
-  var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
-  type
-    PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
-    EmscriptenMMapBlock {.pure, inheritable.} = object
-      realSize: int        # size of previous chunk; for coalescing
-      realPointer: pointer     # if < PageSize it is a small chunk
-
-  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
-            off: int): pointer {.header: "<sys/mman.h>".}
-
-  proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
-
-  proc osAllocPages(block_size: int): pointer {.inline.} =
-    let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1
-    result = mmap(nil, realSize, PROT_READ or PROT_WRITE,
-                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
-    if result == nil or result == cast[pointer](-1):
-      raiseOutOfMem()
-
-    let realPointer = result
-    let pos = cast[int](result)
-
-    # Convert pointer to PageSize correct one.
-    var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
-    if (new_pos-pos)< sizeof(EmscriptenMMapBlock):
-      new_pos = new_pos +% PageSize
-    result = cast[pointer](new_pos)
-
-    var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock)
-
-    var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
-    mmapDescr.realSize = realSize
-    mmapDescr.realPointer = realPointer
-
-    c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer))
-
-  proc osDeallocPages(p: pointer, size: int) {.inline} =
-    var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
-    var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
-    munmap(mmapDescr.realPointer, mmapDescr.realSize)
-
-elif defined(posix):
-  const
-    PROT_READ  = 1             # page can be read
-    PROT_WRITE = 2             # page can be written
-    MAP_PRIVATE = 2'i32        # Changes are private
-
-  when defined(macosx) or defined(bsd):
-    const MAP_ANONYMOUS = 0x1000
-  elif defined(solaris):
-    const MAP_ANONYMOUS = 0x100
-  else:
-    var
-      MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
-
-  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
-            off: int): pointer {.header: "<sys/mman.h>".}
-
-  proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
-
-  proc osAllocPages(size: int): pointer {.inline.} =
-    result = mmap(nil, size, PROT_READ or PROT_WRITE,
-                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
-    if result == nil or result == cast[pointer](-1):
-      raiseOutOfMem()
-
-  proc osDeallocPages(p: pointer, size: int) {.inline} =
-    when reallyOsDealloc: munmap(p, size)
-
-elif defined(windows):
-  const
-    MEM_RESERVE = 0x2000
-    MEM_COMMIT = 0x1000
-    MEM_TOP_DOWN = 0x100000
-    PAGE_READWRITE = 0x04
-
-    MEM_DECOMMIT = 0x4000
-    MEM_RELEASE = 0x8000
-
-  proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
-                    flProtect: int32): pointer {.
-                    header: "<windows.h>", stdcall, importc: "VirtualAlloc".}
-
-  proc virtualFree(lpAddress: pointer, dwSize: int,
-                   dwFreeType: int32) {.header: "<windows.h>", stdcall,
-                   importc: "VirtualFree".}
-
-  proc osAllocPages(size: int): pointer {.inline.} =
-    result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
-                          PAGE_READWRITE)
-    if result == nil: raiseOutOfMem()
-
-  proc osDeallocPages(p: pointer, size: int) {.inline.} =
-    # according to Microsoft, 0 is the only correct value for MEM_RELEASE:
-    # This means that the OS has some different view over how big the block is
-    # that we want to free! So, we cannot reliably release the memory back to
-    # Windows :-(. We have to live with MEM_DECOMMIT instead.
-    # Well that used to be the case but MEM_DECOMMIT fragments the address
-    # space heavily, so we now treat Windows as a strange unmap target.
-    when reallyOsDealloc: virtualFree(p, 0, MEM_RELEASE)
-    #VirtualFree(p, size, MEM_DECOMMIT)
-
-elif hostOS == "standalone":
-  var
-    theHeap: array[1024*PageSize, float64] # 'float64' for alignment
-    bumpPointer = cast[int](addr theHeap)
-
-  proc osAllocPages(size: int): pointer {.inline.} =
-    if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap):
-      result = cast[pointer](bumpPointer)
-      inc bumpPointer, size
-    else:
-      raiseOutOfMem()
-
-  proc osDeallocPages(p: pointer, size: int) {.inline.} =
-    if bumpPointer-size == cast[int](p):
-      dec bumpPointer, size
-else:
-  {.error: "Port memory manager to your platform".}
-
-# --------------------- end of non-portable code -----------------------------
+include osalloc
 
 # 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
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 1e85853d1..e0d14737e 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -518,6 +518,9 @@ else:
     sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
   when compileOption("gc", "v2"):
     include "system/gc2"
+  elif defined(gcStack):
+    # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
+    include "system/gc_stack"
   elif defined(gcMarkAndSweep):
     # XXX use 'compileOption' here
     include "system/gc_ms"
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
new file mode 100644
index 000000000..cc1a28213
--- /dev/null
+++ b/lib/system/osalloc.nim
@@ -0,0 +1,171 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+proc roundup(x, v: int): int {.inline.} =
+  result = (x + (v-1)) and not (v-1)
+  sysAssert(result >= x, "roundup: result < x")
+  #return ((-x) and (v-1)) +% x
+
+sysAssert(roundup(14, PageSize) == PageSize, "invalid PageSize")
+sysAssert(roundup(15, 8) == 16, "roundup broken")
+sysAssert(roundup(65, 8) == 72, "roundup broken 2")
+
+# ------------ platform specific chunk allocation code -----------
+
+# some platforms have really weird unmap behaviour:
+# unmap(blockStart, PageSize)
+# really frees the whole block. Happens for Linux/PowerPC for example. Amd64
+# and x86 are safe though; Windows is special because MEM_RELEASE can only be
+# used with a size of 0. We also allow unmapping to be turned off with
+# -d:nimAllocNoUnmap:
+const doNotUnmap = not (defined(amd64) or defined(i386)) or
+                   defined(windows) or defined(nimAllocNoUnmap)
+
+
+when defined(emscripten):
+  const
+    PROT_READ  = 1             # page can be read
+    PROT_WRITE = 2             # page can be written
+    MAP_PRIVATE = 2'i32        # Changes are private
+
+  var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+  type
+    PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
+    EmscriptenMMapBlock {.pure, inheritable.} = object
+      realSize: int        # size of previous chunk; for coalescing
+      realPointer: pointer     # if < PageSize it is a small chunk
+
+  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
+            off: int): pointer {.header: "<sys/mman.h>".}
+
+  proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
+
+  proc osAllocPages(block_size: int): pointer {.inline.} =
+    let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1
+    result = mmap(nil, realSize, PROT_READ or PROT_WRITE,
+                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
+    if result == nil or result == cast[pointer](-1):
+      raiseOutOfMem()
+
+    let realPointer = result
+    let pos = cast[int](result)
+
+    # Convert pointer to PageSize correct one.
+    var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
+    if (new_pos-pos)< sizeof(EmscriptenMMapBlock):
+      new_pos = new_pos +% PageSize
+    result = cast[pointer](new_pos)
+
+    var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock)
+
+    var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
+    mmapDescr.realSize = realSize
+    mmapDescr.realPointer = realPointer
+
+    c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer))
+
+  proc osTryAllocPages(size: int): pointer = osAllocPages(size)
+
+  proc osDeallocPages(p: pointer, size: int) {.inline} =
+    var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
+    var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
+    munmap(mmapDescr.realPointer, mmapDescr.realSize)
+
+elif defined(posix):
+  const
+    PROT_READ  = 1             # page can be read
+    PROT_WRITE = 2             # page can be written
+    MAP_PRIVATE = 2'i32        # Changes are private
+
+  when defined(macosx) or defined(bsd):
+    const MAP_ANONYMOUS = 0x1000
+  elif defined(solaris):
+    const MAP_ANONYMOUS = 0x100
+  else:
+    var
+      MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+
+  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
+            off: int): pointer {.header: "<sys/mman.h>".}
+
+  proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
+
+  proc osAllocPages(size: int): pointer {.inline.} =
+    result = mmap(nil, size, PROT_READ or PROT_WRITE,
+                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
+    if result == nil or result == cast[pointer](-1):
+      raiseOutOfMem()
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    result = mmap(nil, size, PROT_READ or PROT_WRITE,
+                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
+    if result == cast[pointer](-1): result = nil
+
+  proc osDeallocPages(p: pointer, size: int) {.inline} =
+    when reallyOsDealloc: munmap(p, size)
+
+elif defined(windows):
+  const
+    MEM_RESERVE = 0x2000
+    MEM_COMMIT = 0x1000
+    MEM_TOP_DOWN = 0x100000
+    PAGE_READWRITE = 0x04
+
+    MEM_DECOMMIT = 0x4000
+    MEM_RELEASE = 0x8000
+
+  proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
+                    flProtect: int32): pointer {.
+                    header: "<windows.h>", stdcall, importc: "VirtualAlloc".}
+
+  proc virtualFree(lpAddress: pointer, dwSize: int,
+                   dwFreeType: int32) {.header: "<windows.h>", stdcall,
+                   importc: "VirtualFree".}
+
+  proc osAllocPages(size: int): pointer {.inline.} =
+    result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
+                          PAGE_READWRITE)
+    if result == nil: raiseOutOfMem()
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
+                          PAGE_READWRITE)
+
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
+    # according to Microsoft, 0 is the only correct value for MEM_RELEASE:
+    # This means that the OS has some different view over how big the block is
+    # that we want to free! So, we cannot reliably release the memory back to
+    # Windows :-(. We have to live with MEM_DECOMMIT instead.
+    # Well that used to be the case but MEM_DECOMMIT fragments the address
+    # space heavily, so we now treat Windows as a strange unmap target.
+    when reallyOsDealloc: virtualFree(p, 0, MEM_RELEASE)
+    #VirtualFree(p, size, MEM_DECOMMIT)
+
+elif hostOS == "standalone":
+  var
+    theHeap: array[1024*PageSize, float64] # 'float64' for alignment
+    bumpPointer = cast[int](addr theHeap)
+
+  proc osAllocPages(size: int): pointer {.inline.} =
+    if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap):
+      result = cast[pointer](bumpPointer)
+      inc bumpPointer, size
+    else:
+      raiseOutOfMem()
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    if size+bumpPointer < cast[int](addr theHeap) + sizeof(theHeap):
+      result = cast[pointer](bumpPointer)
+      inc bumpPointer, size
+
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
+    if bumpPointer-size == cast[int](p):
+      dec bumpPointer, size
+else:
+  {.error: "Port memory manager to your platform".}