inlining of the write barrier for dlls

This commit is contained in:
Andreas Rumpf 2010-08-08 22:45:21 +02:00
commit 8098e2a421
25 changed files with 856 additions and 850 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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