simple stuff works with --gc:stack
This commit is contained in:
parent
ef99a2cf15
commit
79f64d2469
4 changed files with 272 additions and 212 deletions
|
|
@ -2565,8 +2565,9 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
|
|
||||||
when not defined(nimscript) and not defined(nogc):
|
when not defined(nimscript) and not defined(nogc):
|
||||||
|
when not defined(gcStack):
|
||||||
proc initGC()
|
proc initGC()
|
||||||
when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc):
|
when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc) and not defined(gcStack):
|
||||||
proc initAllocator() {.inline.}
|
proc initAllocator() {.inline.}
|
||||||
|
|
||||||
proc initStackBottom() {.inline, compilerproc.} =
|
proc initStackBottom() {.inline, compilerproc.} =
|
||||||
|
|
|
||||||
|
|
@ -36,33 +36,34 @@ type
|
||||||
BaseChunk = object
|
BaseChunk = object
|
||||||
next: Chunk
|
next: Chunk
|
||||||
size: int
|
size: int
|
||||||
head, last: ptr ObjHeader # first and last object in chunk that
|
head, tail: ptr ObjHeader # first and last object in chunk that
|
||||||
# has a finalizer attached to it
|
# has a finalizer attached to it
|
||||||
|
|
||||||
type
|
type
|
||||||
StackPtr = object
|
StackPtr = object
|
||||||
chunk: pointer
|
bump: pointer
|
||||||
remaining: int
|
remaining: int
|
||||||
current: Chunk
|
current: Chunk
|
||||||
|
|
||||||
MemRegion* = object
|
MemRegion* = object
|
||||||
remaining: int
|
remaining: int
|
||||||
chunk: pointer
|
bump: pointer
|
||||||
head, last: Chunk
|
head, tail: Chunk
|
||||||
nextChunkSize, totalSize: int
|
nextChunkSize, totalSize: int
|
||||||
hole: ptr Hole # we support individual freeing
|
hole: ptr Hole # we support individual freeing
|
||||||
|
when hasThreadSupport:
|
||||||
lock: SysLock
|
lock: SysLock
|
||||||
|
|
||||||
var
|
var
|
||||||
region {.threadVar.}: MemRegion
|
tlRegion {.threadVar.}: MemRegion
|
||||||
|
|
||||||
template withRegion*(r: MemRegion; body: untyped) =
|
template withRegion*(r: MemRegion; body: untyped) =
|
||||||
let oldRegion = region
|
let oldRegion = tlRegion
|
||||||
region = r
|
tlRegion = r
|
||||||
try:
|
try:
|
||||||
body
|
body
|
||||||
finally:
|
finally:
|
||||||
region = oldRegion
|
tlRegion = oldRegion
|
||||||
|
|
||||||
template inc(p: pointer, s: int) =
|
template inc(p: pointer, s: int) =
|
||||||
p = cast[pointer](cast[int](p) +% s)
|
p = cast[pointer](cast[int](p) +% s)
|
||||||
|
|
@ -71,7 +72,7 @@ template `+!`(p: pointer, s: int): pointer =
|
||||||
cast[pointer](cast[int](p) +% s)
|
cast[pointer](cast[int](p) +% s)
|
||||||
|
|
||||||
template `-!`(p: pointer, s: int): pointer =
|
template `-!`(p: pointer, s: int): pointer =
|
||||||
cast[pointer](cast[int](p) +% s)
|
cast[pointer](cast[int](p) -% s)
|
||||||
|
|
||||||
proc allocSlowPath(r: var MemRegion; size: int) =
|
proc allocSlowPath(r: var MemRegion; size: int) =
|
||||||
# we need to ensure that the underlying linked list
|
# we need to ensure that the underlying linked list
|
||||||
|
|
@ -84,7 +85,7 @@ proc allocSlowPath(r: var MemRegion; size: int) =
|
||||||
r.nextChunkSize =
|
r.nextChunkSize =
|
||||||
if r.totalSize < 64 * 1024: PageSize*4
|
if r.totalSize < 64 * 1024: PageSize*4
|
||||||
else: r.nextChunkSize*2
|
else: r.nextChunkSize*2
|
||||||
var s = align(size+sizeof(BaseChunk), PageSize)
|
var s = roundup(size+sizeof(BaseChunk), PageSize)
|
||||||
var fresh: Chunk
|
var fresh: Chunk
|
||||||
if s > r.nextChunkSize:
|
if s > r.nextChunkSize:
|
||||||
fresh = cast[Chunk](osAllocPages(s))
|
fresh = cast[Chunk](osAllocPages(s))
|
||||||
|
|
@ -97,22 +98,25 @@ proc allocSlowPath(r: var MemRegion; size: int) =
|
||||||
else:
|
else:
|
||||||
s = r.nextChunkSize
|
s = r.nextChunkSize
|
||||||
fresh.size = s
|
fresh.size = s
|
||||||
fresh.final = nil
|
fresh.head = nil
|
||||||
r.totalSize += s
|
fresh.tail = nil
|
||||||
let old = r.last
|
inc r.totalSize, s
|
||||||
|
let old = r.tail
|
||||||
if old == nil:
|
if old == nil:
|
||||||
r.head = fresh
|
r.head = fresh
|
||||||
else:
|
else:
|
||||||
r.last.next = fresh
|
r.tail.next = fresh
|
||||||
r.chunk = fresh +! sizeof(BaseChunk)
|
r.bump = fresh +! sizeof(BaseChunk)
|
||||||
r.last = fresh
|
r.tail = fresh
|
||||||
r.remaining = s - sizeof(BaseChunk)
|
r.remaining = s - sizeof(BaseChunk)
|
||||||
|
|
||||||
proc alloc(r: var MemRegion; size: int): pointer {.inline.} =
|
proc alloc(r: var MemRegion; size: int): pointer {.inline.} =
|
||||||
if unlikely(r.remaining < size): allocSlowPath(r, size)
|
if size > r.remaining:
|
||||||
|
allocSlowPath(r, size)
|
||||||
|
sysAssert(size <= r.remaining, "size <= r.remaining")
|
||||||
dec(r.remaining, size)
|
dec(r.remaining, size)
|
||||||
result = r.chunk
|
result = r.bump
|
||||||
inc r.chunk, size
|
inc r.bump, size
|
||||||
|
|
||||||
proc runFinalizers(c: Chunk) =
|
proc runFinalizers(c: Chunk) =
|
||||||
var it = c.head
|
var it = c.head
|
||||||
|
|
@ -120,63 +124,76 @@ proc runFinalizers(c: Chunk) =
|
||||||
# indivually freed objects with finalizer stay in the list, but
|
# indivually freed objects with finalizer stay in the list, but
|
||||||
# their typ is nil then:
|
# their typ is nil then:
|
||||||
if it.typ != nil and it.typ.finalizer != nil:
|
if it.typ != nil and it.typ.finalizer != nil:
|
||||||
(cast[Finalizer](cell.typ.finalizer))(cell+!sizeof(ObjHeader))
|
(cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
|
||||||
it = it.next
|
it = it.nextFinal
|
||||||
|
|
||||||
proc dealloc(r: var MemRegion; p: pointer) =
|
proc dealloc(r: var MemRegion; p: pointer) =
|
||||||
let it = p-!sizeof(ObjHeader)
|
let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
|
||||||
if it.typ != nil and it.typ.finalizer != nil:
|
if it.typ != nil and it.typ.finalizer != nil:
|
||||||
(cast[Finalizer](cell.typ.finalizer))(p)
|
(cast[Finalizer](it.typ.finalizer))(p)
|
||||||
it.typ = nil
|
it.typ = nil
|
||||||
|
|
||||||
proc deallocAll(head: Chunk) =
|
proc deallocAll(r: var MemRegion; head: Chunk) =
|
||||||
var it = head
|
var it = head
|
||||||
while it != nil:
|
while it != nil:
|
||||||
|
let nxt = it.next
|
||||||
runFinalizers(it)
|
runFinalizers(it)
|
||||||
|
dec r.totalSize, it.size
|
||||||
osDeallocPages(it, it.size)
|
osDeallocPages(it, it.size)
|
||||||
it = it.next
|
it = nxt
|
||||||
|
|
||||||
proc deallocAll*(r: var MemRegion) =
|
proc deallocAll*(r: var MemRegion) =
|
||||||
deallocAll(r.head)
|
deallocAll(r, r.head)
|
||||||
zeroMem(addr r, sizeof r)
|
zeroMem(addr r, sizeof r)
|
||||||
|
|
||||||
proc obstackPtr*(r: MemRegion): StackPtr =
|
proc obstackPtr*(r: MemRegion): StackPtr =
|
||||||
result.chunk = r.chunk
|
result.bump = r.bump
|
||||||
result.remaining = r.remaining
|
result.remaining = r.remaining
|
||||||
result.current = r.last
|
result.current = r.tail
|
||||||
|
|
||||||
proc setObstackPtr*(r: MemRegion; sp: StackPtr) =
|
template computeRemaining(r): untyped =
|
||||||
|
r.tail.size -% (cast[int](r.bump) -% cast[int](r.tail))
|
||||||
|
|
||||||
|
proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
|
||||||
# free everything after 'sp':
|
# free everything after 'sp':
|
||||||
if sp.current != nil:
|
if sp.current != nil:
|
||||||
deallocAll(sp.current.next)
|
deallocAll(r, sp.current.next)
|
||||||
r.chunk = sp.chunk
|
sp.current.next = nil
|
||||||
|
else:
|
||||||
|
deallocAll(r, r.head)
|
||||||
|
r.head = nil
|
||||||
|
r.bump = sp.bump
|
||||||
|
r.tail = sp.current
|
||||||
r.remaining = sp.remaining
|
r.remaining = sp.remaining
|
||||||
r.last = sp.current
|
|
||||||
|
proc obstackPtr*(): StackPtr = tlRegion.obstackPtr()
|
||||||
|
proc setObstackPtr*(sp: StackPtr) = tlRegion.setObstackPtr(sp)
|
||||||
|
|
||||||
proc joinRegion*(dest: var MemRegion; src: MemRegion) =
|
proc joinRegion*(dest: var MemRegion; src: MemRegion) =
|
||||||
# merging is not hard.
|
# merging is not hard.
|
||||||
if dest.head.isNil:
|
if dest.head.isNil:
|
||||||
dest.head = src.head
|
dest.head = src.head
|
||||||
else:
|
else:
|
||||||
dest.last.next = src.head
|
dest.tail.next = src.head
|
||||||
dest.last = src.last
|
dest.tail = src.tail
|
||||||
dest.chunk = src.chunk
|
dest.bump = src.bump
|
||||||
dest.remaining = src.remaining
|
dest.remaining = src.remaining
|
||||||
dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
|
dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
|
||||||
dest.totalSize += src.totalSize
|
inc dest.totalSize, src.totalSize
|
||||||
if dest.hole.size < src.hole.size:
|
|
||||||
dest.hole = src.hole
|
|
||||||
|
|
||||||
proc isOnHeap*(r: MemRegion; p: pointer): bool =
|
proc isOnHeap*(r: MemRegion; p: pointer): bool =
|
||||||
# the last chunk is the largest, so check it first. It's also special
|
# the tail chunk is the largest, so check it first. It's also special
|
||||||
# in that contains the current bump pointer:
|
# in that contains the current bump pointer:
|
||||||
if r.last >= p and p < r.chunk:
|
if r.tail >= p and p < r.bump:
|
||||||
return true
|
return true
|
||||||
var it = r.head
|
var it = r.head
|
||||||
while it != r.last:
|
while it != r.tail:
|
||||||
if it >= p and p <= it+!it.size: return true
|
if it >= p and p <= it+!it.size: return true
|
||||||
it = it.next
|
it = it.next
|
||||||
|
|
||||||
|
when false:
|
||||||
|
# essential feature for later: copy data over from one region to another
|
||||||
|
|
||||||
proc isInteriorPointer(r: MemRegion; p: pointer): pointer =
|
proc isInteriorPointer(r: MemRegion; p: pointer): pointer =
|
||||||
discard " we cannot patch stack pointers anyway!"
|
discard " we cannot patch stack pointers anyway!"
|
||||||
|
|
||||||
|
|
@ -331,17 +348,17 @@ proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer =
|
||||||
var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader)))
|
var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader)))
|
||||||
res.typ = typ
|
res.typ = typ
|
||||||
if typ.finalizer != nil:
|
if typ.finalizer != nil:
|
||||||
res.nextFinal = r.chunk.head
|
res.nextFinal = r.head.head
|
||||||
r.chunk.head = res
|
r.head.head = res
|
||||||
result = res +! sizeof(ObjHeader)
|
result = res +! sizeof(ObjHeader)
|
||||||
|
|
||||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
result = rawNewObj(typ, size, region)
|
result = rawNewObj(tlRegion, typ, size)
|
||||||
zeroMem(result, size)
|
zeroMem(result, size)
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
result = rawNewObj(typ, size, region)
|
result = rawNewObj(tlRegion, typ, size)
|
||||||
when defined(memProfiler): nimProfile(size)
|
when defined(memProfiler): nimProfile(size)
|
||||||
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
|
|
@ -351,7 +368,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
|
||||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
result = rawNewObj(typ, size, gch)
|
result = rawNewObj(tlRegion, typ, size)
|
||||||
zeroMem(result, size)
|
zeroMem(result, size)
|
||||||
|
|
||||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
|
|
@ -360,23 +377,63 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).reserved = len
|
cast[PGenericSeq](result).reserved = len
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
proc growObj(region: var MemRegion; old: pointer, newsize: int): pointer =
|
||||||
collectCT(gch)
|
let typ = cast[ptr ObjHeader](old -! sizeof(ObjHeader)).typ
|
||||||
var ol = usrToCell(old)
|
result = rawNewObj(region, typ, newsize)
|
||||||
sysAssert(ol.typ != nil, "growObj: 1")
|
let elemSize = if typ.kind == tyString: 1 else: typ.base.size
|
||||||
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
|
let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||||
|
copyMem(result, old, oldsize)
|
||||||
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
|
zeroMem(result +! oldsize, newsize-oldsize)
|
||||||
var elemSize = 1
|
|
||||||
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
|
|
||||||
|
|
||||||
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
|
||||||
copyMem(res, ol, oldsize + sizeof(Cell))
|
|
||||||
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
|
|
||||||
newsize-oldsize)
|
|
||||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
|
|
||||||
result = cellToUsr(res)
|
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||||
result = growObj(old, newsize, region)
|
result = growObj(tlRegion, old, newsize)
|
||||||
|
|
||||||
|
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest[] = src
|
||||||
|
proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest[] = src
|
||||||
|
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest[] = src
|
||||||
|
|
||||||
|
proc alloc(size: Natural): pointer =
|
||||||
|
result = cmalloc(size)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc alloc0(size: Natural): pointer =
|
||||||
|
result = alloc(size)
|
||||||
|
zeroMem(result, size)
|
||||||
|
proc realloc(p: pointer, newsize: Natural): pointer =
|
||||||
|
result = crealloc(p, newsize)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc dealloc(p: pointer) = cfree(p)
|
||||||
|
|
||||||
|
proc allocShared(size: Natural): pointer =
|
||||||
|
result = cmalloc(size)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc allocShared0(size: Natural): pointer =
|
||||||
|
result = alloc(size)
|
||||||
|
zeroMem(result, size)
|
||||||
|
proc reallocShared(p: pointer, newsize: Natural): pointer =
|
||||||
|
result = crealloc(p, newsize)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc deallocShared(p: pointer) = cfree(p)
|
||||||
|
|
||||||
|
when hasThreadSupport:
|
||||||
|
proc getFreeSharedMem(): int = 0
|
||||||
|
proc getTotalSharedMem(): int = 0
|
||||||
|
proc getOccupiedSharedMem(): int = 0
|
||||||
|
|
||||||
|
proc GC_disable() = discard
|
||||||
|
proc GC_enable() = discard
|
||||||
|
proc GC_fullCollect() = discard
|
||||||
|
proc GC_setStrategy(strategy: GC_Strategy) = discard
|
||||||
|
proc GC_enableMarkAndSweep() = discard
|
||||||
|
proc GC_disableMarkAndSweep() = discard
|
||||||
|
proc GC_getStatistics(): string = return ""
|
||||||
|
|
||||||
|
proc getOccupiedMem(): int =
|
||||||
|
result = tlRegion.totalSize - tlRegion.remaining
|
||||||
|
proc getFreeMem(): int = tlRegion.remaining
|
||||||
|
proc getTotalMem(): int =
|
||||||
|
result = tlRegion.totalSize
|
||||||
|
|
||||||
|
proc setStackBottom(theStackBottom: pointer) = discard
|
||||||
|
|
|
||||||
|
|
@ -511,6 +511,7 @@ elif defined(nogc):
|
||||||
include "system/cellsets"
|
include "system/cellsets"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
when not defined(gcStack):
|
||||||
include "system/alloc"
|
include "system/alloc"
|
||||||
|
|
||||||
include "system/cellsets"
|
include "system/cellsets"
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,8 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||||
elif newLen < result.len:
|
elif newLen < result.len:
|
||||||
# we need to decref here, otherwise the GC leaks!
|
# we need to decref here, otherwise the GC leaks!
|
||||||
when not defined(boehmGC) and not defined(nogc) and
|
when not defined(boehmGC) and not defined(nogc) and
|
||||||
not defined(gcMarkAndSweep) and not defined(gogc):
|
not defined(gcMarkAndSweep) and not defined(gogc) and
|
||||||
|
not defined(gcStack):
|
||||||
when false: # compileOption("gc", "v2"):
|
when false: # compileOption("gc", "v2"):
|
||||||
for i in newLen..result.len-1:
|
for i in newLen..result.len-1:
|
||||||
let len0 = gch.tempStack.len
|
let len0 = gch.tempStack.len
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue