beginnings of --gc:stack
This commit is contained in:
parent
ad7e219117
commit
75b03188d0
5 changed files with 181 additions and 148 deletions
|
|
@ -205,6 +205,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
|
||||||
of "generational": result = gSelectedGC == gcGenerational
|
of "generational": result = gSelectedGC == gcGenerational
|
||||||
of "go": result = gSelectedGC == gcGo
|
of "go": result = gSelectedGC == gcGo
|
||||||
of "none": result = gSelectedGC == gcNone
|
of "none": result = gSelectedGC == gcNone
|
||||||
|
of "stack": result = gSelectedGC == gcStack
|
||||||
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
|
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
|
||||||
of "opt":
|
of "opt":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
|
|
@ -394,6 +395,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
of "none":
|
of "none":
|
||||||
gSelectedGC = gcNone
|
gSelectedGC = gcNone
|
||||||
defineSymbol("nogc")
|
defineSymbol("nogc")
|
||||||
|
of "stack":
|
||||||
|
gSelectedGC= gcStack
|
||||||
|
defineSymbol("gcstack")
|
||||||
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
|
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
|
||||||
of "warnings", "w":
|
of "warnings", "w":
|
||||||
if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
|
if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@ type # please make sure we have under 32 options
|
||||||
cmdRun # run the project via TCC backend
|
cmdRun # run the project via TCC backend
|
||||||
TStringSeq* = seq[string]
|
TStringSeq* = seq[string]
|
||||||
TGCMode* = enum # the selected GC
|
TGCMode* = enum # the selected GC
|
||||||
gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
|
gcNone, gcBoehm, gcGo, gcStack, gcMarkAndSweep, gcRefc,
|
||||||
|
gcV2, gcGenerational
|
||||||
|
|
||||||
IdeCmd* = enum
|
IdeCmd* = enum
|
||||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
|
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
|
||||||
|
|
|
||||||
|
|
@ -13,153 +13,7 @@
|
||||||
# - make searching for block O(1)
|
# - make searching for block O(1)
|
||||||
{.push profiler:off.}
|
{.push profiler:off.}
|
||||||
|
|
||||||
proc roundup(x, v: int): int {.inline.} =
|
include osalloc
|
||||||
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 -----------------------------
|
|
||||||
|
|
||||||
# We manage *chunks* of memory. Each chunk is a multiple of the page 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
|
# Each chunk starts at an address that is divisible by the page size. Chunks
|
||||||
|
|
|
||||||
|
|
@ -518,6 +518,9 @@ else:
|
||||||
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
||||||
when compileOption("gc", "v2"):
|
when compileOption("gc", "v2"):
|
||||||
include "system/gc2"
|
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):
|
elif defined(gcMarkAndSweep):
|
||||||
# XXX use 'compileOption' here
|
# XXX use 'compileOption' here
|
||||||
include "system/gc_ms"
|
include "system/gc_ms"
|
||||||
|
|
|
||||||
171
lib/system/osalloc.nim
Normal file
171
lib/system/osalloc.nim
Normal 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".}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue