Merge pull request #2851 from stefantalpalaru/gogc

the Go GC (using the gccgo implementation)
This commit is contained in:
Andreas Rumpf
2015-06-16 20:46:23 +02:00
10 changed files with 228 additions and 17 deletions

View File

@@ -22,6 +22,7 @@ bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import
@@ -86,7 +87,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC)
msgQuit(0)
var
@@ -181,6 +182,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
of "v2": result = gSelectedGC == gcV2
of "markandsweep": result = gSelectedGC == gcMarkAndSweep
of "generational": result = gSelectedGC == gcGenerational
of "go": result = gSelectedGC == gcGo
of "none": result = gSelectedGC == gcNone
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "opt":
@@ -365,6 +367,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "generational":
gSelectedGC = gcGenerational
defineSymbol("gcgenerational")
of "go":
gSelectedGC = gcGo
defineSymbol("gogc")
of "none":
gSelectedGC = gcNone
defineSymbol("nogc")

View File

@@ -82,7 +82,7 @@ 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, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
IdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse

View File

@@ -58,7 +58,7 @@ Advanced options:
--skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files
--skipProjCfg do not read the project's configuration file
--gc:refc|v2|markAndSweep|boehm|none
--gc:refc|v2|markAndSweep|boehm|go|none
select the GC to use; default is 'refc'
--index:on|off turn index file generation on|off
--putenv:key=value set an environment variable

View File

@@ -71,7 +71,7 @@ options:
are limited to pure Nim code at compilation time. Enabling
this switch will allow macros to execute non-nim code at
compilation time (eg. open a file and write to it).
--gc:refc|v2|markAndSweep|boehm|none
--gc:refc|v2|markAndSweep|boehm|go|none
Selects which garbage collection strategy to use for the compiler
and generated code. See the `Nim's Garbage Collector <gc.html>`_
documentation for more information.

View File

@@ -68,11 +68,15 @@ type
TGenericSeq {.importc.} = object
len, space: int
when defined(gogc):
elemSize: int
PGenSeq = ptr TGenericSeq
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
const
GenericSeqSize = (2 * sizeof(int))
when defined(gogc):
const GenericSeqSize = (3 * sizeof(int))
else:
const GenericSeqSize = (2 * sizeof(int))
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.

View File

@@ -305,6 +305,8 @@ when not defined(JS):
type
TGenericSeq {.compilerproc, pure, inheritable.} = object
len, reserved: int
when defined(gogc):
elemSize: int
PGenericSeq {.exportc.} = ptr TGenericSeq
UncheckedCharArray {.unchecked.} = array[0..ArrayDummySize, char]
# len and space without counting the terminating zero:
@@ -1092,7 +1094,7 @@ proc compileOption*(option, arg: string): bool {.
const
hasThreadSupport = compileOption("threads")
hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode")
when taintMode:
@@ -2347,7 +2349,7 @@ when not defined(JS): #and not defined(NimrodVM):
when not defined(NimrodVM) and hostOS != "standalone":
proc initGC()
when not defined(boehmgc) and not defined(useMalloc):
when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc):
proc initAllocator() {.inline.}
proc initStackBottom() {.inline, compilerproc.} =
@@ -2682,8 +2684,10 @@ when not defined(JS): #and not defined(NimrodVM):
when not defined(NimrodVM):
include "system/sets"
const
GenericSeqSize = (2 * sizeof(int))
when defined(gogc):
const GenericSeqSize = (3 * sizeof(int))
else:
const GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
# Nim high-level memory manager: It supports Boehm's GC, no GC and the
# Nim high-level memory manager: It supports Boehm's GC, Go's GC, no GC and the
# native Nim GC. The native Nim GC is the default.
#{.push checks:on, assertions:on.}
@@ -195,6 +195,201 @@ when defined(boehmgc):
proc deallocOsPages() {.inline.} = discard
include "system/cellsets"
elif defined(gogc):
when defined(windows):
const goLib = "libgo.dll"
elif defined(macosx):
const goLib = "libgo.dylib"
else:
const goLib = "libgo.so"
proc `div`[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
proc `-`[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.}
proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1)
proc initGC() = discard
# runtime_setgcpercent is only available in GCC 5
proc GC_disable() = discard
proc GC_enable() = discard
proc goRuntimeGC(force: int32) {.importc: "runtime_gc", dynlib: goLib.}
proc GC_fullCollect() = goRuntimeGC(2)
proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
const
goNumSizeClasses = 67
type
cbool {.importc: "_Bool", nodecl.} = bool
goMStats_inner_struct = object
size: uint32
nmalloc: uint64
nfree: uint64
goMStats = object
# General statistics.
alloc: uint64 # bytes allocated and still in use
total_alloc: uint64 # bytes allocated (even if freed)
sys: uint64 # bytes obtained from system (should be sum of xxx_sys below, no locking, approximate)
nlookup: uint64 # number of pointer lookups
nmalloc: uint64 # number of mallocs
nfree: uint64 # number of frees
# Statistics about malloc heap.
# protected by mheap.Lock
heap_alloc: uint64 # bytes allocated and still in use
heap_sys: uint64 # bytes obtained from system
heap_idle: uint64 # bytes in idle spans
heap_inuse: uint64 # bytes in non-idle spans
heap_released: uint64 # bytes released to the OS
heap_objects: uint64 # total number of allocated objects
# Statistics about allocation of low-level fixed-size structures.
# Protected by FixAlloc locks.
stacks_inuse: uint64 # bootstrap stacks
stacks_sys: uint64
mspan_inuse: uint64 # MSpan structures
mspan_sys: uint64
mcache_inuse: uint64 # MCache structures
mcache_sys: uint64
buckhash_sys: uint64 # profiling bucket hash table
gc_sys: uint64
other_sys: uint64
# Statistics about garbage collector.
# Protected by mheap or stopping the world during GC.
next_gc: uint64 # next GC (in heap_alloc time)
last_gc: uint64 # last GC (in absolute time)
pause_total_ns: uint64
pause_ns: array[256, uint64]
numgc: uint32
enablegc: cbool
debuggc: cbool
# Statistics about allocation size classes.
by_size: array[goNumSizeClasses, goMStats_inner_struct]
proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl, importc: "runtime_ReadMemStats", codegenDecl: "$1 $2$3 __asm__ (\"runtime.ReadMemStats\");\n$1 $2$3", dynlib: goLib.}
proc GC_getStatistics(): string =
var mstats: goMStats
goRuntime_ReadMemStats(addr mstats)
result = "[GC] total allocated memory: " & $(mstats.total_alloc) & "\n" &
"[GC] total memory obtained from system: " & $(mstats.sys) & "\n" &
"[GC] occupied memory: " & $(mstats.alloc) & "\n" &
"[GC] number of pointer lookups: " & $(mstats.nlookup) & "\n" &
"[GC] number of mallocs: " & $(mstats.nmalloc) & "\n" &
"[GC] number of frees: " & $(mstats.nfree) & "\n" &
"[GC] heap objects: " & $(mstats.heap_objects) & "\n" &
"[GC] numgc: " & $(mstats.numgc) & "\n" &
"[GC] enablegc: " & $(mstats.enablegc) & "\n" &
"[GC] debuggc: " & $(mstats.debuggc) & "\n" &
"[GC] total pause time [ms]: " & $(mstats.pause_total_ns div 1000_000)
proc getOccupiedMem(): int =
var mstats: goMStats
goRuntime_ReadMemStats(addr mstats)
result = int(mstats.alloc)
proc getFreeMem(): int =
var mstats: goMStats
goRuntime_ReadMemStats(addr mstats)
result = int(mstats.sys - mstats.alloc)
proc getTotalMem(): int =
var mstats: goMStats
goRuntime_ReadMemStats(addr mstats)
result = int(mstats.sys)
proc setStackBottom(theStackBottom: pointer) = discard
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 = discard
proc getTotalSharedMem(): int = discard
proc getOccupiedSharedMem(): int = discard
const goFlagNoZero: uint32 = 1 shl 3
proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
proc goFree(v: pointer) {.importc: "__go_free", dynlib: goLib.}
proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = goRuntimeMallocGC(roundup(size, sizeof(pointer)).uint, 0.uint, 0.uint32)
if typ.finalizer != nil:
goSetFinalizer(result, typ.finalizer)
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = goRuntimeMallocGC(roundup(size, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
if typ.finalizer != nil:
goSetFinalizer(result, typ.finalizer)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, len * typ.base.size + GenericSeqSize)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
cast[PGenericSeq](result).elemSize = typ.base.size
proc growObj(old: pointer, newsize: int): pointer =
# the Go GC doesn't have a realloc
var
oldsize = cast[PGenericSeq](old).len * cast[PGenericSeq](old).elemSize + GenericSeqSize
result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
copyMem(result, old, oldsize)
zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
goFree(old)
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
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
type
MemRegion = object {.final, pure.}
{.deprecated: [TMemRegion: MemRegion].}
proc alloc(r: var MemRegion, size: int): pointer =
result = alloc(size)
proc alloc0(r: var MemRegion, size: int): pointer =
result = alloc0(size)
proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard
elif defined(nogc) and defined(useMalloc):
when not defined(useNimRtl):
@@ -291,7 +486,6 @@ elif defined(nogc):
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size)

View File

@@ -205,7 +205,7 @@ when not defined(useNimRtl):
cl: var ReprClosure) =
# we know that p is not nil here:
when declared(CellSet):
when defined(boehmGC) or defined(nogc):
when defined(boehmGC) or defined(gogc) or defined(nogc):
var cell = cast[PCell](p)
else:
var cell = usrToCell(p)

View File

@@ -50,12 +50,16 @@ proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
if s < 7: s = 7
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
result.reserved = s
when defined(gogc):
result.elemSize = 1
proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space
if s < 7: s = 7
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s
when defined(gogc):
result.elemSize = 1
proc mnewString(len: int): NimString {.compilerProc.} =
result = rawNewString(len)
@@ -216,7 +220,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
elif newLen < result.len:
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep):
not defined(gcMarkAndSweep) and not defined(gogc):
when compileOption("gc", "v2"):
for i in newLen..result.len-1:
let len0 = gch.tempStack.len

View File

@@ -237,7 +237,7 @@ when not defined(useNimRtl):
echo "too large thread local storage size requested"
quit 1
when hasSharedHeap and not defined(boehmgc) and not defined(nogc):
when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
var
threadList: PGcThread
@@ -287,7 +287,7 @@ type
## a pointer as a thread ID.
{.deprecated: [TThread: Thread, TThreadId: ThreadId].}
when not defined(boehmgc) and not hasSharedHeap:
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc):
proc deallocOsPages()
template threadProcWrapperBody(closure: expr) {.immediate.} =
@@ -295,7 +295,7 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
var t = cast[ptr Thread[TArg]](closure)
when useStackMaskHack:
var tls: ThreadLocalStorage
when not defined(boehmgc) and not defined(nogc) and not hasSharedHeap:
when not defined(boehmgc) and not defined(gogc) and not defined(nogc) and not hasSharedHeap:
# init the GC for this thread:
setStackBottom(addr(t))
initGC()