diff options
author | Ștefan Talpalaru <stefantalpalaru@yahoo.com> | 2018-10-11 22:15:17 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-10-11 22:15:17 +0200 |
commit | 10f5f6776799af43ae34501e7aa47fb7fe52dd20 (patch) | |
tree | 57b817a1d29c5e47c886cc0db14e5e4872a90bbf | |
parent | d48e964950d92a57c24770d40ef0f05ddfe51b7e (diff) | |
download | Nim-10f5f6776799af43ae34501e7aa47fb7fe52dd20.tar.gz |
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
-rw-r--r-- | compiler/ccgexprs.nim | 27 | ||||
-rw-r--r-- | compiler/cgen.nim | 8 | ||||
-rw-r--r-- | compiler/options.nim | 6 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 160 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 2 |
5 files changed, 84 insertions, 119 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 54e805a44..d73dac82f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -169,7 +169,7 @@ proc canMove(p: BProc, n: PNode): bool = # result = false proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.storage == OnStack or not usesWriteBarrier(p.config): + if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap @@ -265,7 +265,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = rdLoc(dest), rdLoc(src)) elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.storage == OnStack or not usesWriteBarrier(p.config): + if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)) @@ -302,7 +302,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode): genRefAssign(p, dest, src, flags) else: - if dest.storage == OnStack or not usesWriteBarrier(p.config): + if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: @@ -1193,13 +1193,19 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = let args = [getTypeDesc(p.module, typ), ti, sizeExpr] if a.storage == OnHeap and usesWriteBarrier(p.config): - # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) else: linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc) - b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args) - linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) + if p.config.selectedGC == gcGo: + # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to + # implement the write barrier + b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc) + else: + # use newObjRC1 as an optimization + b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args) + linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) else: b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) genAssignment(p, a, b, {}) # set the object type: @@ -1229,8 +1235,13 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = else: linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc) if not lenIsZero: - call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args) - linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) + if p.config.selectedGC == gcGo: + # we need the write barrier + call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, dest), call.rdLoc) + else: + call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args) + linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) else: if lenIsZero: call.r = rope"NIM_NIL" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 37b07d38d..86aa7fb45 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -702,8 +702,12 @@ proc closureSetup(p: BProc, prc: PSym) = #echo "created environment: ", env.id, " for ", prc.name.s assignLocalVar(p, ls) # generate cast assignment: - linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", - rdLoc(env.loc), getTypeDesc(p.module, env.typ)) + if p.config.selectedGC == gcGo: + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, ($2) ClE_0);$n", + addrLoc(p.config, env.loc), getTypeDesc(p.module, env.typ)) + else: + linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", + rdLoc(env.loc), getTypeDesc(p.module, env.typ)) proc containsResult(n: PNode): bool = if n.kind == nkSym and n.sym.kind == skResult: diff --git a/compiler/options.nim b/compiler/options.nim index b4d2bb64e..4b7d664f4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -104,8 +104,10 @@ type cmdJsonScript # compile a .json build file TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors, - gcRefc, gcV2 + gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcDestructors, + gcRefc, gcV2, gcGo + # gcRefc and the GCs that follow it use a write barrier, + # as far as usesWriteBarrier() is concerned IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index e7e14b948..9a7af0a28 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -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 - - proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl, - importc: "runtime_ReadMemStats", - codegenDecl: "$1 $2$3 __asm__ (\"runtime.ReadMemStats\");\n$1 $2$3", - dynlib: goLib.} + 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 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 diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 6438a0541..33cd4415f 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -122,6 +122,8 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + s+1)) result.reserved = s + when defined(gogc): + result.elemSize = 1 else: result = rawNewStringNoInit(src.len) result.len = src.len |