diff options
-rwxr-xr-x | compiler/ast.nim | 1 | ||||
-rw-r--r-- | compiler/ccgcalls.nim | 6 | ||||
-rwxr-xr-x | compiler/cgen.nim | 58 | ||||
-rw-r--r-- | compiler/cgendata.nim | 2 | ||||
-rwxr-xr-x | compiler/commands.nim | 2 | ||||
-rwxr-xr-x | compiler/extccomp.nim | 12 | ||||
-rwxr-xr-x | compiler/types.nim | 2 | ||||
-rwxr-xr-x | lib/nimbase.h | 8 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 43 | ||||
-rwxr-xr-x | lib/system/alloc.nim | 19 | ||||
-rwxr-xr-x | lib/system/gc.nim | 7 | ||||
-rwxr-xr-x | todo.txt | 9 |
12 files changed, 125 insertions, 44 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 419d57562..0b14a0c02 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -273,6 +273,7 @@ type const tyPureObject* = tyTuple + GcTypeKinds* = {tyRef, tySequence, tyString} type TTypeKinds* = set[TTypeKind] diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index b28765f8f..3ea539389 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -252,7 +252,8 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) = genNamedParamCall(p, e, d) else: genPrefixCall(p, nil, e, d) - if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) + when false: + if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = if ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and @@ -262,5 +263,6 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) - if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) + when false: + if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 16210c026..7dfc259ef 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -297,23 +297,36 @@ proc getTemp(p: BProc, t: PType, result: var TLoc) = initTemp(p, result) proc keepAlive(p: BProc, toKeepAlive: TLoc) = - if optRefcGC notin gGlobalOptions: return - var result: TLoc - inc(p.labels) - result.r = con("LOC", toRope(p.labels)) - appf(p.s[cpsLocals], "volatile $1 $2;$n", - [getTypeDesc(p.module, toKeepAlive.t), result.r]) - result.k = locTemp - result.a = -1 - result.t = toKeepAlive.t - result.s = OnStack - result.flags = {} - if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType: - appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)]) - else: - appcg(p, cpsStmts, - "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", - [addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)]) + when false: + # deactivated because of the huge slowdown this causes; GC will take care + # of interior pointers instead + if optRefcGC notin gGlobalOptions: return + var result: TLoc + var fid = toRope(p.gcFrameId) + result.r = con("GCFRAME.F", fid) + appf(p.gcFrameType, " $1 F$2;$n", + [getTypeDesc(p.module, toKeepAlive.t), fid]) + inc(p.gcFrameId) + result.k = locTemp + result.a = -1 + result.t = toKeepAlive.t + result.s = OnStack + result.flags = {} + + if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType: + appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)]) + else: + appcg(p, cpsStmts, + "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", + [addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)]) + +proc initGCFrame(p: BProc): PRope = + if p.gcFrameId > 0: result = ropef("struct {$1} GCFRAME;$n", p.gcFrameType) + +proc deinitGCFrame(p: BProc): PRope = + if p.gcFrameId > 0: + result = ropecg(p.module, + "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n") proc cstringLit(p: BProc, r: var PRope, s: string): PRope = if gCmd == cmdCompileToLLVM: @@ -370,6 +383,8 @@ proc assignLocalVar(p: BProc, s: PSym) = if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)) if sfRegister in s.flags: app(p.s[cpsLocals], " register") + elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: + app(p.s[cpsLocals], " GC_GUARD") if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): app(p.s[cpsLocals], " volatile") appf(p.s[cpsLocals], " $1;$n", [s.loc.r]) @@ -428,7 +443,7 @@ include "ccgexprs.nim", "ccgstmts.nim" proc libCandidates(s: string, dest: var TStringSeq) = var le = strutils.find(s, '(') var ri = strutils.find(s, ')', le+1) - if le >= 0 and ri > le: + if le >= 0 and ri > le: var prefix = substr(s, 0, le - 1) var suffix = substr(s, ri + 1) for middle in split(substr(s, le + 1, ri - 1), '|'): @@ -583,8 +598,9 @@ proc genProcAux(m: BModule, prc: PSym) = if sfPure in prc.flags: generatedProc = ropeff("$1 {$n$2$3$4}$n", "define $1 {$n$2$3$4}$n", [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]]) - else: + else: generatedProc = ropeff("$1 {$n", "define $1 {$n", [header]) + app(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: getFrameDecl(p) app(generatedProc, p.s[cpsLocals]) @@ -608,6 +624,7 @@ proc genProcAux(m: BModule, prc: PSym) = app(generatedProc, p.s[cpsInit]) app(generatedProc, p.s[cpsStmts]) if p.beforeRetNeeded: appf(generatedProc, "BeforeRet: $n;") + app(generatedProc, deinitGCFrame(p)) if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM): appf(generatedProc, @@ -816,6 +833,8 @@ proc genInitCode(m: BModule) = m.FrameDeclared = true getFrameDecl(m.initProc) + app(prc, initGCFrame(m.initProc)) + app(prc, genSectionStart(cpsLocals)) app(prc, m.initProc.s[cpsLocals]) app(prc, genSectionEnd(cpsLocals)) @@ -842,6 +861,7 @@ proc genInitCode(m: BModule) = if optStackTrace in m.initProc.options and not m.PreventStackTrace: app(prc, deinitFrame(m.initProc)) app(prc, genSectionEnd(cpsStmts)) + app(prc, deinitGCFrame(m.initProc)) appf(prc, "}$n$n") # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # that would lead to a *nesting* of merge sections which the merger does diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 54e1f5d1a..ecbec664e 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -72,6 +72,8 @@ type receiveClosure*: PType # closure record type that we get module*: BModule # used to prevent excessive parameter passing withinLoop*: int # > 0 if we are within a loop + gcFrameId*: natural # for the GC stack marking + gcFrameType*: PRope # the struct {} we put the GC markers into TTypeSeq* = seq[PType] TCGen = object of TPassContext # represents a C source file diff --git a/compiler/commands.nim b/compiler/commands.nim index f07361abe..14b943327 100755 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -207,7 +207,7 @@ proc addPathRec(dir: string, info: TLineInfo) = proc track(arg: string, info: TLineInfo) = var a = arg.split(',') - if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLMUN") + if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN") var line, column: int if parseUtils.parseInt(a[1], line) <= 0: LocalError(info, errInvalidNumber, a[1]) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index facb22432..6635c2748 100755 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -21,7 +21,8 @@ type hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasComputedGoto, # CC has computed goto (GNU C extension) hasCpp, # CC is/contains a C++ compiler - hasAssume # CC has __assume (Visual C extension) + hasAssume, # CC has __assume (Visual C extension) + hasGcGuard # CC supports GC_GUARD to keep stack roots TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -71,7 +72,7 @@ compiler gcc: debug: "", pic: "-fPIC", asmStmtFrmt: "asm($1);$n", - props: {hasSwitchRange, hasComputedGoto, hasCpp}) + props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard}) compiler gpp: result = gcc() @@ -79,11 +80,10 @@ compiler gpp: result.name = "gpp" result.compilerExe = "g++" result.linkerExe = "g++" - - result.debug.add " -g " # XXX: Why is this default for g++, but not for gcc? - result.buildDll = " -mdll" # XXX: Hmm, I'm keeping this from the previos version, - # but my gcc doesn't even have such an option (is this mingw?) + result.buildDll = " -mdll" + # XXX: Hmm, I'm keeping this from the previos version, + # but my gcc doesn't even have such an option (is this mingw?) compiler llvmGcc: result = gcc() diff --git a/compiler/types.nim b/compiler/types.nim index d3f2bd1b5..8bce4fc5f 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -277,7 +277,7 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = result = analyseObjectWithTypeFieldAux(t, marker) proc isGBCRef(t: PType): bool = - result = t.kind in {tyRef, tySequence, tyString} + result = t.kind in GcTypeKinds proc containsGarbageCollectedRef(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the diff --git a/lib/nimbase.h b/lib/nimbase.h index bc8c3c28c..e2afed8f9 100755 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -16,6 +16,7 @@ __GNUC__ __DMC__ __POCC__ __TINYC__ +__clang__ */ @@ -437,4 +438,11 @@ __declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) { # define unlikely(x) (x) #endif +#if defined(__GNUC__) || defined(__clang__) +static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } +# define GC_GUARD __attribute__ ((cleanup(GCGuard))) +#else +# define GC_GUARD +#endif + #endif diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 33bb5b5f0..6b6c3861f 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -699,7 +699,7 @@ proc find*(s, sub: string, start: int = 0): int {.noSideEffect, rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` starting at position `start`. Searching is ## case-sensitive. If `sub` is not in `s`, -1 is returned. - var a: TSkipTable + var a {.noinit.}: TSkipTable preprocessSub(sub, a) result = findAux(s, sub, start, a) @@ -742,7 +742,7 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} = proc replace*(s, sub: string, by = ""): string {.noSideEffect, rtl, extern: "nsuReplaceStr".} = ## Replaces `sub` in `s` by the string `by`. - var a: TSkipTable + var a {.noinit.}: TSkipTable result = "" preprocessSub(sub, a) var i = 0 @@ -989,7 +989,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, ## after the decimal point for Nimrod's ``biggestFloat`` type. const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e'] var - frmtstr: array[0..5, char] + frmtstr {.noinit.}: array[0..5, char] buf: array[0..80, char] frmtstr[0] = '%' frmtstr[1] = '#' @@ -1018,16 +1018,41 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault, ## after the decimal point for Nimrod's ``float`` type. result = formatBiggestFloat(f, format, precision) +proc formatSize*(bytes: biggestInt, decimalSep = '.'): string = + ## Rounds and formats `bytes`. Examples: + ## + ## .. code-block:: nimrod + ## + ## formatSize(1'i64 shl 31 + 300'i64) == "4GB" + ## formatSize(4096) == "4KB" + ## + template frmt(a, b, c: expr): expr = + let bs = $b + insertSep($a) & decimalSep & bs.substr(0, 2) & c + let gigabytes = bytes shr 30 + let megabytes = bytes shr 20 + let kilobytes = bytes shr 10 + if gigabytes != 0: + result = frmt(gigabytes, megabytes, "GB") + elif megabytes != 0: + result = frmt(megabytes, kilobytes, "MB") + elif kilobytes != 0: + result = frmt(kilobytes, bytes, "KB") + else: + result = insertSep($bytes) & "B" + {.pop.} when isMainModule: - assert align("abc", 4) == " abc" - assert align("a", 0) == "a" - assert align("1232", 6) == " 1232" + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" echo wordWrap(""" this is a long text -- muchlongerthan10chars and here it goes""", 10, false) - assert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" - assert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11" + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11" - assert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB" + echo formatSize(1'i64 shl 31) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 6dee145c8..f33d40b0a 100755 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -171,7 +171,7 @@ proc getMaxMem(a: var TMemRegion): int = # Since we update maxPagesCount only when freeing pages, # maxPagesCount may not be up to date. Thus we use the # maximum of these both values here: - return max(a.currMem, a.maxMem) + result = max(a.currMem, a.maxMem) proc llAlloc(a: var TMemRegion, size: int): pointer = # *low-level* alloc for the memory managers data structures. Deallocation @@ -550,6 +550,23 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = var c = cast[PBigChunk](c) result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1 +proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer = + if isAccessible(a, p): + var c = pageAddr(p) + if not chunkUnused(c): + if isSmallChunk(c): + var c = cast[PSmallChunk](c) + var offset = (cast[TAddress](p) and (PageSize-1)) -% + smallChunkOverhead() + if c.acc >% offset: + var d = cast[ptr TFreeCell](cast[TAddress](addr(c.data)) +% + offset -% (offset %% c.size)) + if d.zeroField >% 1: result = d + else: + var c = cast[PBigChunk](c) + var d = addr(c.data) + if p >= d and cast[ptr TFreeCell](d).zeroField >% 1: result = d + proc ptrSize(p: pointer): int = var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell)) result = pageAddr(x).size - sizeof(TFreeCell) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index caab22e34..f13015573 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -579,6 +579,13 @@ proc nimKeepAlive(p: PGenericSeq) {.compilerRtl, noinline.} = if isAllocatedPtr(gch.region, c): c.refcount = c.refcount or rcMarked +proc nimGCFrame(p: pointer) {.compilerRtl, noinline.} = + # 'cast' is correct here! no offset to add: + var c = cast[PCell](p) + var x = cast[TAddress](c) + if x <% PageSize and (x and (MemAlign-1)) == 0: + c.refcount = c.refcount or rcMarked + proc markThreadStacks(gch: var TGcHeap) = when hasThreadSupport and hasSharedHeap: {.error: "not fully implemented".} diff --git a/todo.txt b/todo.txt index 7b8aec385..35fa001d7 100755 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,9 @@ version 0.8.14 ============== -- compiler/GC interaction need to generate code to prevent tail call - optimization - -- warning for implicit openArray -> varargs convention +- GC should care about interior pointers on the stack +- BUG: type TX = TTable[string, int] +- warning for implicit openArray -> varargs conversion - implement explicit varargs; **but** ``len(varargs)`` problem remains! --> solve by implicit conversion from varargs to openarray - implicit invokation of `items`/`pairs` seems nice @@ -93,7 +92,7 @@ Library ------- - wrappers for poppler; libharu -- radix tree for strings; maybe suffix tree +- suffix trees - locale support - bignums |