JVM use new stack state analyzer in non-local returns preprocessing
This commit is contained in:
committed by
teamcityserver
parent
cf2b4dd277
commit
aef9701661
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-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 ->
|
||||||
|
|||||||
Reference in New Issue
Block a user