summary refs log tree commit diff stats
path: root/tests/gc
diff options
context:
space:
mode:
Diffstat (limited to 'tests/gc')
-rw-r--r--tests/gc/closureleak.nim41
-rw-r--r--tests/gc/cyclecollector.nim5
-rw-r--r--tests/gc/cycleleak.nim2
-rw-r--r--tests/gc/gcbench.nim18
-rw-r--r--tests/gc/gcemscripten.nim2
-rw-r--r--tests/gc/gcleak.nim9
-rw-r--r--tests/gc/gcleak2.nim14
-rw-r--r--tests/gc/gcleak3.nim8
-rw-r--r--tests/gc/gcleak4.nim13
-rw-r--r--tests/gc/gcleak5.nim2
-rw-r--r--tests/gc/growobjcrash.nim9
-rw-r--r--tests/gc/panicoverride.nim14
-rw-r--r--tests/gc/stackrefleak.nim3
-rw-r--r--tests/gc/tdisable_orc.nim9
-rw-r--r--tests/gc/thavlak.nim321
-rw-r--r--tests/gc/trace_globals.nim33
-rw-r--r--tests/gc/tregionleak.nim23
-rw-r--r--tests/gc/tstandalone.nim14
-rw-r--r--tests/gc/weakrefs.nim9
19 files changed, 330 insertions, 219 deletions
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim
index 18d320bdf..e67beb513 100644
--- a/tests/gc/closureleak.nim
+++ b/tests/gc/closureleak.nim
@@ -1,30 +1,49 @@
 discard """
   outputsub: "true"
+  disabled: "32bit"
 """
 
-from strutils import join
-
 type
-  TFoo * = object
+  TFoo* = object
     id: int
-    fn: proc(){.closure.}
+    fn: proc() {.closure.}
 var foo_counter = 0
 var alive_foos = newseq[int](0)
 
-proc free*(some: ref TFoo) =
-  #echo "Tfoo #", some.id, " freed"
-  alive_foos.del alive_foos.find(some.id)
+when defined(gcDestructors):
+  proc `=destroy`(some: TFoo) =
+    alive_foos.del alive_foos.find(some.id)
+    # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe`
+    # the debugging info below came from `symPrototype` in the liftdestructors
+    # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv}
+    # var proc (){.closure, gcsafe.}, {tfHasGCedMem}
+    # it worked by accident with var T destructors because in the sempass2
+    #
+    # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar`
+    # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
+    #   localError(tracked.config, n.info, $n & " is not GC safe")
+    {.cast(gcsafe).}:
+      `=destroy`(some.fn)
+
+else:
+  proc free*(some: ref TFoo) =
+    #echo "Tfoo #", some.id, " freed"
+    alive_foos.del alive_foos.find(some.id)
+
 proc newFoo*(): ref TFoo =
-  new result, free
+  when defined(gcDestructors):
+    new result
+  else:
+    new result, free
 
   result.id = foo_counter
   alive_foos.add result.id
   inc foo_counter
 
-for i in 0 .. <10:
- discard newFoo()
+for i in 0 ..< 10:
+  discard newFoo()
 
-for i in 0 .. <10:
+for i in 0 ..< 10:
   let f = newFoo()
   f.fn = proc =
     echo f.id
diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim
index 7b47758f2..2d02a7a3c 100644
--- a/tests/gc/cyclecollector.nim
+++ b/tests/gc/cyclecollector.nim
@@ -9,7 +9,10 @@ type
 proc createCycle(leaf: string): Node =
   new result
   result.a = result
-  shallowCopy result.leaf, leaf
+  when defined(gcArc) or defined(gcOrc):
+    result.leaf = leaf
+  else:
+    shallowCopy result.leaf, leaf
 
 proc main =
   for i in 0 .. 100_000:
diff --git a/tests/gc/cycleleak.nim b/tests/gc/cycleleak.nim
index 9f5c30ebd..e355abc96 100644
--- a/tests/gc/cycleleak.nim
+++ b/tests/gc/cycleleak.nim
@@ -10,7 +10,7 @@ type
   PModule = ref Module
 
   Node = object
-    owner*: PModule
+    owner* {.cursor.}: PModule
     data*: array[0..200, char] # some fat to drain memory faster
     id: int
 
