From d559212d70b8724adb11d1637b66473c08f9aeb8 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 29 May 2017 13:44:28 +0300 Subject: [PATCH] Optimize out trivial INSTANCEOF checks #KT-18157 Fixed Target versions 1.1.4 --- .../RedundantNullCheckMethodTransformer.kt | 57 ++++++++----------- .../trivialInstanceOf.kt | 17 ++++++ .../trivialInstanceOf.kt | 24 ++++++++ .../when/sealedWhenInitialization.kt | 4 +- .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++ .../codegen/BytecodeTextTestGenerated.java | 6 ++ .../LightAnalysisModeTestGenerated.java | 6 ++ .../semantics/JsCodegenBoxTestGenerated.java | 6 ++ 9 files changed, 97 insertions(+), 35 deletions(-) create mode 100644 compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt create mode 100644 compiler/testData/codegen/bytecodeText/nullCheckOptimization/trivialInstanceOf.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt index 94e0c166a9c..bb2f2456658 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt @@ -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) = NullabilityAssumptionsBuilder(checkedReferenceTypes).injectNullabilityAssumptions() - private fun analyzeNullabilities(): Map { + private fun analyzeNullabilities(): Map { val frames = analyze(internalClassName, methodNode, NullabilityInterpreter()) val insns = methodNode.instructions.toArray() - val nullabilityMap = HashMap() + val nullabilityMap = LinkedHashMap() 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) { - for ((insn, nullability) in nullabilityMap) { + private fun transformTrivialChecks(nullabilityMap: Map) { + 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 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(Opcodes.INVOKESTATIC) { diff --git a/compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt b/compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt new file mode 100644 index 00000000000..89a0f2d67ad --- /dev/null +++ b/compiler/testData/codegen/box/nullCheckOptimization/trivialInstanceOf.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/trivialInstanceOf.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/trivialInstanceOf.kt new file mode 100644 index 00000000000..7201986c3a8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/trivialInstanceOf.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/when/sealedWhenInitialization.kt b/compiler/testData/codegen/bytecodeText/when/sealedWhenInitialization.kt index 2d70f2ce77e..c7aad5ad3cd 100644 --- a/compiler/testData/codegen/bytecodeText/when/sealedWhenInitialization.kt +++ b/compiler/testData/codegen/bytecodeText/when/sealedWhenInitialization.kt @@ -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 diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 5a7499c86ec..bb9cabbcdfc 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6f532a96e58..a15852d3e06 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 3dfb1fda03a..2d796711387 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index c2e3316f629..6f43d1ea3ac 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 98d40bdc74c..24d5e057ff7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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")