JVM use new stack state analyzer in non-local returns preprocessing

This commit is contained in:
Dmitry Petrov
2021-07-21 15:27:20 +03:00
committed by teamcityserver
parent cf2b4dd277
commit aef9701661
2 changed files with 55 additions and 52 deletions
@@ -17,8 +17,8 @@ import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.*
import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.fixStack.HackedFixStackMethodAnalyzerBase
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
@@ -36,8 +36,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.LocalVariablesSorter import org.jetbrains.org.objectweb.asm.commons.LocalVariablesSorter
import org.jetbrains.org.objectweb.asm.commons.MethodRemapper import org.jetbrains.org.objectweb.asm.commons.MethodRemapper
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter
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.Frame
import org.jetbrains.org.objectweb.asm.util.Printer import org.jetbrains.org.objectweb.asm.util.Printer
import java.util.* import java.util.*
@@ -57,8 +55,10 @@ class MethodInliner(
) { ) {
private val languageVersionSettings = inliningContext.state.languageVersionSettings private val languageVersionSettings = inliningContext.state.languageVersionSettings
private val invokeCalls = ArrayList<InvokeCall>() private val invokeCalls = ArrayList<InvokeCall>()
//keeps order //keeps order
private val transformations = ArrayList<TransformationInfo>() private val transformations = ArrayList<TransformationInfo>()
//current state //current state
private val currentTypeMapping = HashMap<String, String?>() private val currentTypeMapping = HashMap<String, String?>()
private val result = InlineResult.create() private val result = InlineResult.create()
@@ -273,7 +273,8 @@ class MethodInliner(
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift) val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
//TODO add skipped this and receiver //TODO add skipped this and receiver
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift) val lambdaResult =
inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
result.mergeWithNotChangeInfo(lambdaResult) result.mergeWithNotChangeInfo(lambdaResult)
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages) result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages) result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages)
@@ -303,7 +304,7 @@ class MethodInliner(
} else capturedParamDesc } else capturedParamDesc
visitFieldInsn( visitFieldInsn(
Opcodes.GETSTATIC, realDesc.containingLambdaName, Opcodes.GETSTATIC, realDesc.containingLambdaName,
FieldRemapper.foldName(realDesc.fieldName), realDesc.type.descriptor foldName(realDesc.fieldName), realDesc.type.descriptor
) )
} }
super.visitMethodInsn(opcode, info.newClassName, name, info.newConstructorDescriptor, itf) super.visitMethodInsn(opcode, info.newClassName, name, info.newConstructorDescriptor, itf)
@@ -441,7 +442,7 @@ class MethodInliner(
else -> "" else -> ""
} }
val varName = if (!varSuffix.isEmpty() && name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name val varName = if (varSuffix.isNotEmpty() && name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)) super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index))
} }
} }
@@ -533,33 +534,37 @@ class MethodInliner(
cur.opcode == Opcodes.GETSTATIC -> { cur.opcode == Opcodes.GETSTATIC -> {
val fieldInsnNode = cur as FieldInsnNode? val fieldInsnNode = cur as FieldInsnNode?
val className = fieldInsnNode!!.owner val className = fieldInsnNode!!.owner
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) { when {
recordTransformation( isAnonymousSingletonLoad(className, fieldInsnNode.name) -> {
AnonymousObjectTransformationInfo( recordTransformation(
className, awaitClassReification, isAlreadyRegenerated(className), true, AnonymousObjectTransformationInfo(
inliningContext.nameGenerator className, awaitClassReification, isAlreadyRegenerated(className), true,
inliningContext.nameGenerator
)
) )
) awaitClassReification = false
awaitClassReification = false }
} else if (isWhenMappingAccess(className, fieldInsnNode.name)) { isWhenMappingAccess(className, fieldInsnNode.name) -> {
recordTransformation( recordTransformation(
WhenMappingTransformationInfo( WhenMappingTransformationInfo(
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
)
) )
) }
} else if (fieldInsnNode.isCheckAssertionsStatus()) { fieldInsnNode.isCheckAssertionsStatus() -> {
fieldInsnNode.owner = inlineCallSiteInfo.ownerClassName fieldInsnNode.owner = inlineCallSiteInfo.ownerClassName
when { when {
// In inline function itself: // In inline function itself:
inliningContext.parent == null -> inliningContext inliningContext.parent == null -> inliningContext
// In method of regenerated object - field should already exist: // In method of regenerated object - field should already exist:
inliningContext.parent is RegeneratedClassContext -> inliningContext.parent inliningContext.parent is RegeneratedClassContext -> inliningContext.parent
// In lambda inlined into the root function: // In lambda inlined into the root function:
inliningContext.parent.parent == null -> inliningContext.parent inliningContext.parent.parent == null -> inliningContext.parent
// In lambda inlined into a method of a regenerated object: // In lambda inlined into a method of a regenerated object:
else -> inliningContext.parent.parent as? RegeneratedClassContext else -> inliningContext.parent.parent as? RegeneratedClassContext
?: throw AssertionError("couldn't find class for \$assertionsDisabled (context = $inliningContext)") ?: throw AssertionError("couldn't find class for \$assertionsDisabled (context = $inliningContext)")
}.generateAssertField = true }.generateAssertField = true
}
} }
} }
@@ -588,7 +593,7 @@ class MethodInliner(
assert(lambdaInfo.lambdaClassType.internalName == nodeRemapper.originalLambdaInternalName) { assert(lambdaInfo.lambdaClassType.internalName == nodeRemapper.originalLambdaInternalName) {
"Wrong bytecode template for contract template: ${lambdaInfo.lambdaClassType.internalName} != ${nodeRemapper.originalLambdaInternalName}" "Wrong bytecode template for contract template: ${lambdaInfo.lambdaClassType.internalName} != ${nodeRemapper.originalLambdaInternalName}"
} }
fieldInsn.name = FieldRemapper.foldName(fieldInsn.name) fieldInsn.name = foldName(fieldInsn.name)
fieldInsn.opcode = Opcodes.PUTSTATIC fieldInsn.opcode = Opcodes.PUTSTATIC
toDelete.addAll(stackTransformations) toDelete.addAll(stackTransformations)
} }
@@ -742,7 +747,7 @@ class MethodInliner(
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node) ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
} }
val frames = analyzeMethodNodeWithInterpreter(node, BasicInterpreter()) val frames = HackedFixStackMethodAnalyzerBase("<fake>", node, FixStackInterpreter()).analyze()
val localReturnsNormalizer = LocalReturnsNormalizer() val localReturnsNormalizer = LocalReturnsNormalizer()
@@ -831,9 +836,9 @@ class MethodInliner(
if (inliningContext.isInliningLambda && inliningContext.lambdaInfo is IrExpressionLambda && !inliningContext.parent!!.isInliningLambda) { if (inliningContext.isInliningLambda && inliningContext.lambdaInfo is IrExpressionLambda && !inliningContext.parent!!.isInliningLambda) {
val capturedVars = inliningContext.lambdaInfo.capturedVars val capturedVars = inliningContext.lambdaInfo.capturedVars
var offset = parameters.realParametersSizeOnStack var offset = parameters.realParametersSizeOnStack
val map = capturedVars.map { val map = capturedVars.associate {
offset to it.also { offset += it.type.size } offset to it.also { offset += it.type.size }
}.toMap() }
var cur: AbstractInsnNode? = node.instructions.first var cur: AbstractInsnNode? = node.instructions.first
while (cur != null) { while (cur != null) {
@@ -878,6 +883,7 @@ class MethodInliner(
} }
} }
@Suppress("SameParameterValue")
private fun wrapException(originalException: Throwable, node: MethodNode, errorSuffix: String): RuntimeException { private fun wrapException(originalException: Throwable, node: MethodNode, errorSuffix: String): RuntimeException {
return if (originalException is InlineException) { return if (originalException is InlineException) {
InlineException("$errorPrefix: $errorSuffix", originalException) InlineException("$errorPrefix: $errorSuffix", originalException)
@@ -890,7 +896,7 @@ class MethodInliner(
private class LocalReturn( private class LocalReturn(
private val returnInsn: AbstractInsnNode, private val returnInsn: AbstractInsnNode,
private val insertBeforeInsn: AbstractInsnNode, private val insertBeforeInsn: AbstractInsnNode,
private val frame: Frame<BasicValue> private val frame: Frame<FixStackValue>
) { ) {
fun transform(insnList: InsnList, returnVariableIndex: Int) { fun transform(insnList: InsnList, returnVariableIndex: Int) {
@@ -901,22 +907,19 @@ class MethodInliner(
if (expectedStackSize == actualStackSize) return if (expectedStackSize == actualStackSize) return
var stackSize = actualStackSize var stackSize = actualStackSize
val topValue = frame.getStack(stackSize - 1)
if (isReturnWithValue) { if (isReturnWithValue) {
val storeOpcode = Opcodes.ISTORE + returnInsn.opcode - Opcodes.IRETURN insnList.insertBefore(insertBeforeInsn, VarInsnNode(topValue.storeOpcode, returnVariableIndex))
insnList.insertBefore(insertBeforeInsn, VarInsnNode(storeOpcode, returnVariableIndex))
stackSize-- stackSize--
} }
while (stackSize > 0) { while (stackSize > 0) {
val stackElementSize = frame.getStack(stackSize - 1).size insnList.insertBefore(insertBeforeInsn, InsnNode(frame.getStack(stackSize - 1).popOpcode))
val popOpcode = if (stackElementSize == 1) Opcodes.POP else Opcodes.POP2
insnList.insertBefore(insertBeforeInsn, InsnNode(popOpcode))
stackSize-- stackSize--
} }
if (isReturnWithValue) { if (isReturnWithValue) {
val loadOpcode = Opcodes.ILOAD + returnInsn.opcode - Opcodes.IRETURN insnList.insertBefore(insertBeforeInsn, VarInsnNode(topValue.loadOpcode, returnVariableIndex))
insnList.insertBefore(insertBeforeInsn, VarInsnNode(loadOpcode, returnVariableIndex))
} }
} }
} }
@@ -926,10 +929,10 @@ class MethodInliner(
private var returnVariableSize = 0 private var returnVariableSize = 0
private var returnOpcode = -1 private var returnOpcode = -1
internal fun addLocalReturnToTransform( fun addLocalReturnToTransform(
returnInsn: AbstractInsnNode, returnInsn: AbstractInsnNode,
insertBeforeInsn: AbstractInsnNode, insertBeforeInsn: AbstractInsnNode,
sourceValueFrame: Frame<BasicValue> sourceValueFrame: Frame<FixStackValue>
) { ) {
assert(isReturnOpcode(returnInsn.opcode)) { "return instruction expected" } assert(isReturnOpcode(returnInsn.opcode)) { "return instruction expected" }
assert(returnOpcode < 0 || returnOpcode == returnInsn.opcode) { "Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.opcode] } assert(returnOpcode < 0 || returnOpcode == returnInsn.opcode) { "Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.opcode] }
@@ -938,11 +941,7 @@ class MethodInliner(
localReturns.add(LocalReturn(returnInsn, insertBeforeInsn, sourceValueFrame)) localReturns.add(LocalReturn(returnInsn, insertBeforeInsn, sourceValueFrame))
if (returnInsn.opcode != Opcodes.RETURN) { if (returnInsn.opcode != Opcodes.RETURN) {
returnVariableSize = if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) { returnVariableSize = if (returnInsn.opcode == Opcodes.LRETURN || returnInsn.opcode == Opcodes.DRETURN) 2 else 1
2
} else {
1
}
} }
} }
@@ -94,7 +94,11 @@ internal open class HackedFixStackMethodAnalyzerBase<V : Value>(
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
visitNopInsn(f, insn) visitNopInsn(f, insn)
} else { } else {
current.init(f).execute(insnNode, interpreter) current.init(f)
if (insnOpcode != Opcodes.RETURN) {
// Don't care about possibly incompatible return type
current.execute(insnNode, interpreter)
}
when { when {
insnNode is JumpInsnNode -> insnNode is JumpInsnNode ->