Optimize out trivial INSTANCEOF checks

#KT-18157 Fixed Target versions 1.1.4
This commit is contained in:
Dmitry Petrov
2017-05-29 13:44:28 +03:00
parent 74ee9ce68b
commit d559212d70
9 changed files with 97 additions and 35 deletions
@@ -20,12 +20,12 @@ import org.jetbrains.kotlin.codegen.coroutines.withInstructionAdapter
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.optimization.common.isInsn
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
@@ -87,28 +87,30 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
private fun injectNullabilityAssumptions(checkedReferenceTypes: Map<AbstractInsnNode, Type>) =
NullabilityAssumptionsBuilder(checkedReferenceTypes).injectNullabilityAssumptions()
private fun analyzeNullabilities(): Map<AbstractInsnNode, Nullability> {
private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> {
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
val insns = methodNode.instructions.toArray()
val nullabilityMap = HashMap<AbstractInsnNode, Nullability>()
val nullabilityMap = LinkedHashMap<AbstractInsnNode, StrictBasicValue>()
for (i in insns.indices) {
val nullability = frames[i]?.top()?.getNullability() ?: continue
val top = frames[i]?.top() as? StrictBasicValue ?: continue
val nullability = top.getNullability()
if (nullability == Nullability.NULLABLE) continue
val insn = insns[i]
if (insn.isInstanceOfOrNullCheck()) {
nullabilityMap[insn] = nullability
nullabilityMap[insn] = top
}
}
return nullabilityMap
}
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, Nullability>) {
for ((insn, nullability) in nullabilityMap) {
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
for ((insn, value) in nullabilityMap) {
val nullability = value.getNullability()
when (insn.opcode) {
Opcodes.IFNULL -> transformTrivialNullJump(insn as JumpInsnNode, nullability == Nullability.NULL)
Opcodes.IFNONNULL -> transformTrivialNullJump(insn as JumpInsnNode, nullability == Nullability.NOT_NULL)
Opcodes.INSTANCEOF -> transformInstanceOf(insn, nullability)
Opcodes.INSTANCEOF -> transformInstanceOf(insn as TypeInsnNode, nullability, value)
}
}
}
@@ -127,37 +129,22 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
}
}
private fun transformInstanceOf(insn: AbstractInsnNode, nullability: Nullability) {
if (nullability != Nullability.NULL) return
private fun transformInstanceOf(insn: TypeInsnNode, nullability: Nullability, value: StrictBasicValue) {
if (ReifiedTypeInliner.isOperationReifiedMarker(insn.previous)) return
changes = true
val nextOpcode = insn.next?.opcode
if (nextOpcode == Opcodes.IFEQ || nextOpcode == Opcodes.IFNE)
transformNullInstanceOfWithJump(insn)
else
transformNullInstanceOf(insn)
}
private fun transformNullInstanceOf(insn: AbstractInsnNode) {
methodNode.instructions.run {
popReferenceValueBefore(insn)
set(insn, InsnNode(Opcodes.ICONST_0))
if (nullability == Nullability.NULL) {
changes = true
transformTrivialInstanceOf(insn, false)
}
else if (nullability == Nullability.NOT_NULL && value.type.internalName == insn.desc) {
changes = true
transformTrivialInstanceOf(insn, true)
}
}
private fun transformNullInstanceOfWithJump(insn: AbstractInsnNode) {
private fun transformTrivialInstanceOf(insn: AbstractInsnNode, constValue: Boolean) {
methodNode.instructions.run {
popReferenceValueBefore(insn)
val jump = insn.next.assertedCast<JumpInsnNode> { "JumpInsnNode expected" }
remove(insn)
if (jump.opcode == Opcodes.IFEQ) {
set(jump, JumpInsnNode(Opcodes.GOTO, jump.label))
}
else {
remove(jump)
}
set(insn, if (constValue) InsnNode(Opcodes.ICONST_1) else InsnNode(Opcodes.ICONST_0))
}
}
@@ -345,7 +332,9 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
}
internal fun AbstractInsnNode.isInstanceOfOrNullCheck() =
opcode == Opcodes.INSTANCEOF || opcode == Opcodes.IFNULL || opcode == Opcodes.IFNONNULL
opcode == Opcodes.INSTANCEOF ||
opcode == Opcodes.IFNULL ||
opcode == Opcodes.IFNONNULL
internal fun AbstractInsnNode.isCheckParameterNotNull() =
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
@@ -0,0 +1,17 @@
sealed class A {
class B : A()
class C : A()
}
inline fun foo(): A = A.B()
fun box(): String {
val a: A = foo()
val b: Boolean
when (a) {
is A.B -> b = true
is A.C -> b = false
}
return if (b) "OK" else "FAIL"
}
@@ -0,0 +1,24 @@
sealed class A {
class B : A()
class C : A()
}
inline fun foo(): A = A.B()
fun box(): String {
val a: A = foo()
val b: Boolean
when (a) {
is A.B -> b = true
is A.C -> b = false
}
return if (b) "OK" else "FAIL"
}
// 0 TABLESWITCH
// 0 LOOKUPSWITCH
// 0 ATHROW
// 0 INSTANCEOF
// 0 FAIL
// 0 POP
@@ -4,8 +4,10 @@ sealed class A {
class C : A()
}
fun foo(): A = A.C()
fun box(): String {
val a: A = A.C()
val a: A = foo()
val b: Boolean
when (a) {
A.B -> b = true
@@ -11443,6 +11443,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt");
doTest(fileName);
}
@TestMetadata("trivialInstanceOf.kt")
public void testTrivialInstanceOf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/objectIntrinsics")
@@ -11443,6 +11443,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt");
doTest(fileName);
}
@TestMetadata("trivialInstanceOf.kt")
public void testTrivialInstanceOf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/objectIntrinsics")
@@ -1792,6 +1792,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/nullCheckOptimization/reifiedNullIs.kt");
doTest(fileName);
}
@TestMetadata("trivialInstanceOf.kt")
public void testTrivialInstanceOf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/nullCheckOptimization/trivialInstanceOf.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/ranges")
@@ -11443,6 +11443,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt");
doTest(fileName);
}
@TestMetadata("trivialInstanceOf.kt")
public void testTrivialInstanceOf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/objectIntrinsics")
@@ -12799,6 +12799,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/kt7774.kt");
doTest(fileName);
}
@TestMetadata("trivialInstanceOf.kt")
public void testTrivialInstanceOf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/objectIntrinsics")