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

org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
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.fixStack

import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.inline.isInlineMarker
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.codegen.pseudoInsns.parsePseudoInsnOrNull
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode

class FixStackMethodTransformer : MethodTransformer() {
    override fun transform(internalClassName: String, methodNode: MethodNode) {
        val context = FixStackContext(methodNode)

        if (!context.hasAnyMarkers()) return

        // If inline method markers are inconsistent, remove them now
        if (!context.consistentInlineMarkers) {
            InsnSequence(methodNode.instructions).forEach { insnNode ->
                if (isInlineMarker(insnNode))
                    methodNode.instructions.remove(insnNode)
            }
        }

        if (context.isAnalysisRequired()) {
            analyzeAndTransformBreakContinueGotos(context, internalClassName, methodNode)
            removeAlwaysFalseIfeqMarkers(context, methodNode)
            analyzeAndTransformSaveRestoreStack(context, internalClassName, methodNode)
        }

        removeAlwaysTrueIfeqMarkers(context, methodNode)
        removeAlwaysFalseIfeqMarkers(context, methodNode)
    }

    private fun analyzeAndTransformBreakContinueGotos(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
        val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
        analyzer.analyze()

        methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize

        val actions = arrayListOf<() -> Unit>()

        transformBreakContinueGotos(methodNode, context, actions, analyzer)

        actions.forEach { it() }
    }

    private fun analyzeAndTransformSaveRestoreStack(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
        val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
        analyzer.analyze()

        val actions = arrayListOf<() -> Unit>()

        transformSaveRestoreStackMarkers(methodNode, context, actions, analyzer)

        actions.forEach { it() }
    }

