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 b0171c632a5..3deb32fde70 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt @@ -17,7 +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.StackPeepholeOptimizationsTransformer import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting @@ -55,7 +55,7 @@ class OptimizationMethodVisitor( RedundantCheckCastEliminationMethodTransformer(), ConstantConditionEliminationMethodTransformer(), RedundantBoxingMethodTransformer(), - FastPopBackwardPropagationTransformer(), + StackPeepholeOptimizationsTransformer(), 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 deleted file mode 100644 index f4965652015..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/FastPopBackwardPropagationTransformer.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.common.findPreviousOrNull -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() - - forInsn@ for (i in 1 until insns.size) { - val insn = insns[i] - val prev = insn.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn - - 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 = prev.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn - 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/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/StackPeepholeOptimizationsTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/StackPeepholeOptimizationsTransformer.kt new file mode 100644 index 00000000000..eeecf3f90b5 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/StackPeepholeOptimizationsTransformer.kt @@ -0,0 +1,122 @@ +/* + * 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.common.findPreviousOrNull +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.InsnList +import org.jetbrains.org.objectweb.asm.tree.InsnNode +import org.jetbrains.org.objectweb.asm.tree.MethodNode + +class StackPeepholeOptimizationsTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + while (transformOnce(methodNode)) { + } + } + + private fun transformOnce(methodNode: MethodNode): Boolean { + val actions = ArrayList<(InsnList) -> Unit>() + + val insns = methodNode.instructions.toArray() + + forInsn@ for (i in 1 until insns.size) { + val insn = insns[i] + val prev = insn.previous + val prevNonNop = insn.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn + + when (insn.opcode) { + Opcodes.POP -> { + when { + prevNonNop.isEliminatedByPop() -> actions.add { + it.set(insn, InsnNode(Opcodes.NOP)) + it.remove(prevNonNop) + } + prevNonNop.opcode == Opcodes.DUP_X1 -> actions.add { + it.remove(insn) + it.set(prevNonNop, InsnNode(Opcodes.SWAP)) + } + } + } + + Opcodes.SWAP -> { + val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn + if (prevNonNop.isPurePushOfSize1() && prevNonNop2.isPurePushOfSize1()) { + actions.add { + it.remove(insn) + it.set(prevNonNop, prevNonNop2.clone(emptyMap())) + it.set(prevNonNop2, prevNonNop.clone(emptyMap())) + } + } + } + + Opcodes.POP2 -> { + if (prevNonNop.isEliminatedByPop2()) { + actions.add { + it.set(insn, InsnNode(Opcodes.NOP)) + it.remove(prevNonNop) + } + } + else if (i > 1) { + val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn + if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) { + actions.add { + it.set(insn, InsnNode(Opcodes.NOP)) + it.remove(prevNonNop) + it.remove(prevNonNop2) + } + } + } + } + + Opcodes.NOP -> + if (prev.opcode == Opcodes.NOP) { + actions.add { + it.remove(prev) + } + } + } + } + + actions.forEach { it(methodNode.instructions) } + + return actions.isNotEmpty() + } + + private fun AbstractInsnNode.isEliminatedByPop() = + isPurePushOfSize1() || + opcode == Opcodes.DUP + + private fun AbstractInsnNode.isPurePushOfSize1(): Boolean = + opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 || + opcode in Opcodes.BIPUSH..Opcodes.ILOAD || + opcode == Opcodes.FLOAD || + opcode == Opcodes.ALOAD || + isUnitInstance() + + private fun AbstractInsnNode.isEliminatedByPop2() = + isPurePushOfSize2() || + opcode == Opcodes.DUP2 + + private fun AbstractInsnNode.isPurePushOfSize2(): Boolean = + opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || + opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 || + opcode == Opcodes.LLOAD || + opcode == Opcodes.DLOAD +} + diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/inRangeCheckWithConst.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/inRangeCheckWithConst.kt index 4146e3d73ea..b2a20820ea2 100644 --- a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/inRangeCheckWithConst.kt +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/inRangeCheckWithConst.kt @@ -3,8 +3,5 @@ fun testPrimitiveArray(ints: IntArray) = 10 in ints.indices -// We currently fail to optimize this method because of DUP_X1 instruction generated for range check. -// TODO either don't generate DUP_X1/DUP2_X2 instructions for range checks (extra local variable + extra instructions), -// or support DUPn_Xm instructions in PopBackwardPropagationTransformer -// - 0 DUP -// - 0 POP \ No newline at end of file +// 0 DUP +// 0 POP \ No newline at end of file