diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt index 31894aff0cc..fddcbe7fb30 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker import org.jetbrains.kotlin.codegen.inline.isMarkedReturn import org.jetbrains.kotlin.codegen.optimization.common.FlexibleMethodAnalyzer -import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.org.objectweb.asm.Opcodes @@ -30,7 +29,6 @@ import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode import org.jetbrains.org.objectweb.asm.tree.LabelNode import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException -import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter import kotlin.math.max @@ -50,9 +48,14 @@ internal class FixStackAnalyzer( val maxExtraStackSize: Int get() = analyzer.maxExtraStackSize - fun getStackToSpill(location: AbstractInsnNode) = analyzer.spilledStacks[location] - fun getActualStack(location: AbstractInsnNode) = getFrame(location)?.getStackContent() - fun getActualStackSize(location: AbstractInsnNode) = getFrame(location)?.stackSizeWithExtra ?: DEAD_CODE_STACK_SIZE + fun getStackToSpill(location: AbstractInsnNode): List? = + analyzer.spilledStacks[location] + + fun getActualStack(location: AbstractInsnNode): List? = + getFrame(location)?.getStackContent() + + fun getActualStackSize(location: AbstractInsnNode): Int = + getFrame(location)?.stackSizeWithExtra ?: DEAD_CODE_STACK_SIZE fun getExpectedStackSize(location: AbstractInsnNode): Int { // We should look for expected stack size at loop entry point markers if available, @@ -90,9 +93,9 @@ internal class FixStackAnalyzer( private val analyzer = InternalAnalyzer(owner) private inner class InternalAnalyzer(owner: String) : - FlexibleMethodAnalyzer(owner, method, OptimizationBasicInterpreter()) { + FlexibleMethodAnalyzer(owner, method, FixStackInterpreter()) { - val spilledStacks = hashMapOf>() + val spilledStacks = hashMapOf>() var maxExtraStackSize = 0; private set override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean { @@ -101,15 +104,15 @@ internal class FixStackAnalyzer( return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode)) } - override fun newFrame(nLocals: Int, nStack: Int): Frame = + override fun newFrame(nLocals: Int, nStack: Int): Frame = FixStackFrame(nLocals, nStack) private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node) - inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { - val extraStack = Stack() + inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { + val extraStack = Stack() - override fun init(src: Frame): Frame { + override fun init(src: Frame): Frame { extraStack.clear() extraStack.addAll((src as FixStackFrame).extraStack) return super.init(src) @@ -120,7 +123,7 @@ internal class FixStackAnalyzer( super.clearStack() } - override fun execute(insn: AbstractInsnNode, interpreter: Interpreter) { + override fun execute(insn: AbstractInsnNode, interpreter: Interpreter) { when { PseudoInsn.SAVE_STACK_BEFORE_TRY.isa(insn) -> executeSaveStackBeforeTry(insn) @@ -141,14 +144,14 @@ internal class FixStackAnalyzer( val stackSizeWithExtra: Int get() = super.getStackSize() + extraStack.size - fun getStackContent(): List { - val savedStack = arrayListOf() + fun getStackContent(): List { + val savedStack = ArrayList() IntRange(0, super.getStackSize() - 1).mapTo(savedStack) { super.getStack(it) } savedStack.addAll(extraStack) return savedStack } - override fun push(value: BasicValue) { + override fun push(value: FixStackValue?) { if (super.getStackSize() < maxStackSize) { super.push(value) } else { @@ -157,19 +160,18 @@ internal class FixStackAnalyzer( } } - fun pushAll(values: Collection) { + fun pushAll(values: Collection) { values.forEach { push(it) } } - override fun pop(): BasicValue { - return if (extraStack.isNotEmpty()) { + override fun pop(): FixStackValue? = + if (extraStack.isNotEmpty()) { extraStack.pop() } else { super.pop() } - } - override fun setStack(i: Int, value: BasicValue) { + override fun setStack(i: Int, value: FixStackValue) { if (i < super.getMaxStackSize()) { super.setStack(i, value) } else { @@ -177,7 +179,7 @@ internal class FixStackAnalyzer( } } - override fun merge(frame: Frame, interpreter: Interpreter): Boolean { + override fun merge(frame: Frame, interpreter: Interpreter): Boolean { val other = frame as FixStackFrame if (stackSizeWithExtra != other.stackSizeWithExtra) { throw AnalyzerException(null, "Incompatible stack heights") diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackInterpreter.kt new file mode 100644 index 00000000000..80951f0b917 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackInterpreter.kt @@ -0,0 +1,151 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.optimization.fixStack + +import org.jetbrains.kotlin.codegen.inline.insnOpcodeText +import org.jetbrains.org.objectweb.asm.Handle +import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.tree.* +import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter + +open class FixStackInterpreter : Interpreter(API_VERSION) { + + override fun newValue(type: Type?): FixStackValue? = + type?.toFixStackValue() + + override fun newOperation(insn: AbstractInsnNode): FixStackValue? = + when (insn.opcode) { + ACONST_NULL -> + FixStackValue.OBJECT + ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5 -> + FixStackValue.INT + LCONST_0, LCONST_1 -> + FixStackValue.LONG + FCONST_0, FCONST_1, FCONST_2 -> + FixStackValue.FLOAT + DCONST_0, DCONST_1 -> + FixStackValue.DOUBLE + BIPUSH, SIPUSH -> + FixStackValue.INT + LDC -> { + val cst = (insn as LdcInsnNode).cst + if (cst is Int) { + FixStackValue.INT + } else if (cst is Float) { + FixStackValue.FLOAT + } else if (cst is Long) { + FixStackValue.LONG + } else if (cst is Double) { + FixStackValue.DOUBLE + } else if (cst is String) { + FixStackValue.OBJECT + } else if (cst is Type) { + val sort = cst.sort + if (sort == Type.OBJECT || sort == Type.ARRAY || sort == Type.METHOD) { + FixStackValue.OBJECT + } else { + throw IllegalArgumentException("Illegal LDC constant $cst") + } + } else if (cst is Handle) { + FixStackValue.OBJECT + } else { + throw IllegalArgumentException("Illegal LDC constant $cst") + } + } + GETSTATIC -> + newValue(Type.getType((insn as FieldInsnNode).desc)) + NEW -> + newValue(Type.getObjectType((insn as TypeInsnNode).desc)) + else -> + throw IllegalArgumentException("Unexpected instruction: " + insn.insnOpcodeText) + } + + override fun copyOperation(insn: AbstractInsnNode, value: FixStackValue?): FixStackValue? = + value + + override fun binaryOperation(insn: AbstractInsnNode, value1: FixStackValue?, value2: FixStackValue?): FixStackValue? = + when (insn.opcode) { + IALOAD, BALOAD, CALOAD, SALOAD, IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IAND, IOR, IXOR -> + FixStackValue.INT + FALOAD, FADD, FSUB, FMUL, FDIV, FREM -> + FixStackValue.FLOAT + LALOAD, LADD, LSUB, LMUL, LDIV, LREM, LSHL, LSHR, LUSHR, LAND, LOR, LXOR -> + FixStackValue.LONG + DALOAD, DADD, DSUB, DMUL, DDIV, DREM -> + FixStackValue.DOUBLE + AALOAD -> + FixStackValue.OBJECT + LCMP, FCMPL, FCMPG, DCMPL, DCMPG -> + FixStackValue.INT + IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, PUTFIELD -> + null + else -> + throw IllegalArgumentException("Unexpected instruction: " + insn.insnOpcodeText) + } + + override fun ternaryOperation( + insn: AbstractInsnNode, + value1: FixStackValue?, + value2: FixStackValue?, + value3: FixStackValue? + ): FixStackValue? = + null + + override fun naryOperation(insn: AbstractInsnNode, values: List): FixStackValue? = + when (insn.opcode) { + MULTIANEWARRAY -> + newValue(Type.getType((insn as MultiANewArrayInsnNode).desc)) + INVOKEDYNAMIC -> + newValue(Type.getReturnType((insn as InvokeDynamicInsnNode).desc)) + else -> + newValue(Type.getReturnType((insn as MethodInsnNode).desc)) + } + + override fun returnOperation(insn: AbstractInsnNode?, value: FixStackValue?, expected: FixStackValue?) { + } + + override fun unaryOperation(insn: AbstractInsnNode, value: FixStackValue?): FixStackValue? = + when (insn.opcode) { + INEG, IINC, L2I, F2I, D2I, I2B, I2C, I2S -> + FixStackValue.INT + FNEG, I2F, L2F, D2F -> + FixStackValue.FLOAT + LNEG, I2L, F2L, D2L -> + FixStackValue.LONG + DNEG, I2D, L2D, F2D -> + FixStackValue.DOUBLE + IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, PUTSTATIC -> + null + GETFIELD -> + newValue(Type.getType((insn as FieldInsnNode).desc)) + NEWARRAY -> + FixStackValue.OBJECT + ANEWARRAY -> { + FixStackValue.OBJECT + } + ARRAYLENGTH -> + FixStackValue.INT + ATHROW -> + null + CHECKCAST -> + FixStackValue.OBJECT + INSTANCEOF -> + FixStackValue.INT + MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL -> + null + else -> + throw IllegalArgumentException("Unexpected instruction: " + insn.insnOpcodeText) + } + + override fun merge(v: FixStackValue?, w: FixStackValue?): FixStackValue? = + when { + v == w -> v + v == null -> w + w == null -> v + else -> throw AssertionError("Mismatching value kinds: $v != $w") + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackValue.kt new file mode 100644 index 00000000000..589cd661dee --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackValue.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.optimization.fixStack + +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.tree.analysis.Value + +enum class FixStackValue( + private val _size: Int, + val loadOpcode: Int, + val storeOpcode: Int, + val popOpcode: Int +) : Value { + INT(1, Opcodes.ILOAD, Opcodes.ISTORE, Opcodes.POP), + LONG(2, Opcodes.LLOAD, Opcodes.LSTORE, Opcodes.POP2), + FLOAT(1, Opcodes.FLOAD, Opcodes.FSTORE, Opcodes.POP), + DOUBLE(2, Opcodes.DLOAD, Opcodes.DSTORE, Opcodes.POP2), + OBJECT(1, Opcodes.ALOAD, Opcodes.ASTORE, Opcodes.POP) + ; + + override fun getSize(): Int = _size +} + +fun Type.toFixStackValue(): FixStackValue? = + when (this.sort) { + Type.VOID -> null + Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT, Type.INT -> FixStackValue.INT + Type.LONG -> FixStackValue.LONG + Type.FLOAT -> FixStackValue.FLOAT + Type.DOUBLE -> FixStackValue.DOUBLE + Type.OBJECT, Type.ARRAY, Type.METHOD -> FixStackValue.OBJECT + else -> throw AssertionError("Unexpected type: $this") + } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/LocalVariablesManager.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/LocalVariablesManager.kt index b7aace59840..5501ed46220 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/LocalVariablesManager.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/LocalVariablesManager.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen.optimization.fixStack import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.MethodNode -import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import kotlin.math.max internal class LocalVariablesManager(val context: FixStackContext, val methodNode: MethodNode) { @@ -39,7 +38,10 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod methodNode.maxLocals = max(methodNode.maxLocals, newValue) } - fun allocateVariablesForSaveStackMarker(saveStackMarker: AbstractInsnNode, savedStackValues: List): SavedStackDescriptor { + fun allocateVariablesForSaveStackMarker( + saveStackMarker: AbstractInsnNode, + savedStackValues: List + ): SavedStackDescriptor { val numRestoreStackMarkers = context.restoreStackMarkersForSaveMarker[saveStackMarker]!!.size return allocateNewHandle(numRestoreStackMarkers, saveStackMarker, savedStackValues) } @@ -47,12 +49,8 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod private fun allocateNewHandle( numRestoreStackMarkers: Int, saveStackMarker: AbstractInsnNode, - savedStackValues: List + savedStackValues: List ): SavedStackDescriptor { - if (savedStackValues.any { it.type == null }) { - throw AssertionError("Uninitialized value on stack at ${methodNode.instructions.indexOf(saveStackMarker)}") - } - val firstUnusedLocalVarIndex = getFirstUnusedLocalVariableIndex() val savedStackDescriptor = SavedStackDescriptor(savedStackValues, firstUnusedLocalVarIndex) updateMaxLocals(savedStackDescriptor.firstUnusedLocalVarIndex) @@ -78,7 +76,7 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod fun allocateVariablesForBeforeInlineMarker( beforeInlineMarker: AbstractInsnNode, - savedStackValues: List + savedStackValues: List ): SavedStackDescriptor { return allocateNewHandle(1, beforeInlineMarker, savedStackValues) } @@ -101,7 +99,7 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod } } - fun createReturnValueVariable(returnValue: BasicValue): Int { + fun createReturnValueVariable(returnValue: FixStackValue): Int { val returnValueIndex = getFirstUnusedLocalVariableIndex() updateMaxLocals(returnValueIndex + returnValue.size) return returnValueIndex diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt index f2a90f4db1f..0376df0fe67 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt @@ -54,10 +54,10 @@ fun Frame.peekWords(size1: Int, size2: Int): List? { } class SavedStackDescriptor( - val savedValues: List, + val savedValues: List, val firstLocalVarIndex: Int ) { - private val savedValuesSize = savedValues.fold(0, { size, value -> size + value.size }) + private val savedValuesSize = savedValues.fold(0) { size, value -> size + value.size } val firstUnusedLocalVarIndex = firstLocalVarIndex + savedValuesSize override fun toString(): String = @@ -88,13 +88,13 @@ fun restoreStackWithReturnValue( methodNode: MethodNode, nodeToReplace: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor, - returnValue: BasicValue, + returnValue: FixStackValue, returnValueLocalVarIndex: Int ) { with(methodNode.instructions) { - insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ISTORE), returnValueLocalVarIndex)) + insertBefore(nodeToReplace, VarInsnNode(returnValue.storeOpcode, returnValueLocalVarIndex)) generateLoadInstructions(methodNode, nodeToReplace, savedStackDescriptor) - insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ILOAD), returnValueLocalVarIndex)) + insertBefore(nodeToReplace, VarInsnNode(returnValue.loadOpcode, returnValueLocalVarIndex)) remove(nodeToReplace) } } @@ -102,10 +102,7 @@ fun restoreStackWithReturnValue( fun generateLoadInstructions(methodNode: MethodNode, location: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor) { var localVarIndex = savedStackDescriptor.firstLocalVarIndex for (value in savedStackDescriptor.savedValues) { - methodNode.instructions.insertBefore( - location, - VarInsnNode(value.type.getOpcode(Opcodes.ILOAD), localVarIndex) - ) + methodNode.instructions.insertBefore(location, VarInsnNode(value.loadOpcode, localVarIndex)) localVarIndex += value.size } } @@ -114,10 +111,7 @@ fun generateStoreInstructions(methodNode: MethodNode, location: AbstractInsnNode var localVarIndex = savedStackDescriptor.firstUnusedLocalVarIndex for (value in savedStackDescriptor.savedValues.asReversed()) { localVarIndex -= value.size - methodNode.instructions.insertBefore( - location, - VarInsnNode(value.type.getOpcode(Opcodes.ISTORE), localVarIndex) - ) + methodNode.instructions.insertBefore(location, VarInsnNode(value.storeOpcode, localVarIndex)) } } @@ -146,10 +140,10 @@ fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode } } -fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List) { +fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List) { with(methodNode.instructions) { for (stackValue in stackContent.subList(expectedStackSize, stackContent.size)) { - insert(node, getPopInstruction(stackValue)) + insert(node, InsnNode(stackValue.popOpcode)) } remove(node) }