Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--gc:destructors now means Nim uses pure refcounting #12557

Merged
merged 3 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
if sizeExpr.isNil:
sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]

if optOwnedRefs in p.config.globalOptions:
if optTinyRtti in p.config.globalOptions:
b.r = ropecg(p.module, "($1) #nimNewObj($2)",
[getTypeDesc(p.module, typ), sizeExpr])
genAssignment(p, a, b, {})
Expand Down
2 changes: 1 addition & 1 deletion compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const
# above X strings a hash-switch for strings is generated

proc getTraverseProc(p: BProc, v: PSym): Rope =
if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and
optOwnedRefs notin p.config.globalOptions and
containsGarbageCollectedRef(v.loc.t):
# we register a specialized marked proc here; this has the advantage
Expand Down
21 changes: 17 additions & 4 deletions compiler/commands.nim
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
of "generational": result = false
of "destructors": result = conf.selectedGC == gcDestructors
of "hooks": result = conf.selectedGC == gcHooks
of "go": result = conf.selectedGC == gcGo
of "none": result = conf.selectedGC == gcNone
of "stack", "regions": result = conf.selectedGC == gcRegions
Expand Down Expand Up @@ -453,14 +454,25 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.selectedGC = gcDestructors
defineSymbol(conf.symbols, "gcdestructors")
incl conf.globalOptions, optSeqDestructors
incl conf.globalOptions, optTinyRtti
if pass in {passCmd2, passPP}:
defineSymbol(conf.symbols, "nimSeqsV2")
defineSymbol(conf.symbols, "nimV2")
of "hooks":
conf.selectedGC = gcHooks
defineSymbol(conf.symbols, "gchooks")
incl conf.globalOptions, optSeqDestructors
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
if pass in {passCmd2, passPP}:
defineSymbol(conf.symbols, "nimSeqsV2")
of "go":
conf.selectedGC = gcGo
defineSymbol(conf.symbols, "gogc")
of "none":
conf.selectedGC = gcNone
defineSymbol(conf.symbols, "nogc")
of "stack", "regions":
conf.selectedGC= gcRegions
conf.selectedGC = gcRegions
defineSymbol(conf.symbols, "gcregions")
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "warnings", "w":
Expand Down Expand Up @@ -509,7 +521,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
undefSymbol(conf.symbols, "useNimRtl")
of "oldnewlines":
case arg.normalize
of "","on":
of "", "on":
conf.oldNewlines = true
defineSymbol(conf.symbols, "nimOldNewlines")
of "off":
Expand Down Expand Up @@ -763,9 +775,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
incl(conf.globalOptions, optOwnedRefs)
incl(conf.globalOptions, optSeqDestructors)
defineSymbol(conf.symbols, "nimV2")
conf.selectedGC = gcDestructors
defineSymbol(conf.symbols, "gcdestructors")
conf.selectedGC = gcHooks
defineSymbol(conf.symbols, "gchooks")
defineSymbol(conf.symbols, "nimSeqsV2")
defineSymbol(conf.symbols, "nimOwnedEnabled")
of "seqsv2":
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
if pass in {passCmd2, passPP}:
Expand Down
3 changes: 2 additions & 1 deletion compiler/lambdalifting.nim
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
# This seems to be generally correct but since it's a bit risky it's disabled
# for now.
let fieldType = if isDefined(c.graph.config, "nimCycleBreaker"):
let fieldType = if isDefined(c.graph.config, "nimCycleBreaker") or
c.graph.config.selectedGC == gcDestructors:
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
else:
c.getEnvTypeForOwner(dep, info)
Expand Down
77 changes: 67 additions & 10 deletions compiler/liftdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,58 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDestructor:
body.add genBuiltin(c.g, mDestroy, "destroy", x)

proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.lastSon

if isFinal(elemType):
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
else:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)

let cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
cond.typ = getSysType(c.g, x.info, tyBool)

case c.kind
of attachedSink:
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedAsgn:
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedDestructor:
body.add genIf(c, cond, actions)
of attachedDeepCopy: assert(false, "cannot happen")

proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Closures are really like refs except they always use a virtual destructor
## and we need to do the refcounting only on the ref field which we call 'xenv':
let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)

var actions = newNodeI(nkStmtList, c.info)
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv)

let cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, xenv)
cond.typ = getSysType(c.g, x.info, tyBool)

case c.kind
of attachedSink:
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedAsgn:
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedDestructor:
body.add genIf(c, cond, actions)
of attachedDeepCopy: assert(false, "cannot happen")

proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedSink:
Expand All @@ -367,7 +419,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
body.add newAsgnStmt(x, y)
of attachedAsgn:
body.add genIf(c, y, callCodegenProc(c.g, "nimIncWeakRef", c.info, y))
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
body.add newAsgnStmt(x, y)
of attachedDestructor:
Expand Down Expand Up @@ -411,8 +463,8 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
call.sons[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
call.sons[1] = y
body.add newAsgnStmt(x, call)
elif optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options:
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options) or c.g.config.selectedGC == gcDestructors:
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
case c.kind
Expand All @@ -424,7 +476,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedAsgn:
let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
yy.typ = getSysType(c.g, c.info, tyPointer)
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncWeakRef", c.info, yy))
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
body.add newAsgnStmt(x, y)
of attachedDestructor:
Expand All @@ -439,7 +491,6 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.lastSon
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
case c.kind
Expand All @@ -457,14 +508,19 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyPtr, tyOpt, tyUncheckedArray:
defaultOp(c, t, body, x, y)
of tyRef:
if optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options:
if c.g.config.selectedGC == gcDestructors:
atomicRefOp(c, t, body, x, y)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options):
weakrefOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyProc:
if t.callConv == ccClosure:
closureOp(c, t, body, x, y)
if c.g.config.selectedGC == gcDestructors:
atomicClosureOp(c, t, body, x, y)
else:
closureOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyOwned:
Expand Down Expand Up @@ -532,7 +588,8 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyGenericInst, tyStatic, tyAlias, tySink:
fillBody(c, lastSon(t), body, x, y)

proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
kind: TTypeAttachedOp; info: TLineInfo): PSym =
assert typ.kind == tyDistinct
let baseType = typ[0]
if baseType.attachedOps[kind] == nil:
Expand Down Expand Up @@ -571,7 +628,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
typ.attachedOps[kind] = result

var tk: TTypeKind
if g.config.selectedGC == gcDestructors:
if g.config.selectedGC in {gcDestructors, gcHooks}:
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
else:
tk = tyNone # no special casing for strings and seqs
Expand Down
1 change: 1 addition & 0 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ type
TStringSeq* = seq[string]
TGCMode* = enum # the selected GC
gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcDestructors,
gcHooks,
gcRefc, gcV2, gcGo
# gcRefc and the GCs that follow it use a write barrier,
# as far as usesWriteBarrier() is concerned
Expand Down
22 changes: 18 additions & 4 deletions lib/core/runtime_v2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
else:
inc allocs

proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
when hasThreadSupport:
atomicDec head(p).rc
else:
dec head(p).rc

proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
when hasThreadSupport:
atomicInc head(p).rc
else:
Expand Down Expand Up @@ -106,11 +106,25 @@ proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
cstderr.rawWrite "has destructor!\n"
nimRawDispose(p)

proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
if p != nil:
when hasThreadSupport:
if atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) == 0:
result = true
else:
if atomicDec(head(p).rc) <= 0:
result = true
else:
if head(p).rc == 0:
result = true
else:
dec head(p).rc

proc isObj(obj: PNimType, subclass: cstring): bool {.compilerRtl, inl.} =
proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}

result = strstr(obj.name, subclass) != nil

proc chckObj(obj: PNimType, subclass: cstring) {.compilerproc.} =
proc chckObj(obj: PNimType, subclass: cstring) {.compilerRtl.} =
# checks if obj is of type subclass:
if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")
18 changes: 10 additions & 8 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1722,12 +1722,12 @@ template `isnot`*(x, y: untyped): untyped = not (x is y)
## assert 42 isnot float
## assert @[1, 2] isnot enum

when (defined(nimV2) and not defined(nimscript)) or defined(nimFixedOwned):
when (defined(nimOwnedEnabled) and not defined(nimscript)) or defined(nimFixedOwned):
type owned*[T]{.magic: "BuiltinType".} ## type constructor to mark a ref/ptr or a closure as `owned`.
else:
template owned*(t: typedesc): typedesc = t