    private fun removeAlwaysFalseIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
        context.fakeAlwaysFalseIfeqMarkers.forEach { marker ->
            removeAlwaysFalseIfeq(methodNode, marker)
        }
        context.fakeAlwaysFalseIfeqMarkers.clear()
    }

    private fun removeAlwaysTrueIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
        context.fakeAlwaysTrueIfeqMarkers.forEach { marker ->
            replaceAlwaysTrueIfeqWithGoto(methodNode, marker)
        }
        context.fakeAlwaysTrueIfeqMarkers.clear()
    }

    private fun transformBreakContinueGotos(
            methodNode: MethodNode,
            fixStackContext: FixStackContext,
            actions: MutableList<() -> Unit>,
            analyzer: FixStackAnalyzer
    ) {
        fixStackContext.breakContinueGotoNodes.forEach { gotoNode ->
            val gotoIndex = methodNode.instructions.indexOf(gotoNode)
            val labelIndex = methodNode.instructions.indexOf(gotoNode.label)

            val actualStackSize = analyzer.getActualStackSize(gotoNode)
            val expectedStackSize = analyzer.getExpectedStackSize(gotoNode.label)

            if (actualStackSize >= 0 && expectedStackSize >= 0) {
                assert(expectedStackSize <= actualStackSize) {
                    "Label at $labelIndex, jump at $gotoIndex: stack underflow: $expectedStackSize > $actualStackSize"
                }
                val actualStackContent = analyzer.getActualStack(gotoNode)
                                         ?: throw AssertionError("Jump at $gotoIndex should be alive")
                actions.add { replaceMarkerWithPops(methodNode, gotoNode.previous, expectedStackSize, actualStackContent) }
            }
            else if (actualStackSize >= 0 && expectedStackSize < 0) {
                throw AssertionError("Live jump $gotoIndex to dead label $labelIndex")
            }
            else {
                val marker = gotoNode.previous
                actions.add { methodNode.instructions.remove(marker) }
            }
        }
    }

    private fun transformSaveRestoreStackMarkers(
            methodNode: MethodNode,
            context: FixStackContext,
            actions: MutableList<() -> Unit>,
            analyzer: FixStackAnalyzer
    ) {
        val localVariablesManager = LocalVariablesManager(context, methodNode)
        InsnSequence(methodNode.instructions).forEach { marker ->
            val pseudoInsn = parsePseudoInsnOrNull(marker)
            when {
                pseudoInsn == PseudoInsn.SAVE_STACK_BEFORE_TRY ->
                    transformSaveStackMarker(methodNode, actions, analyzer, marker, localVariablesManager)
                pseudoInsn == PseudoInsn.RESTORE_STACK_IN_TRY_CATCH ->
                    transformRestoreStackMarker(methodNode, actions, marker, localVariablesManager)
                isBeforeInlineMarker(marker) ->
                    transformBeforeInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager)
                isAfterInlineMarker(marker) ->
                    transformAfterInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager)
            }
        }
    }

    private fun transformSaveStackMarker(
            methodNode: MethodNode,
            actions: MutableList<() -> Unit>,
            analyzer: FixStackAnalyzer,
            marker: AbstractInsnNode,
            localVariablesManager: LocalVariablesManager
    ) {
        val savedStackValues = analyzer.getStackToSpill(marker)
        if (savedStackValues != null) {
            val savedStackDescriptor = localVariablesManager.allocateVariablesForSaveStackMarker(marker, savedStackValues)
            actions.add { saveStack(methodNode, marker, savedStackDescriptor) }
        }
        else {
            // marker is dead code
            localVariablesManager.allocateVariablesForSaveStackMarker(marker, emptyList())
            actions.add { methodNode.instructions.remove(marker) }
        }
    }

    private fun transformRestoreStackMarker(
            methodNode: MethodNode,
            actions: MutableList<() -> Unit>,
            marker: AbstractInsnNode,
            localVariablesManager: LocalVariablesManager
    ) {
        val savedStackDescriptor = localVariablesManager.getSavedStackDescriptor(marker)
        actions.add { restoreStack(methodNode, marker, savedStackDescriptor) }
        localVariablesManager.markRestoreStackMarkerEmitted(marker)
    }

    private fun transformAfterInlineCallMarker(
            methodNode: MethodNode,
            actions: MutableList<() -> Unit>,
            analyzer: FixStackAnalyzer,
            inlineMarker: AbstractInsnNode,
            localVariablesManager: LocalVariablesManager
    ) {
        val savedStackDescriptor = localVariablesManager.getBeforeInlineDescriptor(inlineMarker)
        val stackContentAfterInline = analyzer.getActualStack(inlineMarker)
        if (stackContentAfterInline != null && savedStackDescriptor.isNotEmpty()) {
            when (stackContentAfterInline.size) {
                1 -> {
                    val returnValue = stackContentAfterInline.last()
                    val returnValueLocalVarIndex = localVariablesManager.createReturnValueVariable(returnValue)
                    actions.add {
                        restoreStackWithReturnValue(methodNode, inlineMarker, savedStackDescriptor,
                                                    returnValue, returnValueLocalVarIndex
                        )
                    }
                }
                0 ->
                    actions.add { restoreStack(methodNode, inlineMarker, savedStackDescriptor) }
                else ->
                    throw AssertionError("Inline method should not leave more than 1 value on stack")
            }
        }
        else {
            // after inline marker is dead code
            actions.add { methodNode.instructions.remove(inlineMarker) }
        }
        localVariablesManager.markAfterInlineMarkerEmitted(inlineMarker)
    }

    private fun transformBeforeInlineCallMarker(
            methodNode: MethodNode,
            actions: MutableList<() -> Unit>,
            analyzer: FixStackAnalyzer,
            inlineMarker: AbstractInsnNode,
            localVariablesManager: LocalVariablesManager
    ) {
        val savedStackValues = analyzer.getStackToSpill(inlineMarker)
        if (savedStackValues != null) {
            val savedStackDescriptor = localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, savedStackValues)
            actions.add { saveStack(methodNode, inlineMarker, savedStackDescriptor) }
        }
        else {
            // before inline marker is dead code
            localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, emptyList())
            actions.add { methodNode.instructions.remove(inlineMarker) }
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy