Refined redundant null check optimization

This commit is contained in:
Denis Zharkov
2014-12-31 16:19:21 +03:00
parent 5675d2b26b
commit 6f94ebb9d6
10 changed files with 139 additions and 5 deletions
@@ -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()));
@@ -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
}
@@ -39,7 +39,7 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer {
InsnList insnList = methodNode.instructions;
Frame<BasicValue>[] frames = analyze(
internalClassName, methodNode,
new BoxingInterpreter(insnList)
new NullabilityInterpreter(insnList)
);
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
@@ -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);
}
}
@@ -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
}
}
@@ -0,0 +1,8 @@
fun box() {
val x = Array(1) { "" }
}
// 0 IFNULL
// 0 IFNONNULL
// 0 ATHROW
// 0 throwNpe
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")