diff options
-rw-r--r-- | compiler/commands.nim | 7 | ||||
-rw-r--r-- | compiler/options.nim | 2 | ||||
-rw-r--r-- | doc/advopt.txt | 2 | ||||
-rw-r--r-- | doc/koch.txt | 2 | ||||
-rw-r--r-- | lib/core/typeinfo.nim | 8 | ||||
-rw-r--r-- | lib/system.nim | 12 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 198 | ||||
-rw-r--r-- | lib/system/repr.nim | 2 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 6 | ||||
-rw-r--r-- | lib/system/threads.nim | 6 |
10 files changed, 228 insertions, 17 deletions
diff --git a/compiler/commands.nim b/compiler/commands.nim index 285d73e7a..7c8abd9b8 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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") diff --git a/compiler/options.nim b/compiler/options.nim index 42a13f8e8..f3277f855 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -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 diff --git a/doc/advopt.txt b/doc/advopt.txt index 195122cc7..a4f72e2d8 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -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 diff --git a/doc/koch.txt b/doc/koch.txt index 7da137458..a58d386ea 100644 --- a/doc/koch.txt +++ b/doc/koch.txt @@ -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. diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index ab150b2a4..dc97784c3 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -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) {. diff --git a/lib/system.nim b/lib/system.nim index 62c024d77..2beb5b88d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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") diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 86d834ceb..50f4cb4dc 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -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) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index f3b69954a..efa85b243 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -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) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 5b4020c8c..33629016f 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -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 diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 865271a50..d77059f68 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -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() |