summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSaem Ghani <saemghani+github@gmail.com>2021-03-02 01:32:43 -0800
committerGitHub <noreply@github.com>2021-03-02 10:32:43 +0100
commitab780f66ef67ea0333278d75e4af064bcae71c57 (patch)
treee7f778e6a92d12803c7cb8c68e432d0eecdd045b
parent33833968c46dd3a5dc8c379abdb7021b0d304b7f (diff)
downloadNim-ab780f66ef67ea0333278d75e4af064bcae71c57.tar.gz
fixes #17198, DFA failure on large case stmts (#17210)
This alters the DFA control flow graph generation for case statments.
Gotos are now generated as a chained link, this ensures that evaluation
of variant branches collapses as early as possible, without hitting the
2k call limit.
-rw-r--r--compiler/dfa.nim11
-rw-r--r--tests/destructor/t17198.nim32
2 files changed, 40 insertions, 3 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 517d13831..c7a9d4694 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -455,6 +455,10 @@ proc genCase(c: var Con; n: PNode) =
   let isExhaustive = skipTypes(n[0].typ,
     abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
 
+  # we generate endings as a set of chained gotos, this is a bit awkward but it
+  # ensures when recursively traversing the CFG for various analysis, we don't
+  # artificially extended the life of each branch (for the purposes of DFA)
+  # beyond the minimum amount.
   var endings: seq[TPosition] = @[]
   c.gen(n[0])
   for i in 1..<n.len:
@@ -462,13 +466,14 @@ proc genCase(c: var Con; n: PNode) =
     if it.len == 1 or (i == n.len-1 and isExhaustive):
       # treat the last branch as 'else' if this is an exhaustive case statement.
       c.gen(it.lastSon)
+      if endings.len != 0:
+          c.patch(endings[^1])
     else:
       forkT(it.lastSon):
         c.gen(it.lastSon)
+        if endings.len != 0:
+          c.patch(endings[^1])
         endings.add c.gotoI(it.lastSon)
-  for i in countdown(endings.high, 0):
-    let endPos = endings[i]
-    c.patch(endPos)
 
 proc genBlock(c: var Con; n: PNode) =
   withBlock(n[0].sym):
diff --git a/tests/destructor/t17198.nim b/tests/destructor/t17198.nim
new file mode 100644
index 000000000..098db8245
--- /dev/null
+++ b/tests/destructor/t17198.nim
@@ -0,0 +1,32 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''
+other
+'''
+"""
+
+import std/macros
+
+macro bigCaseStmt(arg: untyped): untyped =
+  result = nnkCaseStmt.newTree(arg)
+
+  # try to change 2000 to a bigger value if it doesn't crash
+  for x in 0 ..< 2000:
+    result.add nnkOfBranch.newTree(newStrLitNode($x), newStrLitNode($x))
+
+  result.add nnkElse.newTree(newStrLitNode("other"))
+
+macro bigIfElseExpr(): untyped =
+  result = nnkIfExpr.newTree()
+
+  for x in 0 ..< 1000:
+    result.add nnkElifExpr.newTree(newLit(false), newStrLitNode($x))
+
+  result.add nnkElseExpr.newTree(newStrLitNode("other"))
+
+proc test(arg: string): string =
+  echo bigIfElseExpr()
+
+  result = bigCaseStmt(arg)
+
+discard test("test")
0b3a86b2fcaad08938b2feba6274'>^
94ad882e ^

9372c16c ^
f73f5500 ^
57628c0e ^
37735c1d ^
eb908763 ^




d783d42c ^
c437c6dd ^
eb908763 ^
d783d42c ^
4cc517e0 ^
93be389b ^
6528a089 ^
eb908763 ^
d783d42c ^
eb908763 ^

d783d42c ^
4cc517e0 ^
93be389b ^
6528a089 ^
eb908763 ^
b09d6b5d ^
c437c6dd ^
eb908763 ^
b09d6b5d ^
4cc517e0 ^
93be389b ^
6528a089 ^
eb908763 ^
d783d42c ^
9372c16c ^
eb908763 ^
d783d42c ^
4cc517e0 ^
93be389b ^
6528a089 ^
eb908763 ^









d783d42c ^
f73f5500 ^
ed0e64a9 ^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100