when defined(nimV2) and not defined(nimscript):
when defined(nimOwnedEnabled) and not defined(nimscript):
proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.}
## Creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``.
Expand Down Expand Up @@ -3237,6 +3237,8 @@ proc `<`*[T: tuple](x, y: T): bool =


# ----------------- GC interface ---------------------------------------------
const
usesDestructors = defined(gcDestructors) or defined(gcHooks)

when not defined(nimscript) and hasAlloc:
type
Expand All @@ -3246,7 +3248,7 @@ when not defined(nimscript) and hasAlloc:
gcOptimizeTime, ## optimize for speed
gcOptimizeSpace ## optimize for memory footprint

when not defined(JS) and not defined(gcDestructors):
when not defined(JS) and not usesDestructors:
proc GC_disable*() {.rtl, inl, benign.}
## Disables the GC. If called `n` times, `n` calls to `GC_enable`
## are needed to reactivate the GC.
Expand Down Expand Up @@ -3602,7 +3604,7 @@ when not defined(JS): #and not defined(nimscript):
{.push stack_trace: off, profiler:off.}

when hasAlloc:
when not defined(gcRegions) and not defined(gcDestructors):
when not defined(gcRegions) and not usesDestructors:
proc initGC() {.gcsafe.}

proc initStackBottom() {.inline, compilerproc.} =
Expand All @@ -3620,7 +3622,7 @@ when not defined(JS): #and not defined(nimscript):
when declared(nimGC_setStackBottom):
nimGC_setStackBottom(locals)

when not defined(gcDestructors):
when not usesDestructors:
{.push profiler: off.}
var
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
Expand Down Expand Up @@ -3792,10 +3794,10 @@ when not defined(JS): #and not defined(nimscript):
when hasAlloc: include "system/strmantle"

when hasThreadSupport:
when hostOS != "standalone" and not defined(gcDestructors): include "system/channels"
when hostOS != "standalone" and not usesDestructors: include "system/channels"

when not defined(nimscript) and hasAlloc:
when not defined(gcDestructors):
when not usesDestructors:
include "system/assign"
when not defined(nimV2):
include "system/repr"
Expand Down Expand Up @@ -4396,7 +4398,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
discard

when hasAlloc and not defined(nimscript) and not defined(JS) and
not defined(gcDestructors):
not usesDestructors:
# XXX how to implement 'deepCopy' is an open problem.
proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
## Performs a deep copy of `y` and copies it into `x`.
Expand Down
4 changes: 2 additions & 2 deletions lib/system/hti.nim
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type
ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow
# version
TNimType {.compilerproc.} = object
when defined(gcDestructors):
when defined(gcHooks):
head*: pointer
size*: int
kind: TNimKind
Expand All @@ -103,7 +103,7 @@ type
instances: int # count the number of instances
sizes: int # sizes of all instances in bytes

when defined(gcDestructors):
when defined(gcHooks):
type
PNimType* = ptr TNimType
else:
Expand Down
4 changes: 2 additions & 2 deletions lib/system/mmdisp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,10 @@ else:
elif defined(gcRegions):
# XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here
include "system/gc_regions"
elif defined(nimV2) or defined(gcDestructors):
elif defined(nimV2) or usesDestructors:
var allocator {.rtlThreadVar.}: MemRegion
instantiateForRegion(allocator)
when defined(gcDestructors):
when defined(gcHooks):
include "system/gc_hooks"
elif defined(gcMarkAndSweep):
# XXX use 'compileOption' here
Expand Down
2 changes: 1 addition & 1 deletion lib/system/repr.nim
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ when not defined(useNimRtl):
cl: var ReprClosure) =
# we know that p is not nil here:
when declared(CellSet):
when defined(boehmGC) or defined(gogc) or defined(nogc) or defined(gcDestructors):
when defined(boehmGC) or defined(gogc) or defined(nogc) or usesDestructors:
var cell = cast[PCell](p)
else:
var cell = usrToCell(p)
Expand Down
2 changes: 1 addition & 1 deletion lib/system/threads.nim
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ else:
proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
when defined(boehmgc):
boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not defined(gcDestructors):
elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors:
var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall, gcsafe.} =
threadProcWrapDispatch[TArg]
# init the GC for refc/markandsweep
Expand Down