inlining of the write barrier for dlls
This commit is contained in:
parent
c9e011e36c
commit
8098e2a421
25 changed files with 856 additions and 850 deletions
|
|
@ -531,48 +531,49 @@ proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
|||
|
||||
# ---------------------- interface to programs -------------------------------
|
||||
|
||||
proc alloc(size: int): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||
assert(not isAllocatedPtr(allocator, result))
|
||||
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||
when not defined(useNimRtl):
|
||||
proc alloc(size: int): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||
assert(not isAllocatedPtr(allocator, result))
|
||||
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||
|
||||
proc alloc0(size: int): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
proc alloc0(size: int): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc dealloc(p: pointer) =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||
rawDealloc(allocator, x)
|
||||
assert(not isAllocatedPtr(allocator, x))
|
||||
proc dealloc(p: pointer) =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||
rawDealloc(allocator, x)
|
||||
assert(not isAllocatedPtr(allocator, x))
|
||||
|
||||
proc ptrSize(p: pointer): int =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||
proc ptrSize(p: pointer): int =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||
|
||||
proc realloc(p: pointer, newsize: int): pointer =
|
||||
if newsize > 0:
|
||||
result = alloc(newsize)
|
||||
if p != nil:
|
||||
copyMem(result, p, ptrSize(p))
|
||||
proc realloc(p: pointer, newsize: int): pointer =
|
||||
if newsize > 0:
|
||||
result = alloc(newsize)
|
||||
if p != nil:
|
||||
copyMem(result, p, ptrSize(p))
|
||||
dealloc(p)
|
||||
elif p != nil:
|
||||
dealloc(p)
|
||||
elif p != nil:
|
||||
dealloc(p)
|
||||
|
||||
proc countFreeMem(): int =
|
||||
# only used for assertions
|
||||
var it = allocator.freeChunksList
|
||||
while it != nil:
|
||||
inc(result, it.size)
|
||||
it = it.next
|
||||
proc countFreeMem(): int =
|
||||
# only used for assertions
|
||||
var it = allocator.freeChunksList
|
||||
while it != nil:
|
||||
inc(result, it.size)
|
||||
it = it.next
|
||||
|
||||
proc getFreeMem(): int =
|
||||
result = allocator.freeMem
|
||||
#assert(result == countFreeMem())
|
||||
proc getFreeMem(): int =
|
||||
result = allocator.freeMem
|
||||
#assert(result == countFreeMem())
|
||||
|
||||
proc getTotalMem(): int = return allocator.currMem
|
||||
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||
proc getTotalMem(): int = return allocator.currMem
|
||||
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||
|
||||
when isMainModule:
|
||||
const iterations = 4000_000
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -61,11 +61,14 @@ when defined(posix):
|
|||
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
|
||||
importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc nimUnloadLibrary(lib: TLibHandle) =
|
||||
dlclose(lib)
|
||||
|
||||
proc nimLoadLibrary(path: string): TLibHandle =
|
||||
result = dlopen(path, RTLD_NOW)
|
||||
#c_fprintf(c_stdout, "%s\n", dlerror())
|
||||
|
||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
||||
result = dlsym(lib, name)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
|
||||
# Exception handling code. This is difficult because it has
|
||||
# to work if there is no more memory. Do not use ``sprintf``, etc. as they are
|
||||
# unsafe!
|
||||
# to work if there is no more memory (but it doesn't yet!).
|
||||
|
||||
when not defined(windows) or not defined(guiapp):
|
||||
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
||||
|
|
|
|||
|
|
@ -74,14 +74,6 @@ var
|
|||
# This is wasteful but safe. This is a lock against recursive garbage
|
||||
# collection, not a lock for threads!
|
||||
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerRtl.}
|
||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||
# stack. It is used by the code generator if it cannot decide wether a
|
||||
# reference is in the stack or not (this can happen for var parameters).
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
|
||||
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
||||
if (c.refcount and rcZct) == 0:
|
||||
c.refcount = c.refcount and not colorMask or rcZct
|
||||
|
|
@ -105,24 +97,6 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
|
|||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||
result = int(usrToCell(p).refcount) shr rcShift
|
||||
|
||||
proc GC_disable() = inc(recGcLock)
|
||||
proc GC_enable() =
|
||||
if recGcLock > 0: dec(recGcLock)
|
||||
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) =
|
||||
case strategy
|
||||
of gcThroughput: nil
|
||||
of gcResponsiveness: nil
|
||||
of gcOptimizeSpace: nil
|
||||
of gcOptimizeTime: nil
|
||||
|
||||
proc GC_enableMarkAndSweep() =
|
||||
cycleThreshold = InitialCycleThreshold
|
||||
|
||||
proc GC_disableMarkAndSweep() =
|
||||
cycleThreshold = high(cycleThreshold)-1
|
||||
# set to the max value to suppress the cycle detector
|
||||
|
||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||
|
|
@ -214,8 +188,13 @@ proc prepareDealloc(cell: PCell) =
|
|||
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||
dec(recGcLock)
|
||||
|
||||
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
|
||||
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
incl(gch.cycleRoots, c)
|
||||
|
||||
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
addZCT(gch.zct, c)
|
||||
|
||||
proc decRef(c: PCell) {.inline.} =
|
||||
when stressGC:
|
||||
|
|
@ -224,19 +203,19 @@ proc decRef(c: PCell) {.inline.} =
|
|||
assert(c.refcount >=% rcIncrement)
|
||||
c.refcount = c.refcount -% rcIncrement
|
||||
if c.refcount <% rcIncrement:
|
||||
addZCT(gch.zct, c)
|
||||
rtlAddZCT(c)
|
||||
elif canBeCycleRoot(c):
|
||||
incl(gch.cycleRoots, c)
|
||||
rtlAddCycleRoot(c)
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
c.refcount = c.refcount +% rcIncrement
|
||||
if canBeCycleRoot(c):
|
||||
incl(gch.cycleRoots, c)
|
||||
rtlAddCycleRoot(c)
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerRtl, inl.} = incRef(usrToCell(p))
|
||||
proc nimGCunref(p: pointer) {.compilerRtl, inl.} = decRef(usrToCell(p))
|
||||
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
|
||||
proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
|
||||
|
||||
proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||
proc asgnRef(dest: ppointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
assert(not isOnStack(dest))
|
||||
# BUGFIX: first incRef then decRef!
|
||||
|
|
@ -244,7 +223,7 @@ proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
|||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
dest^ = src
|
||||
|
||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc if it is known at compile time that no
|
||||
# cycle is possible.
|
||||
if src != nil:
|
||||
|
|
@ -254,30 +233,34 @@ proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
|||
var c = usrToCell(dest^)
|
||||
c.refcount = c.refcount -% rcIncrement
|
||||
if c.refcount <% rcIncrement:
|
||||
addZCT(gch.zct, c)
|
||||
rtlAddZCT(c)
|
||||
dest^ = src
|
||||
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) =
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
|
||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||
# stack. It is used by the code generator if it cannot decide wether a
|
||||
# reference is in the stack or not (this can happen for var parameters).
|
||||
if not IsOnStack(dest):
|
||||
if src != nil: incRef(usrToCell(src))
|
||||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
dest^ = src
|
||||
|
||||
proc initGC() =
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
Init(gch.cycleRoots)
|
||||
Init(gch.decStack)
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
when not defined(useNimRtl):
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
Init(gch.cycleRoots)
|
||||
Init(gch.decStack)
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||
var d = cast[TAddress](dest)
|
||||
|
|
@ -325,7 +308,7 @@ proc checkCollection {.inline.} =
|
|||
if recGcLock == 0:
|
||||
collectCT(gch)
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
# generates a new object and sets its reference counter to 0
|
||||
assert(typ.kind in {tyRef, tyString, tySequence})
|
||||
checkCollection()
|
||||
|
|
@ -357,7 +340,7 @@ proc newObj(typ: PNimType, size: int): pointer =
|
|||
gcTrace(res, csAllocated)
|
||||
result = cellToUsr(res)
|
||||
|
||||
proc newSeq(typ: PNimType, len: int): pointer =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).space = len
|
||||
|
|
@ -490,17 +473,18 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
|||
else:
|
||||
const stackIncreases = false
|
||||
|
||||
proc setStackBottom(theStackBottom: pointer) =
|
||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||
# the first init must be the one that defines the stack bottom:
|
||||
if stackBottom == nil: stackBottom = theStackBottom
|
||||
else:
|
||||
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
|
||||
var b = cast[TAddress](stackBottom)
|
||||
when stackIncreases:
|
||||
stackBottom = cast[pointer](min(a, b))
|
||||
when not defined(useNimRtl):
|
||||
proc setStackBottom(theStackBottom: pointer) =
|
||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||
# the first init must be the one that defines the stack bottom:
|
||||
if stackBottom == nil: stackBottom = theStackBottom
|
||||
else:
|
||||
stackBottom = cast[pointer](max(a, b))
|
||||
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
|
||||
var b = cast[TAddress](stackBottom)
|
||||
when stackIncreases:
|
||||
stackBottom = cast[pointer](min(a, b))
|
||||
else:
|
||||
stackBottom = cast[pointer](max(a, b))
|
||||
|
||||
proc stackSize(): int {.noinline.} =
|
||||
var stackTop: array[0..1, pointer]
|
||||
|
|
@ -647,22 +631,41 @@ proc collectCT(gch: var TGcHeap) =
|
|||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
|
||||
unmarkStackAndRegisters(gch)
|
||||
|
||||
proc GC_fullCollect() =
|
||||
var oldThreshold = cycleThreshold
|
||||
cycleThreshold = 0 # forces cycle collection
|
||||
collectCT(gch)
|
||||
cycleThreshold = oldThreshold
|
||||
when not defined(useNimRtl):
|
||||
proc GC_disable() = inc(recGcLock)
|
||||
proc GC_enable() =
|
||||
if recGcLock > 0: dec(recGcLock)
|
||||
|
||||
proc GC_getStatistics(): string =
|
||||
GC_disable()
|
||||
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max stack size: " & $gch.stat.maxStackSize
|
||||
when traceGC: writeLeakage()
|
||||
GC_enable()
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) =
|
||||
case strategy
|
||||
of gcThroughput: nil
|
||||
of gcResponsiveness: nil
|
||||
of gcOptimizeSpace: nil
|
||||
of gcOptimizeTime: nil
|
||||
|
||||
proc GC_enableMarkAndSweep() =
|
||||
cycleThreshold = InitialCycleThreshold
|
||||
|
||||
proc GC_disableMarkAndSweep() =
|
||||
cycleThreshold = high(cycleThreshold)-1
|
||||
# set to the max value to suppress the cycle detector
|
||||
|
||||
proc GC_fullCollect() =
|
||||
var oldThreshold = cycleThreshold
|
||||
cycleThreshold = 0 # forces cycle collection
|
||||
collectCT(gch)
|
||||
cycleThreshold = oldThreshold
|
||||
|
||||
proc GC_getStatistics(): string =
|
||||
GC_disable()
|
||||
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max stack size: " & $gch.stat.maxStackSize
|
||||
when traceGC: writeLeakage()
|
||||
GC_enable()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ when defined(createNimRtl):
|
|||
{.error: "nimrtl must be built as a library!".}
|
||||
|
||||
when defined(createNimRtl):
|
||||
# NOTE: compilerproc cannot make use of name mangling!
|
||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
|
||||
{.pragma: inl.}
|
||||
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
||||
|
|
|
|||
|
|
@ -187,28 +187,6 @@ elif defined(nogc):
|
|||
dest^ = src
|
||||
|
||||
include "system/cellsets"
|
||||
elif defined(useNimRtl):
|
||||
proc initGC() = nil
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.}
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerRtl.}
|
||||
proc nimGCunref(p: pointer) {.compilerRtl.}
|
||||
|
||||
# The write barrier is performance critical!
|
||||
# XXX We should ensure that they are inlined here.
|
||||
# Later implementations will do this.
|
||||
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.
|
||||
compilerRtl.}
|
||||
proc asgnRef(dest: ppointer, src: pointer) {.
|
||||
compilerRtl.}
|
||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.
|
||||
compilerRtl.}
|
||||
|
||||
include "system/cellsets"
|
||||
|
||||
else:
|
||||
include "system/alloc"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue