summary refs log tree commit diff stats
path: root/tests/gc/thavlak.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/gc/thavlak.nim')
-rw-r--r--tests/gc/thavlak.nim346
1 files changed, 165 insertions, 181 deletions
diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim
index efab49e36..cfd860e25 100644
--- a/tests/gc/thavlak.nim
+++ b/tests/gc/thavlak.nim
@@ -1,51 +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 50 iterations...
-..................................................
-Found 1 loops (including artificial root node) (50)'''
+Another 3 iterations...
+...
+Found 1 loops (including artificial root node) (3)'''
 """
 
 # bug #3184
 
-import tables
-import sequtils
-import sets
+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)
@@ -54,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
@@ -188,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.
@@ -233,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.
@@ -245,7 +215,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
   #     - the list of backedges (backPreds) or
   #     - the list of non-backedges (nonBackPreds)
   #
-  for w in 0 .. <size:
+  for w in 0 ..< size:
     header[w] = 0
     types[w]  = BB_NONHEADER
 
@@ -278,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
@@ -291,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
@@ -299,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:
@@ -332,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)
@@ -358,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
@@ -379,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)
@@ -410,48 +379,63 @@ 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:
-    var h = newHavlakLoopFinder(self.cfg, newLsg())
-    var res = h.findLoops
+  for i in 1..5000:
+    withScratchRegion:
+      var h = newHavlakLoopFinder(self.cfg, newLsg())
+      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 50 iterations..."
+  echo "Another 3 iterations..."
 
   var sum = 0
-  for i in 1..50:
-    write stdout, "."
-    flushFile(stdout)
-    var hlf = newHavlakLoopFinder(self.cfg, newLsg())
-    sum += hlf.findLoops
-    #echo getOccupiedMem()
+  for i in 1..3:
+    withScratchRegion:
+      write stdout, "."
+      flushFile(stdout)
+      var hlf = newHavlakLoopFinder(self.cfg, newLsg())
+      sum += hlf.findLoops
+      #echo getOccupiedMem()
   echo "\nFound ", loops, " loops (including artificial root node) (", sum, ")"
 
-var l = newLoopTesterApp()
-l.run
+  when false:
+    echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
+    echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
+
+proc main =
+  var l = newLoopTesterApp()
+  l.run
+
+let mem = getOccupiedMem()
+main()
+when defined(gcOrc):
+  GC_fullCollect()
+  doAssert getOccupiedMem() == mem