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 a5046e84937..edf5a0e8bc7 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 @@ -30,7 +30,6 @@ import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor -import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* @@ -703,7 +702,7 @@ class ExpressionCodegen( val endLabel = Label() if (shouldGenerateCondition) { - genConditionWithOptimizationsIfPossible(branch, data, elseLabel) + genConditionalJumpWithOptimizationsIfPossible(branch.condition, 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". @@ -740,16 +739,21 @@ class ExpressionCodegen( return if (shouldGenerateBody) resultFromBody else resultFromTail } - private fun genConditionWithOptimizationsIfPossible(branch: IrBranch, data: BlockInfo, elseLabel: Label) { - var condition = branch.condition - var jumpIfFalse = true + private fun genConditionalJumpWithOptimizationsIfPossible( + originalCondition: IrExpression, + data: BlockInfo, + jumpToLabel: Label, + originalJumpIfFalse: Boolean = true + ) { + var condition = originalCondition + var jumpIfFalse = originalJumpIfFalse // Instead of materializing a negated value when used for control flow, flip the branch // targets instead. This significantly cuts down the amount of branches and loads of // const_0 and const_1 in the generated java bytecode. if (isNegation(condition, classCodegen.context)) { condition = negationArgument(condition as IrCall) - jumpIfFalse = false + jumpIfFalse = !jumpIfFalse } // Do not materialize null constants to check for null. Instead use the JVM bytecode @@ -761,9 +765,9 @@ class ExpressionCodegen( val other = if (left.isNullConst()) right else left gen(other, data).put(other.asmType, mv) if (jumpIfFalse) { - mv.ifnonnull(elseLabel) + mv.ifnonnull(jumpToLabel) } else { - mv.ifnull(elseLabel) + mv.ifnull(jumpToLabel) } return } @@ -776,7 +780,7 @@ class ExpressionCodegen( val callable = resolveToCallable(condition, false) (callable as IrIntrinsicFunction).loadArguments(this, data) val stackValue = intrinsic.genStackValue(condition, classCodegen.context) - BranchedValue.condJump(stackValue, elseLabel, jumpIfFalse, mv) + BranchedValue.condJump(stackValue, jumpToLabel, jumpIfFalse, mv) return } } @@ -784,7 +788,8 @@ class ExpressionCodegen( // For instance of type operators, branch directly on the instanceof result instead // of materializing a boolean and performing an extra jump. if (condition is IrTypeOperatorCall && - (condition.operator == IrTypeOperator.NOT_INSTANCEOF || condition.operator == IrTypeOperator.INSTANCEOF)) { + (condition.operator == IrTypeOperator.NOT_INSTANCEOF || condition.operator == IrTypeOperator.INSTANCEOF) + ) { val asmType = condition.typeOperand.toKotlinType().asmType gen(condition.argument, OBJECT_TYPE, data) val type = boxType(asmType) @@ -794,12 +799,12 @@ class ExpressionCodegen( onStack(Type.BOOLEAN_TYPE) else StackValue.not(onStack(Type.BOOLEAN_TYPE)) - BranchedValue.condJump(stackValue, elseLabel, jumpIfFalse, mv) + BranchedValue.condJump(stackValue, jumpToLabel, jumpIfFalse, mv) return } gen(condition, data).put(condition.asmType, mv) - BranchedValue.condJump(onStack(condition.asmType), elseLabel, jumpIfFalse, mv) + BranchedValue.condJump(onStack(condition.asmType), jumpToLabel, jumpIfFalse, mv) } private fun isNullCheck(expression: IrExpression): Boolean { @@ -920,8 +925,7 @@ class ExpressionCodegen( mv.fakeAlwaysTrueIfeq(label) } } else { - gen(condition, data) - BranchedValue.condJump(StackValue.onStack(condition.asmType), label, jumpIfFalse, mv) + genConditionalJumpWithOptimizationsIfPossible(condition, data, label, jumpIfFalse) } } diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedNonZeroCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedNonZeroCompare.kt new file mode 100644 index 00000000000..f85ebce76de --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedNonZeroCompare.kt @@ -0,0 +1,123 @@ +// Generates ICONST_1 +val a = 1 + +fun main() { + // Negated == comparisons + // Generates IF_ICMPEQ and GOTO + if (!(a == 42)) { + "then" + } else { + "else" + } + + // Generates IF_ICMPEQ and GOTO + while (!(a == 42)) { + "loop" + } + + // Generates IF_ICMPNE + do { + "loop" + } while (!(a == 42)) + + // != comparisons + // Generates IF_ICMPEQ and GOTO + if (a != 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPEQ and GOTO + while (a != 42) { + "loop" + } + + // Generates IF_ICMPNE + do { + "loop" + } while (a != 42) + + // Negated > comparisons + // Generates IF_ICMPGT and GOTO + if (!(a > 42)) { + "then" + } else { + "else" + } + + // Generates IF_ICMPGT and GOTO + while (!(a > 42)) { + "loop" + } + + // Generates IF_ICMPLE + do { + "loop" + } while (!(a > 42)) + + // Negated >= comparisons + // Generates IF_ICMPGE and GOTO + if (!(a >= 42)) { + "then" + } else { + "else" + } + + // Generates IF_ICMPGE and GOTO + while (!(a >= 42)) { + "loop" + } + + // Generates IF_ICMPLT + do { + "loop" + } while (!(a >= 42)) + + // Negated < comparisons + // Generates IF_ICMPLT and GOTO + if (!(a < 42)) { + "then" + } else { + "else" + } + + // Generates IF_ICMPLT and GOTO + while (!(a < 42)) { + "loop" + } + + // Generates IF_ICMPGE + do { + "loop" + } while (!(a < 42)) + + // Negated <= comparisons + // Generates IF_ICMPLE and GOTO + if (!(a <= 42)) { + "then" + } else { + "else" + } + + // Generates IF_ICMPLE and GOTO + while (!(a <= 42)) { + "loop" + } + + // Generates IF_ICMPGT + do { + "loop" + } while (!(a <= 42)) +} + +//0 ICONST_0 +//1 ICONST_1 +//2 IF_ICMPNE +//4 IF_ICMPEQ +//3 IF_ICMPLE +//3 IF_ICMPLT +//3 IF_ICMPGE +//3 IF_ICMPGT +//18 IF +//12 GOTO \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt index 82770b9acc6..d6ab83fc7a5 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt @@ -1,14 +1,45 @@ -fun main(p: String?) { +fun main(p: String?, p2: String?) { + // Negated == null comparisons + // Generates IFNULL and GOTO if (!(p == null)) { "then" } else { "else" } + + // Generates IFNULL and GOTO + while (!(p == null)) { + "loop" + } + + // Generates IFNONNULL + do { + "loop" + } while (!(p == null)) + + // != null comparisons + // Generates IFNULL and GOTO + if (p2 != null) { + "then" + } else { + "else" + } + + // Generates IFNULL and GOTO + while (p2 != null) { + "loop" + } + + // Generates IFNONNULL + do { + "loop" + } while (p2 != null) } //0 ICONST_0 //0 ICONST_1 //0 ACONST_NULL -//1 IFNULL -//1 IF -//1 GOTO +//4 IFNULL +//2 IFNONNULL +//6 IF +//4 GOTO diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedZeroCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedZeroCompare.kt index a00f8492543..c9907c116c0 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/negatedZeroCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedZeroCompare.kt @@ -1,16 +1,123 @@ +// Generates ICONST_1 val a = 1 fun main() { + // Negated == comparisons + // Generates IFEQ and GOTO if (!(a == 0)) { "then" } else { "else" } + + // Generates IFEQ and GOTO + while (!(a == 0)) { + "loop" + } + + // Generates IFNE + do { + "loop" + } while (!(a == 0)) + + // != comparisons + // Generates IFEQ and GOTO + if (a != 0) { + "then" + } else { + "else" + } + + // Generates IFEQ and GOTO + while (a != 0) { + "loop" + } + + // Generates IFNE + do { + "loop" + } while (a != 0) + + // Negated > comparisons + // Generates IFGT and GOTO + if (!(a > 0)) { + "then" + } else { + "else" + } + + // Generates IFGT and GOTO + while (!(a > 0)) { + "loop" + } + + // Generates IFLE + do { + "loop" + } while (!(a > 0)) + + // Negated >= comparisons + // Generates IFGE and GOTO + if (!(a >= 0)) { + "then" + } else { + "else" + } + + // Generates IFGE and GOTO + while (!(a >= 0)) { + "loop" + } + + // Generates IFLT + do { + "loop" + } while (!(a >= 0)) + + // Negated < comparisons + // Generates IFLT and GOTO + if (!(a < 0)) { + "then" + } else { + "else" + } + + // Generates IFLT and GOTO + while (!(a < 0)) { + "loop" + } + + // Generates IFGE + do { + "loop" + } while (!(a < 0)) + + // Negated <= comparisons + // Generates IFLE and GOTO + if (!(a <= 0)) { + "then" + } else { + "else" + } + + // Generates IFLE and GOTO + while (!(a <= 0)) { + "loop" + } + + // Generates IFGT + do { + "loop" + } while (!(a <= 0)) } //0 ICONST_0 //1 ICONST_1 -//1 IFEQ -//0 IFNE -//1 IF -//1 GOTO \ No newline at end of file +//2 IFNE +//4 IFEQ +//3 IFLE +//3 IFLT +//3 IFGE +//3 IFGT +//18 IF +//12 GOTO \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/conditions/nonZeroCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/nonZeroCompare.kt new file mode 100644 index 00000000000..cbf0281103e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/nonZeroCompare.kt @@ -0,0 +1,105 @@ +// Generates ICONST_1 +val a = 1 + +fun main() { + // == comparisons + // Generates IF_ICMPNE and GOTO + if (a == 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPNE and GOTO + while (a == 42) { + "loop" + } + + // Generates IF_ICMPEQ + do { + "loop" + } while (a == 42) + + // > comparisons + // Generates IF_ICMPLE and GOTO + if (a > 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPLE and GOTO + while (a > 42) { + "loop" + } + + // Generates IF_ICMPGT + do { + "loop" + } while (a > 42) + + // >= comparisons + // Generates IF_ICMPLT and GOTO + if (a >= 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPLT and GOTO + while (a >= 42) { + "loop" + } + + // Generates IF_ICMPGE + do { + "loop" + } while (a >= 42) + + // < comparisons + // Generates IF_ICMPGE and GOTO + if (a < 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPGE and GOTO + while (a < 42) { + "loop" + } + + // Generates IF_ICMPLT + do { + "loop" + } while (a < 42) + + // <= comparisons + // Generates IF_ICMPGT and GOTO + if (a <= 42) { + "then" + } else { + "else" + } + + // Generates IF_ICMPGT and GOTO + while (a <= 42) { + "loop" + } + + // Generates IF_ICMPLE + do { + "loop" + } while (a <= 42) +} + +//0 ICONST_0 +//1 ICONST_1 +//2 IF_ICMPNE +//1 IF_ICMPEQ +//3 IF_ICMPLE +//3 IF_ICMPLT +//3 IF_ICMPGE +//3 IF_ICMPGT +//15 IF +//10 GOTO diff --git a/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt index 28110eba44d..fb39c38c964 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt @@ -1,14 +1,26 @@ fun main(p: String?) { + // Generates IFNONNULL and GOTO if (p == null) { "then" } else { "else" } + + // Generates IFNONNULL and GOTO + while (p == null) { + "loop" + } + + // Generates IFNULL + do { + "loop" + } while (p == null) } //0 ICONST_0 //0 ICONST_1 //0 ACONST_NULL -//1 IFNONNULL -//1 IF -//1 GOTO +//2 IFNONNULL +//1 IFNULL +//3 IF +//2 GOTO diff --git a/compiler/testData/codegen/bytecodeText/conditions/zeroCompare.kt b/compiler/testData/codegen/bytecodeText/conditions/zeroCompare.kt index 9010e305d5d..5172b1d8b1a 100644 --- a/compiler/testData/codegen/bytecodeText/conditions/zeroCompare.kt +++ b/compiler/testData/codegen/bytecodeText/conditions/zeroCompare.kt @@ -1,16 +1,105 @@ +// Generates ICONST_1 val a = 1 fun main() { + // == comparisons + // Generates IFNE and GOTO if (a == 0) { "then" } else { "else" } + + // Generates IFNE and GOTO + while (a == 0) { + "loop" + } + + // Generates IFEQ + do { + "loop" + } while (a == 0) + + // > comparisons + // Generates IFLE and GOTO + if (a > 0) { + "then" + } else { + "else" + } + + // Generates IFLE and GOTO + while (a > 0) { + "loop" + } + + // Generates IFGT + do { + "loop" + } while (a > 0) + + // >= comparisons + // Generates IFLT and GOTO + if (a >= 0) { + "then" + } else { + "else" + } + + // Generates IFLT and GOTO + while (a >= 0) { + "loop" + } + + // Generates IFGE + do { + "loop" + } while (a >= 0) + + // < comparisons + // Generates IFGE and GOTO + if (a < 0) { + "then" + } else { + "else" + } + + // Generates IFGE and GOTO + while (a < 0) { + "loop" + } + + // Generates IFLT + do { + "loop" + } while (a < 0) + + // <= comparisons + // Generates IFGT and GOTO + if (a <= 0) { + "then" + } else { + "else" + } + + // Generates IFGT and GOTO + while (a <= 0) { + "loop" + } + + // Generates IFLE + do { + "loop" + } while (a <= 0) } //0 ICONST_0 //1 ICONST_1 -//0 IFEQ -//1 IFNE -//1 IF -//1 GOTO +//2 IFNE +//1 IFEQ +//3 IFLE +//3 IFLT +//3 IFGE +//3 IFGT +//15 IF +//10 GOTO diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 2f8e16d0b33..e6de0a9e09b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -914,6 +914,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt"); } + @TestMetadata("negatedNonZeroCompare.kt") + public void testNegatedNonZeroCompare() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/conditions/negatedNonZeroCompare.kt"); + } + @TestMetadata("negatedNullCompare.kt") public void testNegatedNullCompare() throws Exception { runTest("compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt"); @@ -939,6 +944,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/conditions/noBoxingForPrimitiveEqObject.kt"); } + @TestMetadata("nonZeroCompare.kt") + public void testNonZeroCompare() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/conditions/nonZeroCompare.kt"); + } + @TestMetadata("nullCompare.kt") public void testNullCompare() throws Exception { runTest("compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 2ab6042614a..1b5302ffac8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -914,6 +914,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt"); } + @TestMetadata("negatedNonZeroCompare.kt") + public void testNegatedNonZeroCompare() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/conditions/negatedNonZeroCompare.kt"); + } + @TestMetadata("negatedNullCompare.kt") public void testNegatedNullCompare() throws Exception { runTest("compiler/testData/codegen/bytecodeText/conditions/negatedNullCompare.kt"); @@ -939,6 +944,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/conditions/noBoxingForPrimitiveEqObject.kt"); } + @TestMetadata("nonZeroCompare.kt") + public void testNonZeroCompare() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/conditions/nonZeroCompare.kt"); + } + @TestMetadata("nullCompare.kt") public void testNullCompare() throws Exception { runTest("compiler/testData/codegen/bytecodeText/conditions/nullCompare.kt");