diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.java index ceea80fca0b..1b85e107bae 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.java @@ -55,7 +55,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { } @NotNull - private BoxedBasicValue createNewBoxing( + protected BasicValue createNewBoxing( @NotNull AbstractInsnNode insn, @NotNull Type type, @Nullable ProgressionIteratorBasicValue progressionIterator ) { @@ -201,7 +201,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { return super.unaryOperation(insn, value); } - private static boolean isExactValue(@NotNull BasicValue value) { + protected boolean isExactValue(@NotNull BasicValue value) { return value instanceof ProgressionIteratorBasicValue || value instanceof BoxedBasicValue || (value.getType() != null && isProgressionClass(value.getType().getInternalName())); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/NullabilityInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/NullabilityInterpreter.kt new file mode 100644 index 00000000000..b637309f74f --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/NullabilityInterpreter.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.optimization.boxing + +import org.jetbrains.org.objectweb.asm.tree.InsnList +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue +import org.jetbrains.kotlin.codegen.optimization.common.BasicValueWrapper +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import org.jetbrains.org.objectweb.asm.Opcodes + +public class NullabilityInterpreter(insns: InsnList) : BoxingInterpreter(insns) { + override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue) = makeNotNullIfNeeded(insn, super.unaryOperation(insn, value)) + + override fun newOperation(insn: AbstractInsnNode) = makeNotNullIfNeeded(insn, super.newOperation(insn)) + + override fun isExactValue(value: BasicValue) = super.isExactValue(value) || value is NotNullBasicValue + + override fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?) = + NotNullBasicValue(BasicValue(type)) +} + +private fun makeNotNullIfNeeded(insn: AbstractInsnNode, value: BasicValue?): BasicValue? = + when (insn.getOpcode()) { + Opcodes.ANEWARRAY, Opcodes.NEWARRAY, Opcodes.LDC, Opcodes.NEW -> NotNullBasicValue(value) + else -> value + } + +class NotNullBasicValue(wrappedValue: BasicValue?) : BasicValueWrapper(wrappedValue) { + override fun equals(other: Any?): Boolean = other is NotNullBasicValue +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java index c2212043194..b05c34b9663 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java @@ -39,7 +39,7 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer { InsnList insnList = methodNode.instructions; Frame[] frames = analyze( internalClassName, methodNode, - new BoxingInterpreter(insnList) + new NullabilityInterpreter(insnList) ); List insnsToOptimize = new ArrayList(); @@ -49,8 +49,7 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer { AbstractInsnNode insn = insnList.get(i); if ((insn.getOpcode() == Opcodes.IFNULL || insn.getOpcode() == Opcodes.IFNONNULL) && - frame != null && frame.getStack(frame.getStackSize() - 1) instanceof BoxedBasicValue) { - + frame != null && frame.getStack(frame.getStackSize() - 1) instanceof NotNullBasicValue) { insnsToOptimize.add(insn); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt index 4e2cdfbc65f..f2ad312649a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt @@ -69,3 +69,11 @@ fun MethodNode.prepareForEmitting() { current = prev } } + +abstract class BasicValueWrapper(val wrappedValue: BasicValue?) : BasicValue(wrappedValue?.getType()) { + val basicValue: BasicValue? get() = (wrappedValue as? BasicValueWrapper)?.basicValue ?: wrappedValue + + override fun equals(other: Any?): Boolean { + return super.equals(other) && this.javaClass == other?.javaClass + } +} diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/arrayConstructor.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/arrayConstructor.kt new file mode 100644 index 00000000000..d171aa9c575 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/arrayConstructor.kt @@ -0,0 +1,8 @@ +fun box() { + val x = Array(1) { "" } +} + +// 0 IFNULL +// 0 IFNONNULL +// 0 ATHROW +// 0 throwNpe diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt index 12165e0962b..ec4628b2635 100644 --- a/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt @@ -1,3 +1,5 @@ +class A + fun foo(x: Any?) {} fun box() { @@ -8,6 +10,9 @@ fun box() { z!! foo(1 as java.lang.Integer) + + val y: Any? = if (1 == 1) x else A() + y!! } // 0 IFNULL diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt new file mode 100644 index 00000000000..a3e1485370b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt @@ -0,0 +1,12 @@ +fun box() { + val x: Any? = "abc" + val y: Any? = if (1 == 1) x else "cde" + + x!! + y!! +} + +// 0 IFNULL +// 0 IFNONNULL +// 0 throwNpe +// 0 ATHROW diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt new file mode 100644 index 00000000000..dfec1f31eba --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt @@ -0,0 +1,15 @@ +class A +fun box() { + val x: A? = A() + val z: A? = A() + val z1: A? = if (1 == 1) z else x + + x!! + z!! + z1!! +} + +// 0 IFNULL +// 0 IFNONNULL +// 0 throwNpe +// 0 ATHROW diff --git a/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt new file mode 100644 index 00000000000..b175e4e2f06 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt @@ -0,0 +1,18 @@ +class A +fun box() { + val x: A? = A() + val y: A? + if (1 == 0) { + y = x + } + else { + y = null + } + + y!! +} + +// 0 IFNULL +// 1 IFNONNULL +// 1 throwNpe +// 0 ATHROW diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index e4e8cdbe8f3..c66ac10d5f4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -309,6 +309,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/deadCodeElimination"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("arrayConstructor.kt") + public void testArrayConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/arrayConstructor.kt"); + doTest(fileName); + } + @TestMetadata("boxing.kt") public void testBoxing() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt"); @@ -326,6 +332,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/lastReturn.kt"); doTest(fileName); } + + @TestMetadata("literal.kt") + public void testLiteral() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/literal.kt"); + doTest(fileName); + } + + @TestMetadata("simpleConstructor.kt") + public void testSimpleConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("simpleConstructorNotRedundant.kt") + public void testSimpleConstructorNotRedundant() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/simpleConstructorNotRedundant.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/bytecodeText/directInvoke")