diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/stackvalue/BranchedValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt similarity index 62% rename from compiler/backend/src/org/jetbrains/kotlin/codegen/stackvalue/BranchedValue.kt rename to compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt index cbfcc3ffe70..7eb32875fc1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/stackvalue/BranchedValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.codegen.stackvalue +package org.jetbrains.kotlin.codegen import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.codegen.AsmUtil @@ -32,7 +32,7 @@ open class BranchedValue(val arg1: StackValue, val arg2: StackValue? = null, val override fun putSelector(type: Type, v: InstructionAdapter) { val branchJumpLabel = Label() - condJump(branchJumpLabel, v) + condJump(branchJumpLabel, v, true) val endLabel = Label() v.iconst(1) v.visitJumpInsn(GOTO, endLabel) @@ -42,14 +42,10 @@ open class BranchedValue(val arg1: StackValue, val arg2: StackValue? = null, val coerceTo(type, v); } - open fun condJump(jumpIfFalse: Label, v: InstructionAdapter) { - arg1.put(operandType, v) + open fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + if (arg1 is CondJump) arg1.condJump(jumpLabel, v, jumpIfFalse) else arg1.put(operandType, v) arg2?.put(operandType, v) - v.visitJumpInsn(patchOpcode(opcode, v), jumpIfFalse); - } - - protected open fun invert(): BranchedValue { - return BranchedValue(this, negatedOperations.get(opcode)) + v.visitJumpInsn(patchOpcode(if (jumpIfFalse) opcode else negatedOperations[opcode], v), jumpLabel); } protected open fun patchOpcode(opcode: Int, v: InstructionAdapter): Int { @@ -60,12 +56,11 @@ open class BranchedValue(val arg1: StackValue, val arg2: StackValue? = null, val val negatedOperations = hashMapOf() val TRUE: BranchedValue = object : BranchedValue(StackValue.Constant(true, Type.BOOLEAN_TYPE), null, Type.BOOLEAN_TYPE, IFEQ) { - override fun invert(): BranchedValue { - return FALSE - } - - override fun condJump(jumpIfFalse: Label, v: InstructionAdapter) { + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + if (!jumpIfFalse) { + v.goTo(jumpLabel) + } } override fun putSelector(type: Type, v: InstructionAdapter) { @@ -73,13 +68,12 @@ open class BranchedValue(val arg1: StackValue, val arg2: StackValue? = null, val coerceTo(type, v); } } - val FALSE: BranchedValue = object : BranchedValue(StackValue.Constant(false, Type.BOOLEAN_TYPE), null, Type.BOOLEAN_TYPE, IFEQ) { - override fun invert(): BranchedValue { - return TRUE - } - override fun condJump(jumpIfFalse: Label, v: InstructionAdapter) { - v.goTo(jumpIfFalse) + val FALSE: BranchedValue = object : BranchedValue(StackValue.Constant(false, Type.BOOLEAN_TYPE), null, Type.BOOLEAN_TYPE, IFEQ) { + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + if (jumpIfFalse) { + v.goTo(jumpLabel) + } } override fun putSelector(type: Type, v: InstructionAdapter) { @@ -110,40 +104,73 @@ open class BranchedValue(val arg1: StackValue, val arg2: StackValue? = null, val if (argument.type != Type.BOOLEAN_TYPE) { throw UnsupportedOperationException("operand of ! must be boolean") } - - if (argument is BranchedValue) { - return argument.invert() - } - return BranchedValue(argument, null, Type.BOOLEAN_TYPE, IFNE) + return Invert(condJump(argument)) } fun condJump(condition: StackValue, label: Label, jumpIfFalse: Boolean, iv: InstructionAdapter) { - var condJump: BranchedValue = if (condition is BranchedValue) { + condJump(condition).condJump(label, iv, jumpIfFalse) + } + + fun condJump(condition: StackValue): CondJump { + return CondJump(if (condition is BranchedValue) { condition } else { BranchedValue(condition, null, Type.BOOLEAN_TYPE, IFEQ) - } - condJump = if (jumpIfFalse) condJump else condJump.invert() - condJump.condJump(label, iv) + }, IFEQ) } public fun cmp(opToken: IElementType, operandType: Type, left: StackValue, right: StackValue): StackValue = if (operandType.getSort() == Type.OBJECT) - ObjectCompare(opToken, ObjectCompare.getObjectCompareOpcode(opToken), operandType, left, right) + ObjectCompare(opToken, operandType, left, right) else - NumberCompare(opToken, NumberCompare.getNumberCompareOpcode(opToken), operandType, left, right) + NumberCompare(opToken, operandType, left, right) } } +class And(arg1: StackValue, arg2: StackValue) : + BranchedValue(BranchedValue.condJump(arg1), BranchedValue.condJump(arg2), Type.BOOLEAN_TYPE, IFEQ) { -class NumberCompare(val opToken: IElementType, opcode: Int, operandType: Type, left: StackValue, right: StackValue) : - BranchedValue(left, right, operandType, opcode) { - - override fun invert(): BranchedValue { - return NumberCompare(opToken, BranchedValue.negatedOperations[opcode], operandType, arg1, arg2!!) + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + val stayLabel = Label() + (arg1 as CondJump).condJump(if (jumpIfFalse) jumpLabel else stayLabel, v, true) + (arg2 as CondJump).condJump(jumpLabel, v, jumpIfFalse) + v.visitLabel(stayLabel) } +} + +class Or(arg1: StackValue, arg2: StackValue) : + BranchedValue(BranchedValue.condJump(arg1), BranchedValue.condJump(arg2), Type.BOOLEAN_TYPE, IFEQ) { + + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + val stayLabel = Label() + (arg1 as CondJump).condJump(if (jumpIfFalse) stayLabel else jumpLabel, v, false) + (arg2 as CondJump).condJump(jumpLabel, v, jumpIfFalse) + v.visitLabel(stayLabel) + } +} + +class Invert(val condition: BranchedValue) : BranchedValue(condition, null, Type.BOOLEAN_TYPE, IFEQ) { + + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + condition.condJump(jumpLabel, v, !jumpIfFalse) + } +} + +class CondJump(val condition: BranchedValue, op: Int) : BranchedValue(condition, null, Type.BOOLEAN_TYPE, op) { + + override fun putSelector(type: Type, v: InstructionAdapter) { + throw UnsupportedOperationException("Use condJump instead") + } + + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + condition.condJump(jumpLabel, v, jumpIfFalse) + } +} + +class NumberCompare(val opToken: IElementType, operandType: Type, left: StackValue, right: StackValue) : + BranchedValue(left, right, operandType, NumberCompare.getNumberCompareOpcode(opToken)) { override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int { when (operandType) { @@ -182,12 +209,8 @@ class NumberCompare(val opToken: IElementType, opcode: Int, operandType: Type, l } } -class ObjectCompare(val opToken: IElementType, opcode: Int, operandType: Type, left: StackValue, right: StackValue) : - BranchedValue(left, right, operandType, opcode) { - - override fun invert(): BranchedValue { - return ObjectCompare(opToken, BranchedValue.negatedOperations[opcode], operandType, arg1, arg2!!) - } +class ObjectCompare(val opToken: IElementType, operandType: Type, left: StackValue, right: StackValue) : + BranchedValue(left, right, operandType, ObjectCompare.getObjectCompareOpcode(opToken)) { companion object { fun getObjectCompareOpcode(opToken: IElementType): Int { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 60cd61c4e7c..fff5f9509de 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -38,7 +38,6 @@ import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; -import org.jetbrains.kotlin.codegen.stackvalue.BranchedValue; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.codegen.when.SwitchCodegen; @@ -3059,40 +3058,12 @@ public class ExpressionCodegen extends JetVisitor implem v.and(Type.INT_TYPE); } - private StackValue generateBooleanAnd(final JetBinaryExpression expression) { - return StackValue.operation(Type.BOOLEAN_TYPE, new Function1() { - @Override - public Unit invoke(InstructionAdapter v) { - gen(expression.getLeft(), Type.BOOLEAN_TYPE); - Label ifFalse = new Label(); - v.ifeq(ifFalse); - gen(expression.getRight(), Type.BOOLEAN_TYPE); - Label end = new Label(); - v.goTo(end); - v.mark(ifFalse); - v.iconst(0); - v.mark(end); - return Unit.INSTANCE$; - } - }); + private StackValue generateBooleanAnd(JetBinaryExpression expression) { + return StackValue.and(genLazy(expression.getLeft(), Type.BOOLEAN_TYPE), genLazy(expression.getRight(), Type.BOOLEAN_TYPE)); } - private StackValue generateBooleanOr(final JetBinaryExpression expression) { - return StackValue.operation(Type.BOOLEAN_TYPE, new Function1() { - @Override - public Unit invoke(InstructionAdapter v) { - gen(expression.getLeft(), Type.BOOLEAN_TYPE); - Label ifTrue = new Label(); - v.ifne(ifTrue); - gen(expression.getRight(), Type.BOOLEAN_TYPE); - Label end = new Label(); - v.goTo(end); - v.mark(ifTrue); - v.iconst(1); - v.mark(end); - return Unit.INSTANCE$; - } - }); + private StackValue generateBooleanOr(JetBinaryExpression expression) { + return StackValue.or(genLazy(expression.getLeft(), Type.BOOLEAN_TYPE), genLazy(expression.getRight(), Type.BOOLEAN_TYPE)); } private StackValue generateEquals(JetExpression left, JetExpression right, IElementType opToken) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 1857af9e269..c7d49f9b755 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -23,7 +23,6 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.PrimitiveType; -import org.jetbrains.kotlin.codegen.stackvalue.BranchedValue; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; @@ -171,6 +170,14 @@ public abstract class StackValue { return BranchedValue.Companion.createInvertValue(stackValue); } + public static StackValue or(@NotNull StackValue left, @NotNull StackValue right) { + return new Or(left, right); + } + + public static StackValue and(@NotNull StackValue left, @NotNull StackValue right) { + return new And(left, right); + } + @NotNull public static StackValue arrayElement(@NotNull Type type, StackValue array, StackValue index) { return new ArrayElement(type, array, index); diff --git a/compiler/testData/codegen/bytecodeText/conditions/conjuction.kt b/compiler/testData/codegen/bytecodeText/conditions/conjuction.kt new file mode 100644 index 00000000000..c610a57813b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/conjuction.kt @@ -0,0 +1,17 @@ +fun main() { + val a = false + val b = false + val c = false + if (a && b && c) { + "then" + } else { + "else" + } +} + +// 3 ICONST_0 +// 0 ICONST_1 +// 3 IFEQ +// 0 IFNE +// 3 IF +// 1 GOTO \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/conditions/disjunction.kt b/compiler/testData/codegen/bytecodeText/conditions/disjunction.kt new file mode 100644 index 00000000000..2fa0aa89f9b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/disjunction.kt @@ -0,0 +1,17 @@ +fun main() { + val a = false + val b = false + val c = false + if (a || b || c) { + "then" + } else { + "else" + } +} + +// 3 ICONST_0 +// 0 ICONST_1 +// 1 IFEQ +// 2 IFNE +// 3 IF +// 1 GOTO \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedConjuction.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedConjuction.kt new file mode 100644 index 00000000000..92a2d84505e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedConjuction.kt @@ -0,0 +1,17 @@ +fun main() { + val a = false + val b = false + val c = false + if (!(a && b && c)) { + "then" + } else { + "else" + } +} + +// 3 ICONST_0 +// 0 ICONST_1 +// 2 IFEQ +// 1 IFNE +// 3 IF +// 1 GOTO \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt b/compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt new file mode 100644 index 00000000000..e21882e88cf --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt @@ -0,0 +1,17 @@ +fun main() { + val a = false + val b = false + val c = false + if (!(a || b || c)) { + "then" + } else { + "else" + } +} + +// 3 ICONST_0 +// 0 ICONST_1 +// 0 IFEQ +// 3 IFNE +// 3 IF +// 1 GOTO \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 915c65f2493..21c95974ff8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -32,6 +32,7 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @InnerTestClasses({ BytecodeTextTestGenerated.BoxingOptimization.class, + BytecodeTextTestGenerated.Conditions.class, BytecodeTextTestGenerated.Constants.class, BytecodeTextTestGenerated.DeadCodeElimination.class, BytecodeTextTestGenerated.DirectInvoke.class, @@ -296,6 +297,39 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/conditions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Conditions extends AbstractBytecodeTextTest { + public void testAllFilesPresentInConditions() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/conditions"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("conjuction.kt") + public void testConjuction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/conditions/conjuction.kt"); + doTest(fileName); + } + + @TestMetadata("disjunction.kt") + public void testDisjunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/conditions/disjunction.kt"); + doTest(fileName); + } + + @TestMetadata("negatedConjuction.kt") + public void testNegatedConjuction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/conditions/negatedConjuction.kt"); + doTest(fileName); + } + + @TestMetadata("negatedDisjunction.kt") + public void testNegatedDisjunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/conditions/negatedDisjunction.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/constants") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)