From fc3e9318d9600736668bb198d5b0efd6a55f7161 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 7 Jul 2017 11:03:01 +0300 Subject: [PATCH] Handle DUPnXm instructions in PopBackwardPropagationTransformer --- .../PopBackwardPropagationTransformer.kt | 116 +++++++++++------- .../fixStack/StackTransformationUtils.kt | 25 ++++ 2 files changed, 99 insertions(+), 42 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/PopBackwardPropagationTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/PopBackwardPropagationTransformer.kt index 44bba2a7542..8d29aa3c632 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/PopBackwardPropagationTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/PopBackwardPropagationTransformer.kt @@ -17,8 +17,10 @@ package org.jetbrains.kotlin.codegen.optimization.boxing import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor +import org.jetbrains.kotlin.codegen.optimization.common.debugText import org.jetbrains.kotlin.codegen.optimization.common.isLoadOperation import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful +import org.jetbrains.kotlin.codegen.optimization.fixStack.peekWords import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer @@ -33,17 +35,9 @@ import java.util.* class PopBackwardPropagationTransformer : MethodTransformer() { override fun transform(internalClassName: String, methodNode: MethodNode) { if (!OptimizationMethodVisitor.canBeOptimizedUsingSourceInterpreter(methodNode)) return - if (methodNode.instructions.toArray().any { it.isUnsafeStackInsn() }) return Transformer(methodNode).transform() } - // TODO better stack operations analysis - private fun AbstractInsnNode.isUnsafeStackInsn() = - opcode == Opcodes.DUP_X1 || - opcode == Opcodes.DUP_X2 || - opcode == Opcodes.DUP2_X1 || - opcode == Opcodes.DUP2_X2 - private class Transformer(val methodNode: MethodNode) { private interface Transformation { fun apply(insn: AbstractInsnNode) @@ -82,46 +76,84 @@ class PopBackwardPropagationTransformer : MethodTransformer() { postprocessNops() } - private fun analyzeMethodBody(): Array?> = - Analyzer(object : SourceInterpreter() { - override fun naryOperation(insn: AbstractInsnNode, values: MutableList): SourceValue { - for (value in values) { - value.insns.markAsDontTouch() - } - return super.naryOperation(insn, values) - } + private fun analyzeMethodBody(): Array?> { + val frames = Analyzer(HazardsTrackingInterpreter()).analyze("fake", methodNode) - override fun copyOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue { - value.insns.markAsDontTouch() - return super.copyOperation(insn, value) - } + postprocessDupNxM(frames) - override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue { - if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) { - value.insns.markAsDontTouch() - } - return super.unaryOperation(insn, value) - } + return frames + } - override fun binaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue): SourceValue { - value1.insns.markAsDontTouch() - value2.insns.markAsDontTouch() - return super.binaryOperation(insn, value1, value2) - } + private fun postprocessDupNxM(frames: Array?>) { + val insns = methodNode.instructions.toArray() + for (i in frames.indices) { + val frame = frames[i] ?: continue + val insn = insns[i] - override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue { - value1.insns.markAsDontTouch() - value2.insns.markAsDontTouch() - value3.insns.markAsDontTouch() - return super.ternaryOperation(insn, value1, value2, value3) + when (insn.opcode) { + Opcodes.DUP_X1 -> { + val top2 = frame.peekWords(1, 1) ?: throwIncorrectBytecode(insn, frame) + top2.forEach { it.insns.markAsDontTouch() } } + Opcodes.DUP2_X1 -> { + val top3 = frame.peekWords(2, 1) ?: throwIncorrectBytecode(insn, frame) + top3.forEach { it.insns.markAsDontTouch() } + } + Opcodes.DUP_X2 -> { + val top3 = frame.peekWords(1, 2) ?: throwIncorrectBytecode(insn, frame) + top3.forEach { it.insns.markAsDontTouch() } + } + Opcodes.DUP2_X2 -> { + val top4 = frame.peekWords(2, 2) ?: throwIncorrectBytecode(insn, frame) + top4.forEach { it.insns.markAsDontTouch() } + } + } + } + } - private fun Collection.markAsDontTouch() { - forEach { - dontTouchInsnIndices[insnList.indexOf(it)] = true - } - } - }).analyze("fake", methodNode) + private fun throwIncorrectBytecode(insn: AbstractInsnNode?, frame: Frame): Nothing { + throw AssertionError("Incorrect bytecode at ${methodNode.instructions.indexOf(insn)}: ${insn.debugText} $frame") + } + + private inner class HazardsTrackingInterpreter : SourceInterpreter() { + override fun naryOperation(insn: AbstractInsnNode, values: MutableList): SourceValue { + for (value in values) { + value.insns.markAsDontTouch() + } + return super.naryOperation(insn, values) + } + + override fun copyOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue { + value.insns.markAsDontTouch() + return super.copyOperation(insn, value) + } + + override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue { + if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) { + value.insns.markAsDontTouch() + } + return super.unaryOperation(insn, value) + } + + override fun binaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue): SourceValue { + value1.insns.markAsDontTouch() + value2.insns.markAsDontTouch() + return super.binaryOperation(insn, value1, value2) + } + + override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue { + value1.insns.markAsDontTouch() + value2.insns.markAsDontTouch() + value3.insns.markAsDontTouch() + return super.ternaryOperation(insn, value1, value2, value3) + } + } + + private fun Collection.markAsDontTouch() { + forEach { + dontTouchInsnIndices[insnList.indexOf(it)] = true + } + } private fun computeTransformations() { 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 e98c4c720a4..001fdd93c8e 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 @@ -28,6 +28,31 @@ fun Frame.top(): V? = fun Frame.peek(offset: Int): V? = if (stackSize > offset) getStack(stackSize - offset - 1) else null +private fun Frame.peekWordsTo(dest: MutableList, size: Int, offset0: Int = 0): Int { + var offset = offset0 + var totalSize = 0 + while (totalSize < size) { + val value = peek(offset++) ?: return -1 + dest.add(value) + totalSize += value.size + } + if (totalSize > size) return -1 + return offset +} + +fun Frame.peekWords(size: Int): List? { + val result = ArrayList(size) + return if (peekWordsTo(result, size) < 0) null else result +} + +fun Frame.peekWords(size1: Int, size2: Int): List? { + val result = ArrayList(size1 + size2) + val offset = peekWordsTo(result, size1) + if (offset < 0) return null + if (peekWordsTo(result, size2, offset) < 0) return null + return result +} + class SavedStackDescriptor( val savedValues: List, val firstLocalVarIndex: Int