Break / continue implementation, test with break / continue, related changes in loop & loop tests
This commit is contained in:
committed by
Dmitry Petrov
parent
e03e13af43
commit
5a04c72e75
+34
-6
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.IrElement
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.dump
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.ir2cfg.graph.ControlFlowGraph
|
import org.jetbrains.kotlin.ir2cfg.graph.ControlFlowGraph
|
||||||
import org.jetbrains.kotlin.ir2cfg.nodes.MergeCfgElement
|
import org.jetbrains.kotlin.ir2cfg.nodes.MergeCfgElement
|
||||||
@@ -31,6 +32,10 @@ class FunctionGenerator(val function: IrFunction) {
|
|||||||
|
|
||||||
val exit = MergeCfgElement(function, "Function exit")
|
val exit = MergeCfgElement(function, "Function exit")
|
||||||
|
|
||||||
|
val loopEntries = mutableMapOf<IrLoop, IrElement>()
|
||||||
|
|
||||||
|
val loopExits = mutableMapOf<IrLoop, IrElement>()
|
||||||
|
|
||||||
fun generate(): ControlFlowGraph {
|
fun generate(): ControlFlowGraph {
|
||||||
val visitor = FunctionVisitor()
|
val visitor = FunctionVisitor()
|
||||||
function.accept(visitor, true)
|
function.accept(visitor, true)
|
||||||
@@ -130,7 +135,10 @@ class FunctionGenerator(val function: IrFunction) {
|
|||||||
if (data) {
|
if (data) {
|
||||||
builder.add(loop)
|
builder.add(loop)
|
||||||
}
|
}
|
||||||
|
val exit = MergeCfgElement(loop, "While exit")
|
||||||
|
loopExits[loop] = exit
|
||||||
val entry = MergeCfgElement(loop, "While entry")
|
val entry = MergeCfgElement(loop, "While entry")
|
||||||
|
loopEntries[loop] = entry
|
||||||
builder.jump(entry)
|
builder.jump(entry)
|
||||||
val condition = loop.condition
|
val condition = loop.condition
|
||||||
condition.process(includeSelf = false)
|
condition.process(includeSelf = false)
|
||||||
@@ -139,25 +147,45 @@ class FunctionGenerator(val function: IrFunction) {
|
|||||||
if (!body?.process().isNothing()) {
|
if (!body?.process().isNothing()) {
|
||||||
builder.jump(entry)
|
builder.jump(entry)
|
||||||
}
|
}
|
||||||
builder.move(condition)
|
builder.jump(exit, from = condition)
|
||||||
return condition
|
return exit
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Boolean): IrElement? {
|
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Boolean): IrElement? {
|
||||||
if (data) {
|
if (data) {
|
||||||
builder.add(loop)
|
builder.add(loop)
|
||||||
}
|
}
|
||||||
|
val exit = MergeCfgElement(loop, "Do..while exit")
|
||||||
|
loopExits[loop] = exit
|
||||||
val entry = MergeCfgElement(loop, "Do..while entry")
|
val entry = MergeCfgElement(loop, "Do..while entry")
|
||||||
|
loopEntries[loop] = entry
|
||||||
builder.jump(entry)
|
builder.jump(entry)
|
||||||
val body = loop.body
|
val body = loop.body
|
||||||
val condition = loop.condition
|
val condition = loop.condition
|
||||||
if (body?.process() !is IrReturn) {
|
if (!body?.process().isNothing()) {
|
||||||
condition.process(includeSelf = false)
|
condition.process(includeSelf = false)
|
||||||
builder.jump(condition)
|
builder.jump(condition)
|
||||||
builder.jump(entry)
|
builder.jump(entry, from = condition)
|
||||||
builder.move(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? {
|
override fun visitElement(element: IrElement, data: Boolean): IrElement? {
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ OUTGOING -> BB 1
|
|||||||
BB 5
|
BB 5
|
||||||
INCOMING <- BB 3
|
INCOMING <- BB 3
|
||||||
CALL 'GT0(Int): Boolean' type=kotlin.Boolean origin=GT
|
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
|
CONTENT
|
||||||
1 GET_VAR 'count: Int' type=kotlin.Int origin=null
|
1 GET_VAR 'count: Int' type=kotlin.Int origin=null
|
||||||
2 RETURN type=kotlin.Nothing from='digitCountInNumber(Int, Int): Int'
|
2 RETURN type=kotlin.Nothing from='digitCountInNumber(Int, Int): Int'
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ OUTGOING -> BB 1
|
|||||||
BB 3
|
BB 3
|
||||||
INCOMING <- BB 1
|
INCOMING <- BB 1
|
||||||
CALL 'hasNext(): Boolean' type=kotlin.Boolean origin=FOR_LOOP_HAS_NEXT
|
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
|
CONTENT
|
||||||
1 GET_VAR 'result: Int' type=kotlin.Int origin=null
|
1 GET_VAR 'result: Int' type=kotlin.Int origin=null
|
||||||
2 RETURN type=kotlin.Nothing from='factorial(Int): Int'
|
2 RETURN type=kotlin.Nothing from='factorial(Int): Int'
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
+74
@@ -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
|
||||||
|
|
||||||
@@ -90,6 +90,12 @@ public class IrCfgTestCaseGenerated extends AbstractIrCfgTestCase {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irCfg/loop/factorial.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irCfg/loop/factorial.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/ir/irCfg/when")
|
||||||
|
|||||||
Reference in New Issue
Block a user