[FIR] Add inlining of control flow graphs of in-place anonymous functions

This commit is contained in:
Dmitriy Novozhilov
2019-08-26 12:58:02 +03:00
parent e72d8e675a
commit fc43c8a74b
15 changed files with 520 additions and 19 deletions
@@ -24,7 +24,7 @@ class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() {
override fun enterFunction(function: FirFunction<*>) {}
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph = ControlFlowGraph(DUMMY)
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? = null
override fun enterBlock(block: FirBlock) {}
@@ -18,7 +18,7 @@ abstract class FirDataFlowAnalyzer {
// ----------------------------------- Named function -----------------------------------
abstract fun enterFunction(function: FirFunction<*>)
abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph
abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph?
// ----------------------------------- Property -----------------------------------
@@ -58,7 +58,7 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF
}
}
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph {
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function)
node.passFlow()
for (valueParameter in function.valueParameters) {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractPropertyAccessor
@@ -65,18 +66,48 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
is FirConstructor -> function.name.asString()
else -> throw IllegalArgumentException("Unknown function: ${function.render()}")
}
graphs.push(ControlFlowGraph(name))
functionExitNodes.push(createFunctionExitNode(function))
lexicalScopes.push(stackOf())
return createFunctionEnterNode(function).also { lastNodes.push(it) }.also { levelCounter++ }
val invocationKind = function.invocationKind
val isInplace = invocationKind.isInplace()
if (!isInplace) {
graphs.push(ControlFlowGraph(name))
}
val enterNode = createFunctionEnterNode(function, isInplace).also {
if (isInplace) {
addNewSimpleNode(it)
} else {
lexicalScopes.push(stackOf())
lastNodes.push(it)
}
}
val exitNode = createFunctionExitNode(function, isInplace)
@Suppress("NON_EXHAUSTIVE_WHEN")
when (invocationKind) {
InvocationKind.AT_LEAST_ONCE -> addEdge(exitNode, enterNode)
InvocationKind.AT_MOST_ONCE -> addEdge(enterNode, exitNode)
}
functionExitNodes.push(exitNode)
levelCounter++
return enterNode
}
fun exitFunction(function: FirFunction<*>): Pair<FunctionExitNode, ControlFlowGraph> {
fun exitFunction(function: FirFunction<*>): Pair<FunctionExitNode, ControlFlowGraph?> {
levelCounter--
val exitNode = functionExitNodes.pop()
addEdge(lastNodes.pop(), exitNode)
lexicalScopes.pop()
return exitNode to graphs.pop()
val isInplace = function.isInplace()
if (isInplace) {
addNewSimpleNode(exitNode)
} else {
addEdge(lastNodes.pop(), exitNode)
lexicalScopes.pop()
}
val graph = if (!isInplace) {
graphs.pop()
} else {
null
}
return exitNode to graph
}
// ----------------------------------- Block -----------------------------------
@@ -503,4 +534,16 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
from.followingNodes += to
to.previousNodes += from
}
private val FirFunction<*>.invocationKind: InvocationKind?
get() = (this as? FirAnonymousFunction)?.invocationKind
private fun InvocationKind?.isInplace(): Boolean {
return this != null && this != InvocationKind.UNKNOWN
}
private fun FirFunction<*>.isInplace(): Boolean {
val invocationKind = this.invocationKind
return invocationKind != null && invocationKind != InvocationKind.UNKNOWN
}
}
@@ -54,13 +54,18 @@ abstract class ControlFlowGraphNodeBuilder {
protected fun createPropertyEnterNode(fir: FirProperty): PropertyEnterNode = PropertyEnterNode(graph, fir, levelCounter)
protected fun createFunctionEnterNode(fir: FirFunction<*>): FunctionEnterNode =
protected fun createFunctionEnterNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionEnterNode =
FunctionEnterNode(graph, fir, levelCounter).also {
graph.enterNode = it
if (!isInPlace) {
graph.enterNode = it
}
}
protected fun createFunctionExitNode(fir: FirFunction<*>): FunctionExitNode = FunctionExitNode(graph, fir, levelCounter).also {
graph.exitNode = it
protected fun createFunctionExitNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionExitNode =
FunctionExitNode(graph, fir, levelCounter).also {
if (!isInPlace) {
graph.exitNode = it
}
}
protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode =
@@ -603,8 +603,9 @@ open class FirBodyResolveTransformer(
dataFlowAnalyzer.enterFunction(function)
super.transformFunction(function, data).also {
val result = it.single as FirFunction<*>
val controlFlowGraph = dataFlowAnalyzer.exitFunction(result)
result.transformControlFlowGraphReference(ControlFlowGraphReferenceTransformer, controlFlowGraph)
dataFlowAnalyzer.exitFunction(result)?.let { controlFlowGraph ->
result.transformControlFlowGraphReference(ControlFlowGraphReferenceTransformer, controlFlowGraph)
}
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ FILE: complex.kt
@R|kotlin/jvm/Throws|(<getClass>(<Unresolved name: IOException>#), <getClass>(<Unresolved name: ResponseParseException>#)) public final fun fetchPluginReleaseDate(pluginId: R|class error: Symbol not found, for `PluginId`|, version: R|kotlin/String|, channel: R|kotlin/String?|): R|class error: Symbol not found, for `LocalDate?`| {
lval url: R|kotlin/String| = <strcat>(String(https://plugins.jetbrains.com/api/plugins/), R|<local>/pluginId|.<Unresolved name: idString>#, String(/updates?version=), R|<local>/version|)
lval pluginDTOs: R|kotlin/Array<class error: Symbol not found, for `PluginDTO`>| = try {
<Unresolved name: HttpRequests>#.<Unresolved name: request>#(R|<local>/url|).<Unresolved name: connect>#(<L> = connect@fun <implicit>.<anonymous>(): <implicit> {
<Unresolved name: HttpRequests>#.<Unresolved name: request>#(R|<local>/url|).<Unresolved name: connect>#(<L> = connect@fun <implicit>.<anonymous>(): <implicit> <kind=EXACTLY_ONCE> {
GsonBuilder#().create#().fromJson#(it#.inputStream#.reader#(), <getClass>(Array#<R|class error: Symbol not found, for `PluginDTO`|>()).java#)
}
)
@@ -0,0 +1,127 @@
digraph lambdas_kt {
subgraph run {
0 [shape=box label="Enter function run"];
1 [shape=box label="Enter block"];
2 [shape=box label="Function call: R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
3 [shape=box label="Exit block"];
4 [shape=box label="Exit function run"];
0 -> {1};
1 -> {2};
2 -> {3};
3 -> {4};
}
subgraph test_1 {
5 [shape=box label="Enter function test_1"];
6 [shape=box label="Enter block"];
7 [shape=box label="Enter when"];
8 [shape=box label="Enter when branch condition "];
9 [shape=box label="Access variable R|<local>/x|"];
10 [shape=box label="Type operator: x is Int"];
11 [shape=box label="Exit when branch condition"];
12 [shape=box label="Enter block"];
13 [shape=box label="Enter function anonymousFunction"];
14 [shape=box label="Enter block"];
15 [shape=box label="Access variable R|<local>/x|"];
16 [shape=box label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
17 [shape=box label="Exit block"];
18 [shape=box label="Exit function anonymousFunction"];
19 [shape=box label="Function call: R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|kotlin/Int.inc|()
}
)"];
20 [shape=box label="Exit block"];
21 [shape=box label="Exit when branch result"];
22 [shape=box label="Enter when branch condition else"];
23 [shape=box label="Exit when branch condition"];
24 [shape=box label="Enter block"];
25 [shape=box label="Exit block"];
26 [shape=box label="Exit when branch result"];
27 [shape=box label="Exit when"];
28 [shape=box label="Exit block"];
29 [shape=box label="Exit function test_1"];
5 -> {6};
6 -> {7};
7 -> {8};
8 -> {9};
9 -> {10};
10 -> {11};
11 -> {12 22};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
21 -> {27};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
28 -> {29};
}
subgraph <anonymous> {
30 [shape=box label="Enter function anonymousFunction"];
31 [shape=box label="Enter block"];
32 [shape=box label="Access variable R|<local>/x|"];
33 [shape=box label="Function call: R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
34 [shape=box label="Exit block"];
35 [shape=box label="Exit function anonymousFunction"];
30 -> {31};
31 -> {32};
32 -> {33};
33 -> {34};
34 -> {35};
}
subgraph test_2 {
36 [shape=box label="Enter function test_2"];
37 [shape=box label="Enter block"];
38 [shape=box label="Enter when"];
39 [shape=box label="Enter when branch condition "];
40 [shape=box label="Access variable R|<local>/x|"];
41 [shape=box label="Type operator: x is Int"];
42 [shape=box label="Exit when branch condition"];
43 [shape=box label="Enter block"];
44 [shape=box label="Variable declaration: lval lambda: R|kotlin/Function0<class error: Ambiguity: inc, [kotlin/inc, kotlin/inc]>|"];
45 [shape=box label="Exit block"];
46 [shape=box label="Exit when branch result"];
47 [shape=box label="Enter when branch condition else"];
48 [shape=box label="Exit when branch condition"];
49 [shape=box label="Enter block"];
50 [shape=box label="Exit block"];
51 [shape=box label="Exit when branch result"];
52 [shape=box label="Exit when"];
53 [shape=box label="Exit block"];
54 [shape=box label="Exit function test_2"];
36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
41 -> {42};
42 -> {43 47};
43 -> {44};
44 -> {45};
45 -> {46};
46 -> {52};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54};
}
}
+19
View File
@@ -0,0 +1,19 @@
fun run(block: () -> Unit) {
block()
}
fun test_1(x: Any?) {
if (x is Int) {
run {
x.inc()
}
}
}
fun test_2(x: Any?) {
if (x is Int) {
val lambda = {
x.inc()
}
}
}
+30
View File
@@ -0,0 +1,30 @@
FILE: lambdas.kt
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_1(x: R|kotlin/Any?|): R|kotlin/Unit| {
when () {
(R|<local>/x| is R|kotlin/Int|) -> {
R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|kotlin/Int.inc|()
}
)
}
else -> {
}
}
}
public final fun test_2(x: R|kotlin/Any?|): R|kotlin/Unit| {
when () {
(R|<local>/x| is R|kotlin/Int|) -> {
lval lambda: R|kotlin/Function0<class error: Ambiguity: inc, [kotlin/inc, kotlin/inc]>| = fun <anonymous>(): <ERROR TYPE REF: Ambiguity: inc, [kotlin/inc, kotlin/inc]> {
R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
}
}
else -> {
}
}
}
@@ -11,7 +11,7 @@ FILE: propertiesAndInitBlocks.kt
public set(value: R|kotlin/Int|): R|kotlin/Unit| {
F|/x2| = Int(1)
}
public final val x3: R|kotlin/Unit| = R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| {
public final val x3: R|kotlin/Unit| = R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
local final fun foo(): R|kotlin/Unit| {
lval c: R|kotlin/Int| = Int(1).R|kotlin/Int.plus|(Int(1))
throw <Ambiguity: Exception, [java/lang/Exception.Exception, java/lang/Exception.Exception]>#()
@@ -0,0 +1,182 @@
digraph inPlaceLambdas_kt {
subgraph foo {
0 [shape=box label="Enter function foo"];
1 [shape=box label="Exit function foo"];
0 -> {1};
}
subgraph bar {
2 [shape=box label="Enter function bar"];
3 [shape=box label="Exit function bar"];
2 -> {3};
}
subgraph run {
4 [shape=box label="Enter function run"];
5 [shape=box label="Enter block"];
6 [shape=box label="Function call: R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
7 [shape=box label="Exit block"];
8 [shape=box label="Exit function run"];
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8};
}
subgraph test_1 {
9 [shape=box label="Enter function test_1"];
10 [shape=box label="Enter block"];
11 [shape=box label="Enter when"];
12 [shape=box label="Enter when branch condition "];
13 [shape=box label="Access variable R|<local>/x|"];
14 [shape=box label="Type operator: x is A"];
15 [shape=box label="Exit when branch condition"];
16 [shape=box label="Enter block"];
17 [shape=box label="Enter function anonymousFunction"];
18 [shape=box label="Enter block"];
19 [shape=box label="Access variable R|<local>/x|"];
20 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
21 [shape=box label="Exit block"];
22 [shape=box label="Exit function anonymousFunction"];
23 [shape=box label="Function call: R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|/A.foo|()
}
)"];
24 [shape=box label="Exit block"];
25 [shape=box label="Exit when branch result"];
26 [shape=box label="Enter when branch condition else"];
27 [shape=box label="Exit when branch condition"];
28 [shape=box label="Enter block"];
29 [shape=box label="Exit block"];
30 [shape=box label="Exit when branch result"];
31 [shape=box label="Exit when"];
32 [shape=box label="Exit block"];
33 [shape=box label="Exit function test_1"];
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14};
14 -> {15};
15 -> {16 26};
16 -> {17};
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {31};
26 -> {27};
27 -> {28};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {32};
32 -> {33};
}
subgraph test_2 {
34 [shape=box label="Enter function test_2"];
35 [shape=box label="Enter block"];
36 [shape=box label="Enter function anonymousFunction"];
37 [shape=box label="Enter block"];
38 [shape=box label="Access variable R|<local>/x|"];
39 [shape=box label="Type operator: x as B"];
40 [shape=box label="Exit block"];
41 [shape=box label="Exit function anonymousFunction"];
42 [shape=box label="Function call: R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
(R|<local>/x| as R|B|)
}
)"];
43 [shape=box label="Access variable R|<local>/x|"];
44 [shape=box label="Function call: R|<local>/x|.R|/B.bar|()"];
45 [shape=box label="Exit block"];
46 [shape=box label="Exit function test_2"];
34 -> {35};
35 -> {36};
36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
44 -> {45};
45 -> {46};
}
subgraph test_3 {
47 [shape=box label="Enter function test_3"];
48 [shape=box label="Enter block"];
49 [shape=box label="Enter when"];
50 [shape=box label="Enter when branch condition "];
51 [shape=box label="Access variable R|<local>/x|"];
52 [shape=box label="Type operator: x is A"];
53 [shape=box label="Exit when branch condition"];
54 [shape=box label="Enter block"];
55 [shape=box label="Enter function anonymousFunction"];
56 [shape=box label="Enter block"];
57 [shape=box label="Access variable R|<local>/x|"];
58 [shape=box label="Function call: R|<local>/x|.R|/A.foo|()"];
59 [shape=box label="Access variable R|<local>/x|"];
60 [shape=box label="Type operator: x as B"];
61 [shape=box label="Exit block"];
62 [shape=box label="Exit function anonymousFunction"];
63 [shape=box label="Function call: R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|/A.foo|()
(R|<local>/x| as R|B|)
}
)"];
64 [shape=box label="Access variable R|<local>/x|"];
65 [shape=box label="Function call: R|<local>/x|.R|/B.bar|()"];
66 [shape=box label="Exit block"];
67 [shape=box label="Exit when branch result"];
68 [shape=box label="Enter when branch condition else"];
69 [shape=box label="Exit when branch condition"];
70 [shape=box label="Enter block"];
71 [shape=box label="Exit block"];
72 [shape=box label="Exit when branch result"];
73 [shape=box label="Exit when"];
74 [shape=box label="Exit block"];
75 [shape=box label="Exit function test_3"];
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54 68};
54 -> {55};
55 -> {56};
56 -> {57};
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {61};
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {66};
66 -> {67};
67 -> {73};
68 -> {69};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {73};
73 -> {74};
74 -> {75};
}
}
@@ -0,0 +1,37 @@
interface A {
fun foo()
}
interface B {
fun bar()
}
fun run(block: () -> Unit) {
block()
}
fun test_1(x: Any?) {
if (x is A) {
run {
x.foo()
}
}
}
fun test_2(x: Any?) {
run {
x as B
}
x.bar()
}
fun test_3(x: Any?) {
if (x is A) {
run {
x.foo()
x as B
}
x.bar()
}
}
@@ -0,0 +1,47 @@
FILE: inPlaceLambdas.kt
public abstract interface A : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Unit|
}
public abstract interface B : R|kotlin/Any| {
public abstract fun bar(): R|kotlin/Unit|
}
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_1(x: R|kotlin/Any?|): R|kotlin/Unit| {
when () {
(R|<local>/x| is R|A|) -> {
R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|/A.foo|()
}
)
}
else -> {
}
}
}
public final fun test_2(x: R|kotlin/Any?|): R|kotlin/Unit| {
R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
(R|<local>/x| as R|B|)
}
)
R|<local>/x|.R|/B.bar|()
}
public final fun test_3(x: R|kotlin/Any?|): R|kotlin/Unit| {
when () {
(R|<local>/x| is R|A|) -> {
R|/run|(<L> = run@fun <anonymous>(): R|kotlin/Unit| <kind=EXACTLY_ONCE> {
R|<local>/x|.R|/A.foo|()
(R|<local>/x| as R|B|)
}
)
R|<local>/x|.R|/B.bar|()
}
else -> {
}
}
}
@@ -51,6 +51,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest {
runTest("compiler/fir/resolve/testData/resolve/cfg/jumps.kt");
}
@TestMetadata("lambdas.kt")
public void testLambdas() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/cfg/lambdas.kt");
}
@TestMetadata("loops.kt")
public void testLoops() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/cfg/loops.kt");
@@ -109,6 +114,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/equalsAndIdentity.kt");
}
@TestMetadata("inPlaceLambdas.kt")
public void testInPlaceLambdas() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt");
}
@TestMetadata("nullability.kt")
public void testNullability() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt");