All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2015 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

import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
import org.jetbrains.kotlin.codegen.inline.InplaceArgumentsMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
import org.jetbrains.kotlin.codegen.optimization.common.nodeType
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.temporaryVals.TemporaryVariablesEliminationTransformer
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode

class OptimizationMethodVisitor(
    delegate: MethodVisitor,
    private val mandatoryTransformationsOnly: Boolean,
    private val generationState: GenerationState,
    access: Int,
    name: String,
    desc: String,
    signature: String?,
    exceptions: Array?
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
    val normalizationMethodTransformer = CompositeMethodTransformer(
        InplaceArgumentsMethodTransformer(),
        FixStackWithLabelNormalizationMethodTransformer(),
        MethodVerifier("AFTER mandatory stack transformations", generationState)
    )

    val optimizationTransformer = CompositeMethodTransformer(
        CapturedVarsOptimizationMethodTransformer(),
        RedundantNullCheckMethodTransformer(generationState),
        RedundantCheckCastEliminationMethodTransformer(),
        ConstantConditionEliminationMethodTransformer(),
        RedundantBoxingMethodTransformer(generationState),
        TemporaryVariablesEliminationTransformer(generationState),
        StackPeepholeOptimizationsTransformer(),
        PopBackwardPropagationTransformer(),
        DeadCodeEliminationMethodTransformer(),
        RedundantGotoMethodTransformer(),
        RedundantNopsCleanupMethodTransformer(),
        NegatedJumpsMethodTransformer(),
        RedundantCheckcastsBeforeAastoreMethodTransformer,
        MethodVerifier("AFTER optimizations", generationState)
    )

    override fun performTransformations(methodNode: MethodNode) {
        normalizationMethodTransformer.transform("fake", methodNode)
        UninitializedStoresProcessor(methodNode).run()

        if (!mandatoryTransformationsOnly && canBeOptimized(methodNode) && !generationState.config.disableOptimization) {
            optimizationTransformer.transform("fake", methodNode)
        }

        DeadCodeEliminationMethodTransformer().transform("fake", methodNode)

        methodNode.prepareForEmitting()
    }

    companion object {
        const val MEMORY_LIMIT_BY_METHOD_MB = 50
        private const val TRY_CATCH_BLOCKS_SOFT_LIMIT = 16

        fun canBeOptimized(node: MethodNode): Boolean {
            if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_SOFT_LIMIT) {
                if (getTotalFramesWeight(getTotalTcbSize(node), node) > MEMORY_LIMIT_BY_METHOD_MB)
                    return false
            }
            return getTotalFramesWeight(node.instructions.first.countInsnsWithFramesUntil(null), node) < MEMORY_LIMIT_BY_METHOD_MB
        }

        fun canBeOptimizedUsingSourceInterpreter(node: MethodNode): Boolean {
            val methodSize = node.instructions.first.countInsnsWithFramesUntil(null)
            if (node.tryCatchBlocks.size > TRY_CATCH_BLOCKS_SOFT_LIMIT) {
                if (getTotalFramesWeight(getTotalTcbSize(node) * methodSize, node) > MEMORY_LIMIT_BY_METHOD_MB)
                    return false
            }
            return getTotalFramesWeight(methodSize * methodSize, node) < MEMORY_LIMIT_BY_METHOD_MB
        }

        private fun AbstractInsnNode?.countInsnsWithFramesUntil(end: AbstractInsnNode?): Int {
            var it = this
            var result = 0
            while (it != end && it != null) {
                // FastMethodAnalyzer will reuse the Frame instance when a pseudo-instruction or a NOP is followed
                // by anything other than a jump-targeted label, so those instructions consume no extra memory.
                // To avoid checking all jumps, here we assume all labels are potentially targeted.
                // (Effectively, all of this means that adding line numbers should not inhibit optimization.)
                val type = it.nodeType
                if ((type != AbstractInsnNode.LINE && type != AbstractInsnNode.FRAME && type != AbstractInsnNode.LABEL &&
                            it.opcode != Opcodes.NOP) || it.next?.nodeType == AbstractInsnNode.LABEL
                ) result++
                it = it.next
            }
            return result
        }

        private fun getTotalFramesWeight(size: Int, node: MethodNode) =
            size.toLong() * (node.maxLocals + node.maxStack) / (1024 * 1024)

        private fun getTotalTcbSize(node: MethodNode) =
            node.tryCatchBlocks.sumOf { it.start.countInsnsWithFramesUntil(it.end) }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy