[FIR] Some control flow graph building fixes

- fix edges for `continue` and labeled return
- fix deadness for init block and properties
This commit is contained in:
Dmitriy Novozhilov
2019-08-29 15:24:28 +03:00
parent 9508fc60c9
commit a87891bec9
11 changed files with 196 additions and 33 deletions
@@ -349,7 +349,9 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF
// ----------------------------------- While Loop ----------------------------------- // ----------------------------------- While Loop -----------------------------------
override fun enterWhileLoop(loop: FirLoop) { override fun enterWhileLoop(loop: FirLoop) {
graphBuilder.enterWhileLoop(loop).passFlow() val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.passFlow()
loopConditionEnterNode.passFlow()
} }
override fun exitWhileLoopCondition(loop: FirLoop) { override fun exitWhileLoopCondition(loop: FirLoop) {
@@ -70,8 +70,11 @@ class NodeStorage<T : FirElement, N : CFGNode<T>>(
} }
} }
class SymbolBasedNodeStorage<T, N : CFGNode<T>> : Stack<N> where T : FirElement { class SymbolBasedNodeStorage<T, N : CFGNode<T>>(
private val stack: Stack<N> = stackOf() pushCallback: ((N) -> Unit)? = null,
popCallback: ((N) -> Unit)? = null
) : Stack<N> where T : FirElement {
private val stack: Stack<N> = StackImpl(pushCallback = pushCallback, popCallback = popCallback)
private val map: MutableMap<FirBasedSymbol<*>, N> = mutableMapOf() private val map: MutableMap<FirBasedSymbol<*>, N> = mutableMapOf()
override val size: Int get() = stack.size override val size: Int get() = stack.size
@@ -25,10 +25,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
private val exitNodes: Stack<CFGNode<*>> = stackOf() private val exitNodes: Stack<CFGNode<*>> = stackOf()
private val functionExitNodes: NodeStorage<FirFunction<*>, FunctionExitNode> = NodeStorage( private val functionExitNodes: SymbolBasedNodeStorage<FirFunction<*>, FunctionExitNode> = SymbolBasedNodeStorage()
pushCallback = { exitNodes.push(it) },
popCallback = { exitNodes.pop() }
)
private val whenExitNodes: NodeStorage<FirWhenExpression, WhenExitNode> = NodeStorage() private val whenExitNodes: NodeStorage<FirWhenExpression, WhenExitNode> = NodeStorage()
@@ -88,6 +85,9 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
} }
functionExitNodes.push(exitNode) functionExitNodes.push(exitNode)
if (!isInplace) {
exitNodes.push(exitNode)
}
levelCounter++ levelCounter++
return enterNode return enterNode
} }
@@ -96,12 +96,16 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
levelCounter-- levelCounter--
val exitNode = functionExitNodes.pop() val exitNode = functionExitNodes.pop()
val isInplace = function.isInplace() val isInplace = function.isInplace()
if (!isInplace) {
exitNodes.pop()
}
if (isInplace) { if (isInplace) {
addNewSimpleNode(exitNode) addNewSimpleNode(exitNode)
} else { } else {
addEdge(lastNodes.pop(), exitNode) addEdge(lastNodes.pop(), exitNode)
lexicalScopes.pop() lexicalScopes.pop()
} }
exitNode.markAsDeadIfNecessary()
val graph = if (!isInplace) { val graph = if (!isInplace) {
graphs.pop() graphs.pop()
} else { } else {
@@ -136,8 +140,10 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
} }
fun exitProperty(property: FirProperty): Pair<PropertyExitNode, ControlFlowGraph> { fun exitProperty(property: FirProperty): Pair<PropertyExitNode, ControlFlowGraph> {
val topLevelVariableExitNode = topLevelVariableExitNodes.pop() val topLevelVariableExitNode = topLevelVariableExitNodes.pop().also {
addNewSimpleNode(topLevelVariableExitNode) addNewSimpleNode(it)
it.markAsDeadIfNecessary()
}
levelCounter-- levelCounter--
lexicalScopes.pop() lexicalScopes.pop()
return topLevelVariableExitNode to graphs.pop() return topLevelVariableExitNode to graphs.pop()
@@ -158,7 +164,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitJump(jump: FirJump<*>): JumpNode { fun exitJump(jump: FirJump<*>): JumpNode {
val node = createJumpNode(jump) val node = createJumpNode(jump)
val nextNode = when (jump) { val nextNode = when (jump) {
is FirReturnExpression -> functionExitNodes[jump.target.labeledElement] is FirReturnExpression -> functionExitNodes[jump.target.labeledElement.symbol]
is FirContinueExpression -> loopEnterNodes[jump.target.labeledElement] is FirContinueExpression -> loopEnterNodes[jump.target.labeledElement]
is FirBreakExpression -> loopExitNodes[jump.target.labeledElement] is FirBreakExpression -> loopExitNodes[jump.target.labeledElement]
else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}")
@@ -213,17 +219,20 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
// ----------------------------------- While Loop ----------------------------------- // ----------------------------------- While Loop -----------------------------------
fun enterWhileLoop(loop: FirLoop): LoopConditionEnterNode { fun enterWhileLoop(loop: FirLoop): Pair<LoopEnterNode, LoopConditionEnterNode> {
addNewSimpleNode(createLoopEnterNode(loop)) val loopEnterNode = createLoopEnterNode(loop).also {
addNewSimpleNode(it)
loopEnterNodes.push(it)
}
loopExitNodes.push(createLoopExitNode(loop)) loopExitNodes.push(createLoopExitNode(loop))
levelCounter++ levelCounter++
val node = createLoopConditionEnterNode(loop.condition) val conditionEnterNode = createLoopConditionEnterNode(loop.condition).also {
addNewSimpleNode(it)
// put conditional node twice so we can refer it after exit from loop block
lastNodes.push(it)
}
levelCounter++ levelCounter++
addNewSimpleNode(node) return loopEnterNode to conditionEnterNode
// put conditional node twice so we can refer it after exit from loop block
lastNodes.push(node)
loopEnterNodes.push(node)
return node
} }
fun exitWhileLoopCondition(loop: FirLoop): LoopConditionExitNode { fun exitWhileLoopCondition(loop: FirLoop): LoopConditionExitNode {
@@ -509,6 +518,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
levelCounter-- levelCounter--
return initBlockExitNodes.pop().also { return initBlockExitNodes.pop().also {
addNewSimpleNode(it) addNewSimpleNode(it)
it.markAsDeadIfNecessary()
lexicalScopes.pop() lexicalScopes.pop()
} }
} }
+102
View File
@@ -197,4 +197,106 @@ subgraph cluster_test_4 {
88 -> {89}; 88 -> {89};
} }
subgraph cluster_test_5 {
90 [shape=box label="Enter function test_5" style="filled"];
91 [shape=box label="Enter block"];
92 [shape=box label="Enter while loop"];
93 [shape=box label="Enter loop condition"];
94 [shape=box label="Access variable R|<local>/b|"];
95 [shape=box label="Exit loop condition"];
96 [shape=box label="Enter loop block"];
97 [shape=box label="Enter block"];
98 [shape=box label="Enter when"];
99 [shape=box label="Enter when branch condition "];
100 [shape=box label="Access variable R|<local>/b|"];
101 [shape=box label="Exit when branch condition"];
102 [shape=box label="Enter block"];
103 [shape=box label="Jump: continue@@@[R|<local>/b|] "];
104 [shape=box label="Stub[DEAD]"];
105 [shape=box label="Exit block[DEAD]"];
106 [shape=box label="Exit when branch result[DEAD]"];
107 [shape=box label="Enter when branch condition else"];
108 [shape=box label="Exit when branch condition"];
109 [shape=box label="Enter block"];
110 [shape=box label="Exit block"];
111 [shape=box label="Exit when branch result"];
112 [shape=box label="Exit when"];
113 [shape=box label="Exit block"];
114 [shape=box label="Exit loop block"];
115 [shape=box label="Exit whileloop"];
116 [shape=box label="Exit block"];
117 [shape=box label="Exit function test_5" style="filled"];
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95};
95 -> {115 96};
96 -> {97};
97 -> {98};
98 -> {99};
99 -> {100};
100 -> {101};
101 -> {102 107};
102 -> {103};
103 -> {92};
103 -> {104} [style=dotted];
104 -> {105} [style=dotted];
105 -> {106} [style=dotted];
106 -> {112} [style=dotted];
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {112};
112 -> {113};
113 -> {114};
114 -> {93};
115 -> {116};
116 -> {117};
}
subgraph cluster_run {
118 [shape=box label="Enter function run" style="filled"];
119 [shape=box label="Enter block"];
120 [shape=box label="Function call: R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
121 [shape=box label="Exit block"];
122 [shape=box label="Exit function run" style="filled"];
118 -> {119};
119 -> {120};
120 -> {121};
121 -> {122};
}
subgraph cluster_test_6 {
123 [shape=box label="Enter function test_6" style="filled"];
124 [shape=box label="Enter block"];
125 [shape=box label="Enter function anonymousFunction"];
126 [shape=box label="Enter block"];
127 [shape=box label="Jump: ^@run Unit"];
128 [shape=box label="Stub[DEAD]"];
129 [shape=box label="Exit block[DEAD]"];
130 [shape=box label="Exit function anonymousFunction"];
131 [shape=box label="Function call: R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
^@run Unit
}
)"];
132 [shape=box label="Exit block"];
133 [shape=box label="Exit function test_6" style="filled"];
123 -> {124};
124 -> {125};
125 -> {126};
126 -> {127};
127 -> {130};
127 -> {128} [style=dotted];
128 -> {129} [style=dotted];
129 -> {130} [style=dotted];
130 -> {131};
131 -> {132};
132 -> {133};
}
} }
+18
View File
@@ -30,4 +30,22 @@ fun test_4(x: Int?) {
break break
} while (true) } while (true)
x.inc() x.inc()
}
fun test_5(b: Boolean) {
while (b) {
if (b) {
continue
}
}
}
fun run(block: () -> Unit) {
block()
}
fun test_6() {
run {
return@run
}
} }
+22
View File
@@ -39,3 +39,25 @@ FILE: jumps.kt
while(Boolean(true)) while(Boolean(true))
R|<local>/x|.R|kotlin/Int.inc|() R|<local>/x|.R|kotlin/Int.inc|()
} }
public final fun test_5(b: R|kotlin/Boolean|): R|kotlin/Unit| {
while(R|<local>/b|) {
when () {
R|<local>/b| -> {
continue@@@[R|<local>/b|]
}
else -> {
}
}
}
}
public final fun run(block: R|kotlin/Function0<kotlin/Unit>|): R|kotlin/Unit| {
R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()
}
public final fun test_6(): R|kotlin/Unit| {
R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
^@run Unit
}
)
}
@@ -30,12 +30,13 @@ subgraph cluster__getter_ {
11 [shape=box label="Jump: ^ Int(1)"]; 11 [shape=box label="Jump: ^ Int(1)"];
12 [shape=box label="Stub[DEAD]"]; 12 [shape=box label="Stub[DEAD]"];
13 [shape=box label="Exit block[DEAD]"]; 13 [shape=box label="Exit block[DEAD]"];
14 [shape=box label="Exit function getter[DEAD]" style="filled"]; 14 [shape=box label="Exit function getter" style="filled"];
8 -> {9}; 8 -> {9};
9 -> {10}; 9 -> {10};
10 -> {11}; 10 -> {11};
11 -> {14 12} [style=dotted]; 11 -> {14};
11 -> {12} [style=dotted];
12 -> {13} [style=dotted]; 12 -> {13} [style=dotted];
13 -> {14} [style=dotted]; 13 -> {14} [style=dotted];
} }
@@ -75,7 +76,7 @@ subgraph cluster_foo {
31 [shape=box label="Throw: throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()"]; 31 [shape=box label="Throw: throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()"];
32 [shape=box label="Stub[DEAD]"]; 32 [shape=box label="Stub[DEAD]"];
33 [shape=box label="Exit block[DEAD]"]; 33 [shape=box label="Exit block[DEAD]"];
34 [shape=box label="Exit function foo[DEAD]" style="filled"]; 34 [shape=box label="Exit function foo" style="filled"];
24 -> {25}; 24 -> {25};
25 -> {26}; 25 -> {26};
@@ -84,7 +85,8 @@ subgraph cluster_foo {
28 -> {29}; 28 -> {29};
29 -> {30}; 29 -> {30};
30 -> {31}; 30 -> {31};
31 -> {34 32} [style=dotted]; 31 -> {34};
31 -> {32} [style=dotted];
32 -> {33} [style=dotted]; 32 -> {33} [style=dotted];
33 -> {34} [style=dotted]; 33 -> {34} [style=dotted];
} }
@@ -103,7 +105,7 @@ subgraph cluster__getter_ {
40 [shape=box label="Throw: throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()"]; 40 [shape=box label="Throw: throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()"];
41 [shape=box label="Stub[DEAD]"]; 41 [shape=box label="Stub[DEAD]"];
42 [shape=box label="Exit block[DEAD]"]; 42 [shape=box label="Exit block[DEAD]"];
43 [shape=box label="Exit init block[DEAD]"]; 43 [shape=box label="Exit init block"];
44 [shape=box label="Enter function getter" style="filled"]; 44 [shape=box label="Enter function getter" style="filled"];
45 [shape=box label="Enter block"]; 45 [shape=box label="Enter block"];
46 [shape=box label="Exit block"]; 46 [shape=box label="Exit block"];
@@ -112,7 +114,8 @@ subgraph cluster__getter_ {
37 -> {38}; 37 -> {38};
38 -> {39}; 38 -> {39};
39 -> {40}; 39 -> {40};
40 -> {43 41} [style=dotted]; 40 -> {43};
40 -> {41} [style=dotted];
41 -> {42} [style=dotted]; 41 -> {42} [style=dotted];
42 -> {43} [style=dotted]; 42 -> {43} [style=dotted];
44 -> {45}; 44 -> {45};
@@ -135,7 +138,7 @@ subgraph cluster_val_x3 {
54 [shape=box label="Stub[DEAD]"]; 54 [shape=box label="Stub[DEAD]"];
55 [shape=box label="Const: Int(1)[DEAD]"]; 55 [shape=box label="Const: Int(1)[DEAD]"];
56 [shape=box label="Exit block[DEAD]"]; 56 [shape=box label="Exit block[DEAD]"];
57 [shape=box label="Exit init block[DEAD]"]; 57 [shape=box label="Exit init block"];
58 [shape=box label="Enter property" style="filled"]; 58 [shape=box label="Enter property" style="filled"];
59 [shape=box label="Enter function anonymousFunction"]; 59 [shape=box label="Enter function anonymousFunction"];
60 [shape=box label="Enter block"]; 60 [shape=box label="Enter block"];
@@ -165,12 +168,13 @@ subgraph cluster_val_x3 {
throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#() throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()
} }
)[DEAD]"]; )[DEAD]"];
67 [shape=box label="Exit property[DEAD]" style="filled"]; 67 [shape=box label="Exit property" style="filled"];
50 -> {51}; 50 -> {51};
51 -> {52}; 51 -> {52};
52 -> {53}; 52 -> {53};
53 -> {57 54} [style=dotted]; 53 -> {57};
53 -> {54} [style=dotted];
54 -> {55} [style=dotted]; 54 -> {55} [style=dotted];
55 -> {56} [style=dotted]; 55 -> {56} [style=dotted];
56 -> {57} [style=dotted]; 56 -> {57} [style=dotted];
@@ -178,7 +182,8 @@ subgraph cluster_val_x3 {
59 -> {60}; 59 -> {60};
60 -> {61}; 60 -> {61};
61 -> {62}; 61 -> {62};
62 -> {65 63} [style=dotted]; 62 -> {67};
62 -> {63} [style=dotted];
63 -> {64} [style=dotted]; 63 -> {64} [style=dotted];
64 -> {65} [style=dotted]; 64 -> {65} [style=dotted];
65 -> {66} [style=dotted]; 65 -> {66} [style=dotted];
@@ -218,6 +218,7 @@ subgraph cluster_test_3 {
92 -> {99} [style=dotted]; 92 -> {99} [style=dotted];
93 -> {109 94}; 93 -> {109 94};
94 -> {95}; 94 -> {95};
95 -> {43};
95 -> {96} [style=dotted]; 95 -> {96} [style=dotted];
96 -> {97} [style=dotted]; 96 -> {97} [style=dotted];
97 -> {98} [style=dotted]; 97 -> {98} [style=dotted];
+1 -1
View File
@@ -13,7 +13,7 @@ FILE: elvis.kt
^test_1 Unit ^test_1 Unit
} }
else -> { else -> {
R|<local>/<elvis>|! R|<local>/<elvis>|
} }
} }
-> { -> {
@@ -328,7 +328,7 @@ subgraph cluster_test_6 {
146 [shape=box label="Access variable R|/Q.data|"]; 146 [shape=box label="Access variable R|/Q.data|"];
147 [shape=box label="Access variable R|/MyData.s|"]; 147 [shape=box label="Access variable R|/MyData.s|"];
148 [shape=box label="Function call: R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|()"]; 148 [shape=box label="Function call: R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|()"];
149 [shape=box label="Variable declaration: lval <elvis>: R|kotlin/Int|"]; 149 [shape=box label="Variable declaration: lval <elvis>: R|kotlin/Int?|"];
150 [shape=box label="Enter when branch condition "]; 150 [shape=box label="Enter when branch condition "];
151 [shape=box label="Const: Null(null)"]; 151 [shape=box label="Const: Null(null)"];
152 [shape=box label="Operator =="]; 152 [shape=box label="Operator =="];
@@ -49,7 +49,7 @@ FILE: nullability.kt
^test_3 Unit ^test_3 Unit
} }
else -> { else -> {
R|<local>/<elvis>|! R|<local>/<elvis>|
} }
} }
@@ -79,12 +79,12 @@ FILE: nullability.kt
} }
public final fun test_6(q: R|Q?|): R|kotlin/Unit| { public final fun test_6(q: R|Q?|): R|kotlin/Unit| {
when (lval <elvis>: R|kotlin/Int| = R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|()) { when (lval <elvis>: R|kotlin/Int?| = R|<local>/q|?.R|/Q.data|?.R|/MyData.s|?.R|kotlin/Int.inc|()) {
==($subj$, Null(null)) -> { ==($subj$, Null(null)) -> {
^test_6 Unit ^test_6 Unit
} }
else -> { else -> {
R|<local>/<elvis>|! R|<local>/<elvis>|
} }
} }