diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt index 030a3faefe3..b0171c632a5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.optimization import org.jetbrains.kotlin.codegen.TransformationMethodVisitor +import org.jetbrains.kotlin.codegen.optimization.boxing.FastPopBackwardPropagationTransformer import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting @@ -54,6 +55,7 @@ class OptimizationMethodVisitor( RedundantCheckCastEliminationMethodTransformer(), ConstantConditionEliminationMethodTransformer(), RedundantBoxingMethodTransformer(), + FastPopBackwardPropagationTransformer(), PopBackwardPropagationTransformer(), DeadCodeEliminationMethodTransformer(), RedundantGotoMethodTransformer(), diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/FastPopBackwardPropagationTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/FastPopBackwardPropagationTransformer.kt new file mode 100644 index 00000000000..8822821e60c --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/FastPopBackwardPropagationTransformer.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2017 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.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import org.jetbrains.org.objectweb.asm.tree.InsnNode +import org.jetbrains.org.objectweb.asm.tree.MethodNode + +class FastPopBackwardPropagationTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + while (transformOnce(methodNode)) { + } + } + + private fun transformOnce(methodNode: MethodNode): Boolean { + val toRemove = ArrayList() + val toReplaceWithNop = ArrayList() + + val insns = methodNode.instructions.toArray() + + for (i in 1 until insns.size) { + val insn = insns[i] + val prev = insns[i - 1] + + when (insn.opcode) { + Opcodes.POP -> { + if (prev.isEliminatedByPop()) { + toReplaceWithNop.add(insn) + toRemove.add(prev) + } + } + + Opcodes.POP2 -> { + if (prev.isEliminatedByPop2()) { + toReplaceWithNop.add(insn) + toRemove.add(prev) + } + else if (i > 1) { + val prev2 = insns[i - 2] + if (prev.isEliminatedByPop() && prev2.isEliminatedByPop()) { + toReplaceWithNop.add(insn) + toRemove.add(prev) + toRemove.add(prev2) + } + } + } + } + } + + toReplaceWithNop.forEach { methodNode.instructions.set(it, InsnNode(Opcodes.NOP)) } + toRemove.forEach { methodNode.instructions.remove(it) } + + return toReplaceWithNop.isNotEmpty() && + toRemove.isNotEmpty() + } + + private fun AbstractInsnNode.isEliminatedByPop() = + opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 || + opcode in Opcodes.BIPUSH..Opcodes.ILOAD || + opcode == Opcodes.FLOAD || + opcode == Opcodes.ALOAD || + isUnitInstance() || + opcode == Opcodes.DUP + + private fun AbstractInsnNode.isEliminatedByPop2() = + opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || + opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 || + opcode == Opcodes.LLOAD || + opcode == Opcodes.DLOAD || + opcode == Opcodes.DUP2 +} + diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/largeMethodWithCoercionToUnit.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/largeMethodWithCoercionToUnit.kt new file mode 100644 index 00000000000..4e0d9509d97 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/largeMethodWithCoercionToUnit.kt @@ -0,0 +1,25 @@ +inline fun inlineFunVoid(f: () -> Unit): Unit { + return f() +} + +inline fun coercedToUnit() { + inlineFunVoid { + var aa = 1 + ++aa + } +} + +inline fun dup(f: () -> Unit): Unit { + f() + f() +} + +fun test() { + dup { dup { dup { dup { dup { + dup { dup { + coercedToUnit() + }}}}} + }} +} + +// 3 POP \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 62077c742b0..fc1e8ee2271 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -776,6 +776,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("largeMethodWithCoercionToUnit.kt") + public void testLargeMethodWithCoercionToUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/largeMethodWithCoercionToUnit.kt"); + doTest(fileName); + } + @TestMetadata("nopInlineFuns.kt") public void testNopInlineFuns() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt");