summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-10-30 16:15:17 +0100
committerGitHub <noreply@github.com>2019-10-30 16:15:17 +0100
commit1746da2d9e3d802d990d9636d2f6887a4aeb5dc9 (patch)
tree687727e8d4b858eb74db2e07a1a778cff58a7bdd
parentb5bb58164270a819cfb37655139251955541a964 (diff)
downloadNim-1746da2d9e3d802d990d9636d2f6887a4aeb5dc9.tar.gz
--gc:destructors now means Nim uses pure refcounting (#12557)
-rw-r--r--compiler/ccgexprs.nim2
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/commands.nim21
-rw-r--r--compiler/lambdalifting.nim3
-rw-r--r--compiler/liftdestructors.nim77
-rw-r--r--compiler/options.nim1
-rw-r--r--lib/core/runtime_v2.nim22
-rw-r--r--lib/system.nim18
-rw-r--r--lib/system/hti.nim4
-rw-r--r--lib/system/mmdisp.nim4
-rw-r--r--lib/system/repr.nim2
-rw-r--r--lib/system/threads.nim2
12 files changed, 123 insertions, 35 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index cc4e8de42..f6cf799bd 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -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, {})
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index fcef4a0ff..a3d807e26 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -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
diff --git a/compiler/commands.nim b/compiler/commands.nim
index a3ab6e522..249312f50 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -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
@@ -453,6 +454,17 @@ 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")
@@ -460,7 +472,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
         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":
@@ -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":
@@ -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}:
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index e99c7bc12..4a2d40ec7 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -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)
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index ffdf3e16e..4b69a9e9a 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -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:
@@ -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:
@@ -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
@@ -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:
@@ -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
@@ -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:
@@ -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:
@@ -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
diff --git a/compiler/options.nim b/compiler/options.nim
index 286ca8fcf..d3918363e 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -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
diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim
index 5c9554ad8..fe4bf37bf 100644
--- a/lib/core/runtime_v2.nim
+++ b/lib/core/runtime_v2.nim
@@ -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:
@@ -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")
diff --git a/lib/system.nim b/lib/system.nim
index 54ced6c7c..1485e4f6a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -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``.
@@ -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
@@ -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.
@@ -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.} =
@@ -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})
@@ -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"
@@ -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`.
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index b2e90211d..c20f132fb 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -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
@@ -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:
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index de89acd33..330c551c5 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -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
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 0c7848c75..43bbc8bb6 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -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)
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index ad1d82be2..b09ed0c6f 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -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