diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 179f1629653..2b0f2f49328 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -39,9 +39,7 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isMarkedNullable import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.toKotlinType -import org.jetbrains.kotlin.ir.util.dump -import org.jetbrains.kotlin.ir.util.isNullConst -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass @@ -694,31 +692,52 @@ class ExpressionCodegen( } private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List): StackValue { + // True or false conditions known at compile time need not be generated. + val shouldGenerateCondition = !branch.condition.isFalseConst() && !branch.condition.isTrueConst() + // Body of an always-false-condition need not be generated. + val shouldGenerateBody = !branch.condition.isFalseConst() + // Don't generate the tail if it doesn't exist or isn't reachable. + val shouldGenerateTail = !otherBranches.isEmpty() && !branch.condition.isTrueConst() + val elseLabel = Label() - val thenBranch = branch.result - //TODO don't generate condition for else branch - java verifier fails with empty stack - val elseBranch = branch is IrElseBranch - if (!elseBranch) { + val endLabel = Label() + + if (shouldGenerateCondition) { genConditionWithOptimizationsIfPossible(branch, data, elseLabel) + } else { + // Even when a condition isn't generated, a linenumber and nop is still required so that a debugger can break on the line of the + // condition, except for the explicit "else". + if (branch !is IrElseBranch) { + branch.condition.markLineNumber(startOffset = true) + mv.nop() + } } - val end = Label() - - val result = thenBranch.run { - val stackValue = gen(this, data) - coerceNotToUnit(stackValue.type, stackValue.kotlinType, type) + val resultFromBody = if (shouldGenerateBody) { + val thenBranch = branch.result + val result = thenBranch.run { + val stackValue = gen(this, data) + coerceNotToUnit(stackValue.type, stackValue.kotlinType, type) + } + mv.goTo(endLabel) + mv.mark(elseLabel) + result + } else { + none() } - mv.goTo(end) - mv.mark(elseLabel) - - if (!otherBranches.isEmpty()) { + val resultFromTail = if (shouldGenerateTail) { val nextBranch = otherBranches.first() genIfWithBranches(nextBranch, data, type, otherBranches.drop(1)) + } else { + none() } - mv.mark(end) - return result + // endLabel is only used to jump from end-of-then-body to the end of the whole if cascade. + if (shouldGenerateBody) + mv.mark(endLabel) + + return if (shouldGenerateBody) resultFromBody else resultFromTail } private fun genConditionWithOptimizationsIfPossible(branch: IrBranch, data: BlockInfo, elseLabel: Label) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt index bfa3e77e948..0fba706faf6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmBuiltinOptimizationLowering.kt @@ -109,10 +109,11 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower } override fun visitWhen(expression: IrWhen): IrExpression { + val isCompilerGenerated = expression.origin == null expression.transformChildrenVoid(this) // Remove all branches with constant false condition. expression.branches.removeIf() { - it.condition.isFalseConst() + it.condition.isFalseConst() && isCompilerGenerated } // If the only condition that is left has a constant true condition remove the // when in favor of the result. If there are no conditions left, remove the when @@ -120,7 +121,7 @@ class JvmBuiltinOptimizationLowering(val context: JvmBackendContext) : FileLower return if (expression.branches.size == 0) { IrBlockImpl(expression.startOffset, expression.endOffset, context.irBuiltIns.unitType) } else { - expression.branches.first().takeIf { it.condition.isTrueConst() }?.result ?: expression + expression.branches.first().takeIf { it.condition.isTrueConst() && isCompilerGenerated }?.result ?: expression } } diff --git a/compiler/testData/codegen/box/controlStructures/ifConst1.kt b/compiler/testData/codegen/box/controlStructures/ifConst1.kt new file mode 100644 index 00000000000..dc74e46b215 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/ifConst1.kt @@ -0,0 +1,19 @@ +fun foo(b: Boolean): String { + return if (b) { + "OK" + } else if (false) { + "fail: reached unreachable code at line 5" + } else if (true) { + "fail: reached unexpected code at line 7" + } else if (true) { + "fail: reached unreachable code at line 9" + } else if (b) { + "fail: reached unreachable code at line 11" + } else { + "fail: reached unreachable code at line 13" + } +} + +fun box(): String { + return foo(true) +} diff --git a/compiler/testData/codegen/box/controlStructures/ifConst2.kt b/compiler/testData/codegen/box/controlStructures/ifConst2.kt new file mode 100644 index 00000000000..bdf1dbc57d2 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/ifConst2.kt @@ -0,0 +1,19 @@ +fun foo(b: Boolean): String { + return if (b) { + "fail: reached unexpected code at line 3" + } else if (false) { + "fail: reached unreachable code at line 5" + } else if (true) { + "OK" + } else if (true) { + "fail: reached unreachable code at line 9" + } else if (b) { + "fail: reached unreachable code at line 11" + } else { + "fail: reached unreachable code at line 13" + } +} + +fun box(): String { + return foo(false) +} diff --git a/compiler/testData/codegen/bytecodeText/controlStructures/ifConsts.kt b/compiler/testData/codegen/bytecodeText/controlStructures/ifConsts.kt new file mode 100644 index 00000000000..495386e9c3c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/controlStructures/ifConsts.kt @@ -0,0 +1,19 @@ +fun foo(b: Boolean): Int { + return if (b) { + 100 + } else if (false) { + 101 + } else if (true) { + 102 + } else if (true) { + 103 + } else if (b) { + 104 + } else { + 105 + } +} + +// 2 BIPUSH +// 1 BIPUSH 100 +// 1 BIPUSH 102 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/ifConsts.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/ifConsts.kt new file mode 100644 index 00000000000..6fdeedeffbf --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/ifConsts.kt @@ -0,0 +1,35 @@ +fun cond() = false + +fun bar() {} + +fun foo() { + if (cond()) { + bar() + } else if (true) { + bar() + } else { + bar() + } + + if (false) { + bar() + } else if (true) { + bar() + } else { + bar() + } + + if (true) { + bar() + } else if (false) { + bar() + } else { + bar() + } +} + +// 1 LINENUMBER 6 +// 1 LINENUMBER 8 +// 1 LINENUMBER 14 +// 1 LINENUMBER 16 +// 1 LINENUMBER 22 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalse.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalse.kt new file mode 100644 index 00000000000..d51843a2e0d --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalse.kt @@ -0,0 +1,7 @@ +fun foo(): Int { + if (false) { + return 1 + } + return 2 +} +// 1 LINENUMBER 2 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalseElse.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalseElse.kt new file mode 100644 index 00000000000..899ced55af5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/ifFalseElse.kt @@ -0,0 +1,8 @@ +fun foo(): Int { + if (false) { + return 1 + } else { + return 2 + } +} +// 1 LINENUMBER 2 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrue.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrue.kt new file mode 100644 index 00000000000..c637bc4ee16 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrue.kt @@ -0,0 +1,7 @@ +fun foo(): Int { + if (true) { + return 1 + } + return 2 +} +// 1 LINENUMBER 2 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt new file mode 100644 index 00000000000..ed33ff7b39a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt @@ -0,0 +1,8 @@ +fun foo(): Int { + if (true) { + return 1 + } else { + return 2 + } +} +// 1 LINENUMBER 2 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 37d6cfa6452..fd9e81b43bd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4673,6 +4673,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/controlStructures/forUserType.kt"); } + @TestMetadata("ifConst1.kt") + public void testIfConst1() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst1.kt"); + } + + @TestMetadata("ifConst2.kt") + public void testIfConst2() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst2.kt"); + } + @TestMetadata("inRangeConditionsInWhen.kt") public void testInRangeConditionsInWhen() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/inRangeConditionsInWhen.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 0239e8afa56..2f8e16d0b33 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1106,6 +1106,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/controlStructures"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("ifConsts.kt") + public void testIfConsts() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/controlStructures/ifConsts.kt"); + } + @TestMetadata("kt17110.kt") public void testKt17110() throws Exception { runTest("compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt"); @@ -2640,11 +2645,36 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/lineNumbers"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("ifConsts.kt") + public void testIfConsts() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifConsts.kt"); + } + @TestMetadata("ifElse.kt") public void testIfElse() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifElse.kt"); } + @TestMetadata("ifFalse.kt") + public void testIfFalse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifFalse.kt"); + } + + @TestMetadata("ifFalseElse.kt") + public void testIfFalseElse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifFalseElse.kt"); + } + + @TestMetadata("ifTrue.kt") + public void testIfTrue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrue.kt"); + } + + @TestMetadata("ifTrueElse.kt") + public void testIfTrueElse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt"); + } + @TestMetadata("singleThen.kt") public void testSingleThen() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index a9e0ccf4e71..98216926ce2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -4673,6 +4673,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/controlStructures/forUserType.kt"); } + @TestMetadata("ifConst1.kt") + public void testIfConst1() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst1.kt"); + } + + @TestMetadata("ifConst2.kt") + public void testIfConst2() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst2.kt"); + } + @TestMetadata("inRangeConditionsInWhen.kt") public void testInRangeConditionsInWhen() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/inRangeConditionsInWhen.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6a1f8139a2f..ff36338896f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -4673,6 +4673,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/controlStructures/forUserType.kt"); } + @TestMetadata("ifConst1.kt") + public void testIfConst1() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst1.kt"); + } + + @TestMetadata("ifConst2.kt") + public void testIfConst2() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst2.kt"); + } + @TestMetadata("inRangeConditionsInWhen.kt") public void testInRangeConditionsInWhen() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/inRangeConditionsInWhen.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 5d4e1dc771e..2ab6042614a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -1106,6 +1106,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/controlStructures"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("ifConsts.kt") + public void testIfConsts() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/controlStructures/ifConsts.kt"); + } + @TestMetadata("kt17110.kt") public void testKt17110() throws Exception { runTest("compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt"); @@ -2640,11 +2645,36 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/lineNumbers"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("ifConsts.kt") + public void testIfConsts() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifConsts.kt"); + } + @TestMetadata("ifElse.kt") public void testIfElse() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifElse.kt"); } + @TestMetadata("ifFalse.kt") + public void testIfFalse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifFalse.kt"); + } + + @TestMetadata("ifFalseElse.kt") + public void testIfFalseElse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifFalseElse.kt"); + } + + @TestMetadata("ifTrue.kt") + public void testIfTrue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrue.kt"); + } + + @TestMetadata("ifTrueElse.kt") + public void testIfTrueElse() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt"); + } + @TestMetadata("singleThen.kt") public void testSingleThen() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 1ee49f4f6be..9b78f13b07a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -3858,6 +3858,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forUserType.kt"); } + @TestMetadata("ifConst1.kt") + public void testIfConst1() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst1.kt"); + } + + @TestMetadata("ifConst2.kt") + public void testIfConst2() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst2.kt"); + } + @TestMetadata("inRangeConditionsInWhen.kt") public void testInRangeConditionsInWhen() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/inRangeConditionsInWhen.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index b0039ccada7..fbeb91df14d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -3868,6 +3868,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forUserType.kt"); } + @TestMetadata("ifConst1.kt") + public void testIfConst1() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst1.kt"); + } + + @TestMetadata("ifConst2.kt") + public void testIfConst2() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/ifConst2.kt"); + } + @TestMetadata("inRangeConditionsInWhen.kt") public void testInRangeConditionsInWhen() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/inRangeConditionsInWhen.kt");