diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index 7ae84730aae..2cc92d1d220 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -33,6 +33,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.LocalVariablesSorter import org.jetbrains.org.objectweb.asm.commons.MethodRemapper import org.jetbrains.org.objectweb.asm.tree.* +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.util.Printer import java.util.* @@ -468,9 +469,11 @@ class MethodInliner( val toDelete = SmartSet.create() - val sources = analyzeMethodNodeWithInterpreter(processingNode, FunctionalArgumentInterpreter(this, toDelete)) + val sources = analyzeMethodNodeWithInterpreter(processingNode, FunctionalArgumentInterpreter(this)) val instructions = processingNode.instructions + markObsoleteInstruction(instructions, toDelete, sources) + var awaitClassReification = false var currentFinallyDeep = 0 @@ -622,6 +625,41 @@ class MethodInliner( return processingNode } + private fun MethodInliner.markObsoleteInstruction( + instructions: InsnList, + toDelete: SmartSet, + sources: Array?> + ) { + for (insn in instructions) { + // Parameter checks are processed separately + if (insn.isAloadBeforeCheckParameterIsNotNull()) continue + val functionalArgument = getFunctionalArgumentIfExists(insn) + if (functionalArgument is LambdaInfo) { + toDelete.add(insn) + } else { + when (insn.opcode) { + Opcodes.ASTORE -> { + if (sources[instructions.indexOf(insn)]?.top().functionalArgument is LambdaInfo) { + toDelete.add(insn) + } + } + Opcodes.SWAP -> { + if (sources[instructions.indexOf(insn)]?.peek(0).functionalArgument is LambdaInfo || + sources[instructions.indexOf(insn)]?.peek(1).functionalArgument is LambdaInfo + ) { + toDelete.add(insn) + } + } + Opcodes.ALOAD -> { + if (sources[instructions.indexOf(insn)]?.getLocal((insn as VarInsnNode).`var`).functionalArgument is LambdaInfo) { + toDelete.add(insn) + } + } + } + } + } + } + // Replace ALOAD 0 // with // ICONST fakeContinuationMarker diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt index aae6878b289..6630cfdc8fd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt @@ -62,48 +62,32 @@ internal class FunctionalArgumentValue( val BasicValue?.functionalArgument get() = (this as? FunctionalArgumentValue)?.functionalArgument -internal class FunctionalArgumentInterpreter( - private val inliner: MethodInliner, private val toDelete: MutableSet -) : BasicInterpreter(Opcodes.API_VERSION) { - +internal class FunctionalArgumentInterpreter(private val inliner: MethodInliner) : BasicInterpreter(Opcodes.API_VERSION) { override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? = - markInstructionIfNeeded(insn, super.unaryOperation(insn, value)) + wrapArgumentInValueIfNeeded(insn, super.unaryOperation(insn, value)) - override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { - val basicValue = super.copyOperation(insn, value) - // Parameter checks are processed separately - if (insn.next?.opcode == Opcodes.LDC && insn.next?.next?.isCheckParameterIsNotNull() == true) { - return basicValue - } - if (value.functionalArgument is LambdaInfo) { - // SWAP and ASTORE - toDelete.add(insn) - return value - } - return markInstructionIfNeeded(insn, basicValue) - } - - private fun markInstructionIfNeeded( - insn: AbstractInsnNode, - basicValue: BasicValue? - ): BasicValue? { - val functionalArgument = inliner.getFunctionalArgumentIfExists(insn) - return if (functionalArgument != null) { - if (functionalArgument is LambdaInfo) { - toDelete.add(insn) - } - FunctionalArgumentValue(functionalArgument, basicValue) - } else basicValue - } + override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? = + wrapArgumentInValueIfNeeded(insn, super.copyOperation(insn, value)) override fun newOperation(insn: AbstractInsnNode): BasicValue? = - markInstructionIfNeeded(insn, super.newOperation(insn)) + wrapArgumentInValueIfNeeded(insn, super.newOperation(insn)) + + private fun wrapArgumentInValueIfNeeded( + insn: AbstractInsnNode, + basicValue: BasicValue? + ): BasicValue? = + inliner.getFunctionalArgumentIfExists(insn) + ?.let { FunctionalArgumentValue(it, basicValue) } + ?: basicValue override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? = if (v is FunctionalArgumentValue && w is FunctionalArgumentValue && v.functionalArgument == w.functionalArgument) v else super.merge(v, w) } +internal fun AbstractInsnNode.isAloadBeforeCheckParameterIsNotNull(): Boolean = + opcode == Opcodes.ALOAD && next?.opcode == Opcodes.LDC && next?.next?.isCheckParameterIsNotNull() == true + // Interpreter, that analyzes only ALOAD_0s, which are used as continuation arguments internal class Aload0BasicValue private constructor(val indices: Set) : BasicValue(AsmTypes.OBJECT_TYPE) {