diff --git a/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt b/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt index 828606d2e4c..007b6113ec5 100644 --- a/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt +++ b/compiler/ir/ir.ir2cfg/src/org/jetbrains/kotlin/ir2cfg/generators/FunctionGenerator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir2cfg.graph.ControlFlowGraph import org.jetbrains.kotlin.ir2cfg.nodes.MergeCfgElement @@ -31,6 +32,10 @@ class FunctionGenerator(val function: IrFunction) { val exit = MergeCfgElement(function, "Function exit") + val loopEntries = mutableMapOf() + + val loopExits = mutableMapOf() + fun generate(): ControlFlowGraph { val visitor = FunctionVisitor() function.accept(visitor, true) @@ -130,7 +135,10 @@ class FunctionGenerator(val function: IrFunction) { if (data) { builder.add(loop) } + val exit = MergeCfgElement(loop, "While exit") + loopExits[loop] = exit val entry = MergeCfgElement(loop, "While entry") + loopEntries[loop] = entry builder.jump(entry) val condition = loop.condition condition.process(includeSelf = false) @@ -139,25 +147,45 @@ class FunctionGenerator(val function: IrFunction) { if (!body?.process().isNothing()) { builder.jump(entry) } - builder.move(condition) - return condition + builder.jump(exit, from = condition) + return exit } override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Boolean): IrElement? { if (data) { builder.add(loop) } + val exit = MergeCfgElement(loop, "Do..while exit") + loopExits[loop] = exit val entry = MergeCfgElement(loop, "Do..while entry") + loopEntries[loop] = entry builder.jump(entry) val body = loop.body val condition = loop.condition - if (body?.process() !is IrReturn) { + if (!body?.process().isNothing()) { condition.process(includeSelf = false) builder.jump(condition) - builder.jump(entry) - builder.move(condition) + builder.jump(entry, from = condition) + builder.jump(exit, from = condition) } - return condition + builder.move(exit) + return exit + } + + override fun visitBreak(jump: IrBreak, data: Boolean): IrElement? { + if (data) { + builder.add(jump) + } + builder.jump(loopExits[jump.loop] ?: throw AssertionError("Loop exit not found for ${jump.loop.dump()}")) + return jump + } + + override fun visitContinue(jump: IrContinue, data: Boolean): IrElement? { + if (data) { + builder.add(jump) + } + builder.jump(loopEntries[jump.loop] ?: throw AssertionError("Loop entry not found for ${jump.loop.dump()}")) + return jump } override fun visitElement(element: IrElement, data: Boolean): IrElement? { diff --git a/compiler/testData/ir/irCfg/loop/digitCount.txt b/compiler/testData/ir/irCfg/loop/digitCount.txt index 715df402be9..8be9b1233d3 100644 --- a/compiler/testData/ir/irCfg/loop/digitCount.txt +++ b/compiler/testData/ir/irCfg/loop/digitCount.txt @@ -40,6 +40,12 @@ OUTGOING -> BB 1 BB 5 INCOMING <- BB 3 CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +CONTENT +OUTGOING -> BB 6 + Do..while exit: DO_WHILE label=null origin=DO_WHILE_LOOP +BB 6 +INCOMING <- BB 5 + Do..while exit: DO_WHILE label=null origin=DO_WHILE_LOOP CONTENT 1 GET_VAR 'count: Int' type=kotlin.Int origin=null 2 RETURN type=kotlin.Nothing from='digitCountInNumber(Int, Int): Int' diff --git a/compiler/testData/ir/irCfg/loop/factorial.txt b/compiler/testData/ir/irCfg/loop/factorial.txt index db484e902e9..6f534cda70a 100644 --- a/compiler/testData/ir/irCfg/loop/factorial.txt +++ b/compiler/testData/ir/irCfg/loop/factorial.txt @@ -28,6 +28,12 @@ OUTGOING -> BB 1 BB 3 INCOMING <- BB 1 CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT +CONTENT +OUTGOING -> BB 4 + While exit: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 4 +INCOMING <- BB 3 + While exit: WHILE label=null origin=FOR_LOOP_INNER_WHILE CONTENT 1 GET_VAR 'result: Int' type=kotlin.Int origin=null 2 RETURN type=kotlin.Nothing from='factorial(Int): Int' diff --git a/compiler/testData/ir/irCfg/loop/isPerfect.kt b/compiler/testData/ir/irCfg/loop/isPerfect.kt new file mode 100644 index 00000000000..34ba793f678 --- /dev/null +++ b/compiler/testData/ir/irCfg/loop/isPerfect.kt @@ -0,0 +1,9 @@ +fun isPerfect(n: Int): Boolean { + var sum = 1 + for (m in 2..n/2) { + if (n % m > 0) continue + sum += m + if (sum > n) break + } + return sum == n +} \ No newline at end of file diff --git a/compiler/testData/ir/irCfg/loop/isPerfect.txt b/compiler/testData/ir/irCfg/loop/isPerfect.txt new file mode 100644 index 00000000000..2eea9851405 --- /dev/null +++ b/compiler/testData/ir/irCfg/loop/isPerfect.txt @@ -0,0 +1,74 @@ +// FILE: /isPerfect.kt +// FUN: isPerfect +BB 0 +CONTENT + 1 FUN public fun isPerfect(n: kotlin.Int): kotlin.Boolean + 2 CONST Int type=kotlin.Int value='1' + 3 VAR var sum: kotlin.Int + 4 CALL 'iterator(): IntIterator' type=kotlin.collections.IntIterator origin=FOR_LOOP_ITERATOR + 5 VAR IR_TEMPORARY_VARIABLE val tmp0_iterator: kotlin.collections.IntIterator + 6 WHILE label=null origin=FOR_LOOP_INNER_WHILE +OUTGOING -> BB 1 + While entry: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 1 +INCOMING <- BB 0, 3, 6 + While entry: WHILE label=null origin=FOR_LOOP_INNER_WHILE +CONTENT +OUTGOING -> BB 2, 7 + CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT +BB 2 +INCOMING <- BB 1 + CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT +CONTENT + 1 CALL 'next(): Int' type=kotlin.Int origin=FOR_LOOP_NEXT + 2 VAR val m: kotlin.Int + 3 WHEN type=kotlin.Unit origin=null +OUTGOING -> BB 3, 4 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +BB 3 +INCOMING <- BB 2 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +CONTENT + 1 CONTINUE label=null loop.label=null +OUTGOING -> BB 1 + While entry: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 4 +INCOMING <- BB 2 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +CONTENT + 1 SET_VAR 'sum: Int' type=kotlin.Unit origin=PLUSEQ + 2 WHEN type=kotlin.Unit origin=null +OUTGOING -> BB 5, 6 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +BB 5 +INCOMING <- BB 4 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +CONTENT + 1 BREAK label=null loop.label=null +OUTGOING -> BB 8 + While exit: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 6 +INCOMING <- BB 4 + CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT +CONTENT +OUTGOING -> BB 1 + While entry: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 7 +INCOMING <- BB 1 + CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT +CONTENT +OUTGOING -> BB 8 + While exit: WHILE label=null origin=FOR_LOOP_INNER_WHILE +BB 8 +INCOMING <- BB 5, 7 + While exit: WHILE label=null origin=FOR_LOOP_INNER_WHILE +CONTENT + 1 CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + 2 RETURN type=kotlin.Nothing from='isPerfect(Int): Boolean' +OUTGOING -> NONE + Function exit: FUN public fun isPerfect(n: kotlin.Int): kotlin.Boolean + +// END FUN: isPerfect + +// END FILE: /isPerfect.kt + diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrCfgTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrCfgTestCaseGenerated.java index 10117ac6eae..968cce51460 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrCfgTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrCfgTestCaseGenerated.java @@ -90,6 +90,12 @@ public class IrCfgTestCaseGenerated extends AbstractIrCfgTestCase { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irCfg/loop/factorial.kt"); doTest(fileName); } + + @TestMetadata("isPerfect.kt") + public void testIsPerfect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irCfg/loop/isPerfect.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/ir/irCfg/when")