diff options
Diffstat (limited to 'tests/gc')
-rw-r--r-- | tests/gc/closureleak.nim | 41 | ||||
-rw-r--r-- | tests/gc/cyclecollector.nim | 5 | ||||
-rw-r--r-- | tests/gc/cycleleak.nim | 2 | ||||
-rw-r--r-- | tests/gc/gcbench.nim | 18 | ||||
-rw-r--r-- | tests/gc/gcemscripten.nim | 2 | ||||
-rw-r--r-- | tests/gc/gcleak.nim | 9 | ||||
-rw-r--r-- | tests/gc/gcleak2.nim | 14 | ||||
-rw-r--r-- | tests/gc/gcleak3.nim | 8 | ||||
-rw-r--r-- | tests/gc/gcleak4.nim | 13 | ||||
-rw-r--r-- | tests/gc/gcleak5.nim | 2 | ||||
-rw-r--r-- | tests/gc/growobjcrash.nim | 9 | ||||
-rw-r--r-- | tests/gc/panicoverride.nim | 14 | ||||
-rw-r--r-- | tests/gc/stackrefleak.nim | 3 | ||||
-rw-r--r-- | tests/gc/tdisable_orc.nim | 9 | ||||
-rw-r--r-- | tests/gc/thavlak.nim | 321 | ||||
-rw-r--r-- | tests/gc/trace_globals.nim | 33 | ||||
-rw-r--r-- | tests/gc/tregionleak.nim | 23 | ||||
-rw-r--r-- | tests/gc/tstandalone.nim | 14 | ||||
-rw-r--r-- | tests/gc/weakrefs.nim | 9 |
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 |