beginnings of --gc:stack

This commit is contained in:
Andreas Rumpf
2016-03-12 13:29:27 +01:00
parent c11487b339
commit e2c8d9ade0
5 changed files with 181 additions and 150 deletions

View File

@@ -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()

View File

@@ -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,

View File

@@ -13,155 +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): cint {.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)
discard 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
elif defined(linux):
const MAP_ANONYMOUS = 0x20'i32
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): cint {.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: discard 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

View File

@@ -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"

171
lib/system/osalloc.nim Normal file
View File

@@ -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".}