diff --git a/tests/gc/gcbench.nim b/tests/gc/gcbench.nim
index fc5d6864f..e29ea762d 100644
--- a/tests/gc/gcbench.nim
+++ b/tests/gc/gcbench.nim
@@ -44,7 +44,7 @@ discard """
 # - No way to check on memory use
 # - No cyclic data structures
 # - No attempt to measure variation with object size
-# - Results are sensitive to locking cost, but we dont
+# - Results are sensitive to locking cost, but we don't
 #   check for proper locking
 #
 
@@ -53,11 +53,11 @@ import
 
 type
   PNode = ref TNode
-  TNode {.final.} = object
+  TNode {.final, acyclic.} = object
     left, right: PNode
     i, j: int
 
-proc newNode(L, r: PNode): PNode =
+proc newNode(L, r: sink PNode): PNode =
   new(result)
   result.left = L
   result.right = r
@@ -97,8 +97,8 @@ proc makeTree(iDepth: int): PNode =
     return newNode(makeTree(iDepth-1), makeTree(iDepth-1))
 
 proc printDiagnostics() =
-  echo("Total memory available: " & $getTotalMem() & " bytes")
-  echo("Free memory: " & $getFreeMem() & " bytes")
+  echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
+  echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
 
 proc timeConstruction(depth: int) =
   var
@@ -166,9 +166,15 @@ proc main() =
 
   var elapsed = epochTime() - t
   printDiagnostics()
-  echo("Completed in " & $elapsed & "ms. Success!")
+  echo("Completed in " & $elapsed & "s. Success!")
+  when declared(getMaxMem):
+    echo "Max memory ", formatSize getMaxMem()
 
 when defined(GC_setMaxPause):
   GC_setMaxPause 2_000
 
+when defined(gcDestructors):
+  let mem = getOccupiedMem()
 main()
+when defined(gcDestructors):
+  doAssert getOccupiedMem() == mem
diff --git a/tests/gc/gcemscripten.nim b/tests/gc/gcemscripten.nim
index bbef13d98..cc12b230f 100644
--- a/tests/gc/gcemscripten.nim
+++ b/tests/gc/gcemscripten.nim
@@ -15,7 +15,7 @@ when defined(allow_print):
 else:
   const print = false
 
-proc myResult3*(i:int):X {.exportc.} =
+proc myResult3*(i:int): X {.exportc.} =
   if print: echo "3"
   new(result)
   if print: echo "3-2"
diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim
index 0b2e6e14d..0bf993968 100644
--- a/tests/gc/gcleak.nim
+++ b/tests/gc/gcleak.nim
@@ -12,7 +12,14 @@ type
 proc makeObj(): TTestObj =
   result.x = "Hello"
 
-for i in 1 .. 100_000:
+const numIter =
+  # see tests/gc/gcleak2.nim
+  when defined(boehmgc):
+    1_000
+  elif defined(gcMarkAndSweep): 10_000
+  else: 100_000
+
+for i in 1 .. numIter:
   when defined(gcMarkAndSweep) or defined(boehmgc):
     GC_fullcollect()
   var obj = makeObj()
diff --git a/tests/gc/gcleak2.nim b/tests/gc/gcleak2.nim
index fe1718aef..bc943dbe7 100644
--- a/tests/gc/gcleak2.nim
+++ b/tests/gc/gcleak2.nim
@@ -14,8 +14,20 @@ proc makeObj(): TTestObj =
   result.x = "Hello"
   result.s = @[1,2,3]
 
+const numIter =
+  when defined(boehmgc):
+    # super slow because GC_fullcollect() at each iteration; especially
+    # on OSX 10.15 where it takes ~170s
+    # `getOccupiedMem` should be constant after each iteration for i >= 3
+    1_000
+  elif defined(gcMarkAndSweep):
+    # likewise, somewhat slow, 1_000_000 would run for 8s
+    # and same remark as above
+    100_000
+  else: 1_000_000
+
 proc inProc() =
-  for i in 1 .. 1_000_000:
+  for i in 1 .. numIter:
     when defined(gcMarkAndSweep) or defined(boehmgc):
       GC_fullcollect()
     var obj: TTestObj
diff --git a/tests/gc/gcleak3.nim b/tests/gc/gcleak3.nim
index 588e238e9..5e146d69f 100644
--- a/tests/gc/gcleak3.nim
+++ b/tests/gc/gcleak3.nim
@@ -17,14 +17,10 @@ for i in 0..1024:
   s.add(obj)
 
 proc limit*[t](a: var seq[t]) =
-  var loop = s.len() - 512
-  for i in 0..loop:
-    #echo i
-    #GC_fullCollect()
+  while s.len > 0:
     if getOccupiedMem() > 3000_000: quit("still a leak!")
-    s.delete(i)
+    s.delete(0)
 
 s.limit()
-
 echo "no leak: ", getOccupiedMem()
 
diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim
index e9b17e557..a72db67b7 100644
--- a/tests/gc/gcleak4.nim
+++ b/tests/gc/gcleak4.nim
@@ -2,11 +2,8 @@ discard """
   outputsub: "no leak: "
 """
 
-when defined(GC_setMaxPause):
-  GC_setMaxPause 2_000
-
 type
-  TExpr = object {.inheritable.} ## abstract base class for an expression
+  TExpr {.inheritable.} = object ## abstract base class for an expression
   PLiteral = ref TLiteral
   TLiteral = object of TExpr
     x: int
@@ -15,7 +12,7 @@ type
     a, b: ref TExpr
     op2: string
 
-method eval(e: ref TExpr): int =
+method eval(e: ref TExpr): int {.base.} =
   # override this base method
   quit "to override!"
 
@@ -27,20 +24,18 @@ method eval(e: ref TPlusExpr): int =
 
 proc newLit(x: int): ref TLiteral =
   new(result)
-  {.watchpoint: result.}
   result.x = x
   result.op1 = $getOccupiedMem()
 
-proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+proc newPlus(a, b: sink(ref TExpr)): ref TPlusExpr =
   new(result)
-  {.watchpoint: result.}
   result.a = a
   result.b = b
   result.op2 = $getOccupiedMem()
 
 const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
 
-for i in 0..100_000:
+for i in 0..50_000:
   var s: array[0..11, ref TExpr]
   for j in 0..high(s):
     s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim
index 6ab50e19e..f1913831b 100644
--- a/tests/gc/gcleak5.nim
+++ b/tests/gc/gcleak5.nim
@@ -9,7 +9,7 @@ proc main =
   for ii in 0..50_000:
     #while true:
     var t = getTime()
-    var g = t.getGMTime()
+    var g = t.utc()
     #echo isOnStack(addr g)
 
     if i mod 100 == 0:
diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim
index 07f92b8f4..ff1aa7e98 100644
--- a/tests/gc/growobjcrash.nim
+++ b/tests/gc/growobjcrash.nim
@@ -1,8 +1,4 @@
-discard """
-  output: "works"
-"""
-
-import cgi, strtabs
+import std/[cgi, strtabs]
 
 proc handleRequest(query: string): StringTableRef =
   iterator foo(): StringTableRef {.closure.} =
@@ -18,7 +14,7 @@ const Limit = 5*1024*1024
 
 proc main =
   var counter = 0
-  for i in 0 .. 100_000:
+  for i in 0 .. 10_000:
     for k, v in handleRequest("nick=Elina2&type=activate"):
       inc counter
       if counter mod 100 == 0:
@@ -26,4 +22,3 @@ proc main =
           quit "but now a leak"
 
 main()
-echo "works"
diff --git a/tests/gc/panicoverride.nim b/tests/gc/panicoverride.nim
new file mode 100644
index 000000000..0f28b0b72
--- /dev/null
+++ b/tests/gc/panicoverride.nim
@@ -0,0 +1,14 @@
+
+proc printf(frmt: cstring) {.varargs, importc, header: "<stdio.h>", cdecl.}
+proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: string) =
+  printf("%s\n", s)
+
+proc panic(s: string) {.noreturn.} =
+  rawoutput(s)
+  exit(1)
+
+{.pop.}
\ No newline at end of file
diff --git a/tests/gc/stackrefleak.nim b/tests/gc/stackrefleak.nim
index 302ef3599..7f3fbff43 100644
--- a/tests/gc/stackrefleak.nim
+++ b/tests/gc/stackrefleak.nim
@@ -12,7 +12,8 @@ type
 proc makePair: PCyclic =
   new(result)
   new(result.sibling)
-  result.sibling.sibling = result
+  when not defined(gcDestructors):
+    result.sibling.sibling = result
 
 proc loop =
   for i in 0..10000:
diff --git a/tests/gc/tdisable_orc.nim b/tests/gc/tdisable_orc.nim
new file mode 100644
index 000000000..b5f161c79
--- /dev/null
+++ b/tests/gc/tdisable_orc.nim
@@ -0,0 +1,9 @@
+discard """
+  joinable: false
+"""
+
+import std/asyncdispatch
+
+# bug #22256
+GC_disableMarkAndSweep()
+waitFor sleepAsync(1000)
diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim
index 2d8df7c1a..cfd860e25 100644
--- a/tests/gc/thavlak.nim
+++ b/tests/gc/thavlak.nim
@@ -1,52 +1,55 @@
 discard """
   output: '''Welcome to LoopTesterApp, Nim edition
 Constructing Simple CFG...
-15000 dummy loops
+5000 dummy loops
 Constructing CFG...
 Performing Loop Recognition
 1 Iteration
-Another 5 iterations...
-.....
-Found 1 loops (including artificial root node) (5)'''
+Another 3 iterations...
+...
+Found 1 loops (including artificial root node) (3)'''
 """
 
 # bug #3184
 
-import tables, sequtils, sets, strutils
+import tables, sets
 
 when not declared(withScratchRegion):
   template withScratchRegion(body: untyped) = body
 
 type
-  BasicBlock = object
-    inEdges: seq[ref BasicBlock]
-    outEdges: seq[ref BasicBlock]
+  BasicBlock = ref object
+    inEdges: seq[BasicBlock]
+    outEdges: seq[BasicBlock]
     name: int
 
-proc newBasicBlock(name: int): ref BasicBlock =
-  new(result)
-  result.inEdges = newSeq[ref BasicBlock]()
-  result.outEdges = newSeq[ref BasicBlock]()
-  result.name = name
+proc newBasicBlock(name: int): BasicBlock =
+  result = BasicBlock(
+    inEdges: newSeq[BasicBlock](),
+    outEdges: newSeq[BasicBlock](),
+    name: name
+  )
 
-proc hash(x: ref BasicBlock): int {.inline.} =
+proc hash(x: BasicBlock): int {.inline.} =
   result = x.name
 
 type
   BasicBlockEdge = object
-    fr: ref BasicBlock
-    to: ref BasicBlock
+    fr: BasicBlock
+    to: BasicBlock
 
   Cfg = object
-    basicBlockMap: Table[int, ref BasicBlock]
+    basicBlockMap: Table[int, BasicBlock]
     edgeList: seq[BasicBlockEdge]
-    startNode: ref BasicBlock
+    startNode: BasicBlock
 
 proc newCfg(): Cfg =
-  result.basicBlockMap = initTable[int, ref BasicBlock]()
-  result.edgeList = newSeq[BasicBlockEdge]()
+  result = Cfg(
+    basicBlockMap: initTable[int, BasicBlock](),
+    edgeList: newSeq[BasicBlockEdge](),
+    startNode: nil)
 
-proc createNode(self: var Cfg, name: int): ref BasicBlock =
+proc createNode(self: var Cfg, name: int): BasicBlock =
   result = self.basicBlockMap.getOrDefault(name)
   if result == nil:
     result = newBasicBlock(name)
@@ -55,128 +58,94 @@ proc createNode(self: var Cfg, name: int): ref BasicBlock =
   if self.startNode == nil:
     self.startNode = result
 
-proc addEdge(self: var Cfg, edge: BasicBlockEdge) =
-  self.edgeList.add(edge)
-
-proc getNumNodes(self: Cfg): int =
-  self.basicBlockMap.len
-
-proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int): BasicBlockEdge =
-  result.fr = cfg.createNode(fromName)
-  result.to = cfg.createNode(toName)
+proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
+  var result = BasicBlockEdge(
+    fr: cfg.createNode(fromName),
+    to: cfg.createNode(toName)
+  )
   result.fr.outEdges.add(result.to)
   result.to.inEdges.add(result.fr)
-  cfg.addEdge(result)
+  cfg.edgeList.add(result)
 
 type
-  SimpleLoop = object
-    basicBlocks: seq[ref BasicBlock] # TODO: set here
-    children: seq[ref SimpleLoop] # TODO: set here
-    parent: ref SimpleLoop
-    header: ref BasicBlock
-    isRoot: bool
-    isReducible: bool
-    counter: int
-    nestingLevel: int
-    depthLevel: int
-
-proc newSimpleLoop(): ref SimpleLoop =
-  new(result)
-  result.basicBlocks = newSeq[ref BasicBlock]()
-  result.children = newSeq[ref SimpleLoop]()
-  result.parent = nil
-  result.header = nil
-  result.isRoot = false
-  result.isReducible = true
-  result.counter = 0
-  result.nestingLevel = 0
-  result.depthLevel = 0
-
-proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) =
-  self.basicBlocks.add bb
-
-proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) =
-  self.children.add loop
-
-proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) =
+  SimpleLoop = ref object
+    basicBlocks: seq[BasicBlock] # TODO: set here
+    children: seq[SimpleLoop] # TODO: set here
+    parent: SimpleLoop
+    header: BasicBlock
+    isRoot, isReducible: bool
+    counter, nestingLevel, depthLevel: int
+
+proc setParent(self: SimpleLoop, parent: SimpleLoop) =
   self.parent = parent
-  self.parent.addChildLoop(self)
+  self.parent.children.add self
 
-proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) =
+proc setHeader(self: SimpleLoop, bb: BasicBlock) =
   self.basicBlocks.add(bb)
   self.header = bb
 
-proc setNestingLevel(self: ref SimpleLoop, level: int) =
+proc setNestingLevel(self: SimpleLoop, level: int) =
   self.nestingLevel = level
   if level == 0: self.isRoot = true
 
-var loop_counter: int = 0
+var loopCounter: int = 0
 
 type
   Lsg = object
-    loops: seq[ref SimpleLoop]
-    root: ref SimpleLoop
-
-proc createNewLoop(self: var Lsg): ref SimpleLoop =
-  result = newSimpleLoop()
-  loop_counter += 1
-  result.counter = loop_counter
-
-proc addLoop(self: var Lsg, l: ref SimpleLoop) =
+    loops: seq[SimpleLoop]
+    root: SimpleLoop
+
+proc createNewLoop(self: var Lsg): SimpleLoop =
+  result = SimpleLoop(
+    basicBlocks: newSeq[BasicBlock](),
+    children: newSeq[SimpleLoop](),
+    isReducible: true)
+  loopCounter += 1
+  result.counter = loopCounter
+
+proc addLoop(self: var Lsg, l: SimpleLoop) =
   self.loops.add l
 
 proc newLsg(): Lsg =
-  result.loops = newSeq[ref SimpleLoop]()
-  result.root = result.createNewLoop()
+  result = Lsg(loops: newSeq[SimpleLoop](),
+    root: result.createNewLoop())
   result.root.setNestingLevel(0)
   result.addLoop(result.root)
 
-proc getNumLoops(self: Lsg): int =
-  self.loops.len
-
 type
-  UnionFindNode = object
-    parent: ref UnionFindNode
-    bb: ref BasicBlock
-    l: ref SimpleLoop
+  UnionFindNode = ref object
+    parent {.cursor.}: UnionFindNode
+    bb: BasicBlock
+    l: SimpleLoop
     dfsNumber: int
 
-proc newUnionFindNode(): ref UnionFindNode =
-  new(result)
-  when false:
-    result.parent = nil
-    result.bb = nil
-    result.l = nil
-    result.dfsNumber = 0
-
-proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) =
+proc initNode(self: UnionFindNode, bb: BasicBlock, dfsNumber: int) =
   self.parent = self
   self.bb = bb
   self.dfsNumber = dfsNumber
 
-proc findSet(self: ref UnionFindNode): ref UnionFindNode =
-  var nodeList = newSeq[ref UnionFindNode]()
-  result = self
+proc findSet(self: UnionFindNode): UnionFindNode =
+  var nodeList = newSeq[UnionFindNode]()
+  var it {.cursor.} = self
 
-  while result != result.parent:
-    var parent = result.parent
-    if parent != parent.parent: nodeList.add result
-    result = parent
+  while it != it.parent:
+    var parent {.cursor.} = it.parent
+    if parent != parent.parent: nodeList.add it
+    it = parent
 
-  for iter in nodeList: iter.parent = result.parent
+  for iter in nodeList: iter.parent = it.parent
+  result = it
 
-proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) =
+proc union(self: UnionFindNode, unionFindNode: UnionFindNode) =
   self.parent = unionFindNode
 
 
 const
-  BB_TOP          = 0 # uninitialized
-  BB_NONHEADER    = 1 # a regular BB
-  BB_REDUCIBLE    = 2 # reducible loop
-  BB_SELF         = 3 # single BB loop
-  BB_IRREDUCIBLE  = 4 # irreducible loop
-  BB_DEAD         = 5 # a dead BB
-  BB_LAST         = 6 # Sentinel
+  BB_NONHEADER = 1 # a regular BB
+  BB_REDUCIBLE = 2 # reducible loop
+  BB_SELF = 3 # single BB loop
+  BB_IRREDUCIBLE = 4 # irreducible loop
+  BB_DEAD = 5 # a dead BB
 
   # # Marker for uninitialized nodes.
   UNVISITED = -1
@@ -189,44 +158,44 @@ type
     cfg: Cfg
     lsg: Lsg
 
-proc newHavlakLoopFinder(cfg: Cfg, lsg: Lsg): HavlakLoopFinder =
-  result.cfg = cfg
-  result.lsg = lsg
+proc newHavlakLoopFinder(cfg: Cfg, lsg: sink Lsg): HavlakLoopFinder =
+  result = HavlakLoopFinder(cfg: cfg, lsg: lsg)
 
-proc isAncestor(w: int, v: int, last: seq[int]): bool =
+proc isAncestor(w, v: int, last: seq[int]): bool =
   w <= v and v <= last[w]
 
-proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int =
+proc dfs(currentNode: BasicBlock, nodes: var seq[UnionFindNode],
+         number: var Table[BasicBlock, int],
+         last: var seq[int], current: int) =
   var stack = @[(currentNode, current)]
   while stack.len > 0:
     let (currentNode, current) = stack.pop()
     nodes[current].initNode(currentNode, current)
     number[currentNode] = current
 
-    result = current
     for target in currentNode.outEdges:
       if number[target] == UNVISITED:
-        stack.add((target, result+1))
+        stack.add((target, current+1))
         #result = dfs(target, nodes, number, last, result + 1)
-  last[number[currentNode]] = result
+  last[number[currentNode]] = current
 
 proc findLoops(self: var HavlakLoopFinder): int =
   var startNode = self.cfg.startNode
   if startNode == nil: return 0
-  var size = self.cfg.getNumNodes
+  var size = self.cfg.basicBlockMap.len
 
-  var nonBackPreds    = newSeq[HashSet[int]]()
-  var backPreds       = newSeq[seq[int]]()
-  var number          = initTable[ref BasicBlock, int]()
-  var header          = newSeq[int](size)
-  var types           = newSeq[int](size)
-  var last            = newSeq[int](size)
-  var nodes           = newSeq[ref UnionFindNode]()
+  var nonBackPreds = newSeq[HashSet[int]]()
+  var backPreds = newSeq[seq[int]]()
+  var number = initTable[BasicBlock, int]()
+  var header = newSeq[int](size)
+  var types = newSeq[int](size)
+  var last = newSeq[int](size)
+  var nodes = newSeq[UnionFindNode]()
 
   for i in 1..size:
-    nonBackPreds.add initSet[int](1)
+    nonBackPreds.add initHashSet[int](1)
     backPreds.add newSeq[int]()
-    nodes.add newUnionFindNode()
+    nodes.add(UnionFindNode())
 
   # Step a:
   #   - initialize all nodes as unvisited.
@@ -234,7 +203,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
   #   - unreached BB's are marked as dead.
   #
   for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED
-  var res = dfs(startNode, nodes, number, last, 0)
+  dfs(startNode, nodes, number, last, 0)
 
   # Step b:
   #   - iterate over all nodes.
@@ -279,7 +248,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
 
   for w in countdown(size - 1, 0):
     # this is 'P' in Havlak's paper
-    var nodePool = newSeq[ref UnionFindNode]()
+    var nodePool = newSeq[UnionFindNode]()
 
     var nodeW = nodes[w].bb
     if nodeW != nil: # dead BB
@@ -292,7 +261,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
 
       # Copy nodePool to workList.
       #
-      var workList = newSeq[ref UnionFindNode]()
+      var workList = newSeq[UnionFindNode]()
       for x in nodePool: workList.add x
 
       if nodePool.len != 0: types[w] = BB_REDUCIBLE
@@ -300,7 +269,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
       # work the list...
       #
       while workList.len > 0:
-        var x = workList[0]
+        let x = workList[0]
         workList.del(0)
 
         # Step e:
@@ -333,7 +302,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
       # Collapse/Unionize nodes in a SCC to a single node
       # For every SCC found, create a loop descriptor and link it in.
       #
-      if (nodePool.len > 0) or (types[w] == BB_SELF):
+      if nodePool.len > 0 or types[w] == BB_SELF:
         var l = self.lsg.createNewLoop
 
         l.setHeader(nodeW)
@@ -359,15 +328,15 @@ proc findLoops(self: var HavlakLoopFinder): int =
           node.union(nodes[w])
 
           # Nested loops are not added, but linked together.
-          var node_l = node.l
-          if node_l != nil:
-            node_l.setParent(l)
+          var nodeL = node.l
+          if nodeL != nil:
+            nodeL.setParent(l)
           else:
-            l.addNode(node.bb)
+            l.basicBlocks.add node.bb
 
         self.lsg.addLoop(l)
 
-  result = self.lsg.getNumLoops
+  result = self.lsg.loops.len
 
 
 type
@@ -380,27 +349,26 @@ proc newLoopTesterApp(): LoopTesterApp =
   result.lsg = newLsg()
 
 proc buildDiamond(self: var LoopTesterApp, start: int): int =
-  var bb0 = start
-  var x1 = newBasicBlockEdge(self.cfg, bb0, bb0 + 1)
-  var x2 = newBasicBlockEdge(self.cfg, bb0, bb0 + 2)
-  var x3 = newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3)
-  var x4 = newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3)
-  result = bb0 + 3
+  newBasicBlockEdge(self.cfg, start, start + 1)
+  newBasicBlockEdge(self.cfg, start, start + 2)
+  newBasicBlockEdge(self.cfg, start + 1, start + 3)
+  newBasicBlockEdge(self.cfg, start + 2, start + 3)
+  result = start + 3
 
-proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) =
-  var x1 = newBasicBlockEdge(self.cfg, start1, end1)
+proc buildConnect(self: var LoopTesterApp, start1, end1: int) =
+  newBasicBlockEdge(self.cfg, start1, end1)
 
-proc buildStraight(self: var LoopTesterApp, start: int, n: int): int =
+proc buildStraight(self: var LoopTesterApp, start, n: int): int =
   for i in 0..n-1:
     self.buildConnect(start + i, start + i + 1)
   result = start + n
 
 proc buildBaseLoop(self: var LoopTesterApp, from1: int): int =
-  var header   = self.buildStraight(from1, 1)
-  var diamond1 = self.buildDiamond(header)
-  var d11      = self.buildStraight(diamond1, 1)
-  var diamond2 = self.buildDiamond(d11)
-  var footer   = self.buildStraight(diamond2, 1)
+  let header = self.buildStraight(from1, 1)
+  let diamond1 = self.buildDiamond(header)
+  let d11 = self.buildStraight(diamond1, 1)
+  let diamond2 = self.buildDiamond(d11)
+  let footer = self.buildStraight(diamond2, 1)
 
   self.buildConnect(diamond2, d11)
   self.buildConnect(diamond1, header)
@@ -411,43 +379,45 @@ proc run(self: var LoopTesterApp) =
   echo "Welcome to LoopTesterApp, Nim edition"
   echo "Constructing Simple CFG..."
 
-  var x1 = self.cfg.createNode(0)
-  var x2 = self.buildBaseLoop(0)
-  var x3 = self.cfg.createNode(1)
+  discard self.cfg.createNode(0)
+  discard self.buildBaseLoop(0)
+  discard self.cfg.createNode(1)
   self.buildConnect(0, 2)
 
-  echo "15000 dummy loops"
+  echo "5000 dummy loops"
 
-  for i in 1..15000:
+  for i in 1..5000:
     withScratchRegion:
       var h = newHavlakLoopFinder(self.cfg, newLsg())
-      var res = h.findLoops
+      discard h.findLoops
 
   echo "Constructing CFG..."
   var n = 2
 
-  for parlooptrees in 1..10:
-    var x6 = self.cfg.createNode(n + 1)
-    self.buildConnect(2, n + 1)
-    n += 1
-    for i in 1..100:
-      var top = n
-      n = self.buildStraight(n, 1)
-      for j in 1..25: n = self.buildBaseLoop(n)
-      var bottom = self.buildStraight(n, 1)
-      self.buildConnect n, top
-      n = bottom
-    self.buildConnect(n, 1)
+  when true: # not defined(gcOrc):
+    # currently cycle detection is so slow that we disable this part
+    for parlooptrees in 1..10:
+      discard self.cfg.createNode(n + 1)
+      self.buildConnect(2, n + 1)
+      n += 1
+      for i in 1..100:
+        var top = n
+        n = self.buildStraight(n, 1)
+        for j in 1..25: n = self.buildBaseLoop(n)
+        var bottom = self.buildStraight(n, 1)
+        self.buildConnect n, top
+        n = bottom
+      self.buildConnect(n, 1)
 
   echo "Performing Loop Recognition\n1 Iteration"
 
   var h = newHavlakLoopFinder(self.cfg, newLsg())
   var loops = h.findLoops
 
-  echo "Another 5 iterations..."
+  echo "Another 3 iterations..."
 
   var sum = 0
-  for i in 1..5:
+  for i in 1..3:
     withScratchRegion:
       write stdout, "."
       flushFile(stdout)
@@ -460,5 +430,12 @@ proc run(self: var LoopTesterApp) =
     echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
     echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
 
-var l = newLoopTesterApp()
-l.run
+proc main =
+  var l = newLoopTesterApp()
+  l.run
+
+let mem = getOccupiedMem()
+main()
+when defined(gcOrc):
+  GC_fullCollect()
+  doAssert getOccupiedMem() == mem
diff --git a/tests/gc/trace_globals.nim b/tests/gc/trace_globals.nim
new file mode 100644
index 000000000..f62a15692
--- /dev/null
+++ b/tests/gc/trace_globals.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+10000000
+10000000
+10000000'''
+"""
+
+# bug #17085
+
+#[
+refs https://github.com/nim-lang/Nim/issues/17085#issuecomment-786466595
+with --gc:boehm, this warning sometimes gets generated:
+Warning: Repeated allocation of very large block (appr. size 14880768):
+May lead to memory leak and poor performance.
+nim CI now runs this test with `testWithoutBoehm` to avoid running it with --gc:boehm.
+]#
+
+proc init(): string =
+  for a in 0..<10000000:
+    result.add 'c'
+
+proc f() =
+  var a {.global.} = init()
+  var b {.global.} = init()
+  var c {.global.} = init()
+
+  echo a.len
+    # `echo` intentional according to
+    # https://github.com/nim-lang/Nim/pull/17469/files/0c9e94cb6b9ebca9da7cb19a063fba7aa409748e#r600016573
+  echo b.len
+  echo c.len
+
+f()
diff --git a/tests/gc/tregionleak.nim b/tests/gc/tregionleak.nim
new file mode 100644
index 000000000..277cfc987
--- /dev/null
+++ b/tests/gc/tregionleak.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: '''nim c --gc:regions $file'''
+  output: '''
+finalized
+finalized
+'''
+"""
+
+proc finish(o: RootRef) =
+  echo "finalized"
+
+withScratchRegion:
+  var test: RootRef
+  new(test, finish)
+
+var
+  mr: MemRegion
+  test: RootRef
+
+withRegion(mr):
+  new(test, finish)
+
+deallocAll(mr)
diff --git a/tests/gc/tstandalone.nim b/tests/gc/tstandalone.nim
new file mode 100644
index 000000000..41dad9ba4
--- /dev/null
+++ b/tests/gc/tstandalone.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--os:standalone --gc:none"
+  exitcode: 1
+  output: "value out of range"
+"""
+
+type
+  rangeType = range[0..1]
+
+var
+  r: rangeType = 0
+  i = 2
+
+r = rangeType(i)
diff --git a/tests/gc/weakrefs.nim b/tests/gc/weakrefs.nim
index 0a6d4b873..81c048d74 100644
--- a/tests/gc/weakrefs.nim
+++ b/tests/gc/weakrefs.nim
@@ -19,8 +19,15 @@ var
 proc finalizer(x: StrongObject) =
   valid.excl(x.id)
 
+when defined(gcDestructors):
+  proc `=destroy`(x: var TMyObject) =
+    valid.excl(x.id)
+
 proc create: StrongObject =
-  new(result, finalizer)
+  when defined(gcDestructors):
+    new(result)
+  else:
+    new(result, finalizer)
   result.id = gid
   valid.incl(gid)
   inc gid