gogc: GCC-8.2.0 compatibility and other improvements (#9211)
- Go's write barriers are now plugged-in in all the relevant points - "gcGo" is correctly classified by usesWriteBarrier() - some gogc structures and functions now use golib wrappers to keep GCC version-specific conditions out of the compiler/stdlib code - we no longer allow mixing the C malloc with Go's - fix a problem with string copying
This commit is contained in:
parent
d48e964950
commit
10f5f67767
5 changed files with 83 additions and 118 deletions
|
|
@ -197,15 +197,11 @@ elif defined(gogc):
|
|||
else:
|
||||
const goLib = "libgo.so"
|
||||
|
||||
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 go_gc() {.importc: "go_gc", dynlib: goLib.}
|
||||
proc GC_fullCollect() = go_gc()
|
||||
proc GC_setStrategy(strategy: GC_Strategy) = discard
|
||||
proc GC_enableMarkAndSweep() = discard
|
||||
proc GC_disableMarkAndSweep() = discard
|
||||
|
|
@ -214,67 +210,24 @@ elif defined(gogc):
|
|||
goNumSizeClasses = 67
|
||||
|
||||
type
|
||||
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] # circular buffer of recent gc pause lengths
|
||||
pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
|
||||
numgc: uint32
|
||||
numforcedgc: uint32 # number of user-forced GCs
|
||||
gc_cpu_fraction: float64 # fraction of CPU time used by GC
|
||||
enablegc: bool
|
||||
debuggc: bool
|
||||
# Statistics about allocation size classes.
|
||||
by_size: array[goNumSizeClasses, goMStats_inner_struct]
|
||||
# Statistics below here are not exported to MemStats directly.
|
||||
tinyallocs: uint64 # number of tiny allocations that didn't cause actual allocation; not exported to go directly
|
||||
gc_trigger: uint64
|
||||
heap_live: uint64
|
||||
heap_scan: uint64
|
||||
heap_marked: uint64
|
||||
alloc: uint64 # bytes allocated and still in use
|
||||
total_alloc: uint64 # bytes allocated (even if freed)
|
||||
sys: uint64 # bytes obtained from system
|
||||
nlookup: uint64 # number of pointer lookups
|
||||
nmalloc: uint64 # number of mallocs
|
||||
nfree: uint64 # number of frees
|
||||
heap_objects: uint64 # total number of allocated objects
|
||||
pause_total_ns: uint64 # cumulative nanoseconds in GC stop-the-world pauses since the program started
|
||||
numgc: uint32 # number of completed GC cycles
|
||||
|
||||
proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl,
|
||||
importc: "runtime_ReadMemStats",
|
||||
codegenDecl: "$1 $2$3 __asm__ (\"runtime.ReadMemStats\");\n$1 $2$3",
|
||||
dynlib: goLib.}
|
||||
proc goMemStats(): goMStats {.importc: "go_mem_stats", dynlib: goLib.}
|
||||
proc goMalloc(size: uint): pointer {.importc: "go_malloc", 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 writebarrierptr(dest: PPointer, src: pointer) {.importc: "writebarrierptr", codegenDecl:"$1 $2$3 __asm__ (\"main.Atomic_store_pointer\");\n$1 $2$3", dynlib: goLib.}
|
||||
|
||||
proc GC_getStatistics(): string =
|
||||
var mstats: goMStats
|
||||
goRuntime_ReadMemStats(addr mstats)
|
||||
var mstats = goMemStats()
|
||||
result = "[GC] total allocated memory: " & $(mstats.total_alloc) & "\n" &
|
||||
"[GC] total memory obtained from system: " & $(mstats.sys) & "\n" &
|
||||
"[GC] occupied memory: " & $(mstats.alloc) & "\n" &
|
||||
|
|
@ -282,107 +235,100 @@ elif defined(gogc):
|
|||
"[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)
|
||||
"[GC] number of completed GC cycles: " & $(mstats.numgc) & "\n" &
|
||||
"[GC] total GC pause time [ms]: " & $(mstats.pause_total_ns div 1000_000)
|
||||
|
||||
proc getOccupiedMem(): int =
|
||||
var mstats: goMStats
|
||||
goRuntime_ReadMemStats(addr mstats)
|
||||
var mstats = goMemStats()
|
||||
result = int(mstats.alloc)
|
||||
|
||||
proc getFreeMem(): int =
|
||||
var mstats: goMStats
|
||||
goRuntime_ReadMemStats(addr mstats)
|
||||
var mstats = goMemStats()
|
||||
result = int(mstats.sys - mstats.alloc)
|
||||
|
||||
proc getTotalMem(): int =
|
||||
var mstats: goMStats
|
||||
goRuntime_ReadMemStats(addr mstats)
|
||||
var mstats = goMemStats()
|
||||
result = int(mstats.sys)
|
||||
|
||||
proc nimGC_setStackBottom(theStackBottom: pointer) = discard
|
||||
|
||||
proc alloc(size: Natural): pointer =
|
||||
result = c_malloc(size)
|
||||
if result == nil: raiseOutOfMem()
|
||||
result = goMalloc(size.uint)
|
||||
|
||||
proc alloc0(size: Natural): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
result = goMalloc(size.uint)
|
||||
|
||||
proc realloc(p: pointer, newsize: Natural): pointer =
|
||||
result = c_realloc(p, newsize)
|
||||
if result == nil: raiseOutOfMem()
|
||||
raise newException(Exception, "not implemented")
|
||||
|
||||
proc dealloc(p: pointer) = c_free(p)
|
||||
proc dealloc(p: pointer) =
|
||||
discard
|
||||
|
||||
proc allocShared(size: Natural): pointer =
|
||||
result = c_malloc(size)
|
||||
if result == nil: raiseOutOfMem()
|
||||
result = alloc(size)
|
||||
|
||||
proc allocShared0(size: Natural): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
result = alloc0(size)
|
||||
|
||||
proc reallocShared(p: pointer, newsize: Natural): pointer =
|
||||
result = c_realloc(p, newsize)
|
||||
if result == nil: raiseOutOfMem()
|
||||
result = realloc(p, newsize)
|
||||
|
||||
proc deallocShared(p: pointer) = c_free(p)
|
||||
proc deallocShared(p: pointer) = dealloc(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 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)
|
||||
writebarrierptr(addr(result), goMalloc(size.uint))
|
||||
if typ.finalizer != nil:
|
||||
goSetFinalizer(result, typ.finalizer)
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
writebarrierptr(addr(result), newObj(typ, size))
|
||||
|
||||
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)
|
||||
writebarrierptr(addr(result), newObj(typ, size))
|
||||
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, len * typ.base.size + GenericSeqSize)
|
||||
writebarrierptr(addr(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 newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
writebarrierptr(addr(result), newSeq(typ, len))
|
||||
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, cap * typ.base.size + GenericSeqSize)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = cap
|
||||
cast[PGenericSeq](result).elemSize = typ.base.size
|
||||
|
||||
proc typedMemMove(dest: pointer, src: pointer, size: uint) {.importc: "typedmemmove", dynlib: goLib.}
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer =
|
||||
# the Go GC doesn't have a realloc
|
||||
var metadataOld = cast[PGenericSeq](old)
|
||||
if metadataOld.elemSize == 0:
|
||||
metadataOld.elemSize = 1
|
||||
let 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)
|
||||
writebarrierptr(addr(result), goMalloc(newsize.uint))
|
||||
typedMemMove(result, old, oldsize.uint)
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
|
||||
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
|
||||
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = discard
|
||||
proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = discard
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
|
||||
|
||||
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest[] = src
|
||||
writebarrierptr(dest, src)
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest[] = src
|
||||
writebarrierptr(dest, src)
|
||||
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||
dest[] = src
|
||||
writebarrierptr(dest, src)
|
||||
|
||||
type
|
||||
MemRegion = object
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue