Refined redundant null check optimization
This commit is contained in:
+2
-2
@@ -55,7 +55,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private BoxedBasicValue createNewBoxing(
|
protected BasicValue createNewBoxing(
|
||||||
@NotNull AbstractInsnNode insn, @NotNull Type type,
|
@NotNull AbstractInsnNode insn, @NotNull Type type,
|
||||||
@Nullable ProgressionIteratorBasicValue progressionIterator
|
@Nullable ProgressionIteratorBasicValue progressionIterator
|
||||||
) {
|
) {
|
||||||
@@ -201,7 +201,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
return super.unaryOperation(insn, value);
|
return super.unaryOperation(insn, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isExactValue(@NotNull BasicValue value) {
|
protected boolean isExactValue(@NotNull BasicValue value) {
|
||||||
return value instanceof ProgressionIteratorBasicValue ||
|
return value instanceof ProgressionIteratorBasicValue ||
|
||||||
value instanceof BoxedBasicValue ||
|
value instanceof BoxedBasicValue ||
|
||||||
(value.getType() != null && isProgressionClass(value.getType().getInternalName()));
|
(value.getType() != null && isProgressionClass(value.getType().getInternalName()));
|
||||||
|
|||||||
+45
@@ -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
|
||||||
|
}
|
||||||
+2
-3
@@ -39,7 +39,7 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer {
|
|||||||
InsnList insnList = methodNode.instructions;
|
InsnList insnList = methodNode.instructions;
|
||||||
Frame<BasicValue>[] frames = analyze(
|
Frame<BasicValue>[] frames = analyze(
|
||||||
internalClassName, methodNode,
|
internalClassName, methodNode,
|
||||||
new BoxingInterpreter(insnList)
|
new NullabilityInterpreter(insnList)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
||||||
@@ -49,8 +49,7 @@ public class RedundantNullCheckMethodTransformer extends MethodTransformer {
|
|||||||
AbstractInsnNode insn = insnList.get(i);
|
AbstractInsnNode insn = insnList.get(i);
|
||||||
|
|
||||||
if ((insn.getOpcode() == Opcodes.IFNULL || insn.getOpcode() == Opcodes.IFNONNULL) &&
|
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);
|
insnsToOptimize.add(insn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,3 +69,11 @@ fun MethodNode.prepareForEmitting() {
|
|||||||
current = prev
|
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 foo(x: Any?) {}
|
||||||
|
|
||||||
fun box() {
|
fun box() {
|
||||||
@@ -8,6 +10,9 @@ fun box() {
|
|||||||
z!!
|
z!!
|
||||||
|
|
||||||
foo(1 as java.lang.Integer)
|
foo(1 as java.lang.Integer)
|
||||||
|
|
||||||
|
val y: Any? = if (1 == 1) x else A()
|
||||||
|
y!!
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 IFNULL
|
// 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
|
||||||
+18
@@ -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);
|
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")
|
@TestMetadata("boxing.kt")
|
||||||
public void testBoxing() throws Exception {
|
public void testBoxing() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/boxing.kt");
|
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");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination/lastReturn.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/directInvoke")
|
||||||
|
|||||||
Reference in New Issue
Block a user