Replace ALOAD 0 with fake continuation if suspension point is default
method #KT-25922 Fixed #KT-26608 Fixed #KT-26658 Fixed
This commit is contained in:
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
@@ -23,7 +24,7 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -640,26 +641,78 @@ class MethodInliner(
|
||||
private fun replaceContinuationAccessesWithFakeContinuationsIfNeeded(processingNode: MethodNode) {
|
||||
val lambdaInfo = inliningContext.lambdaInfo ?: return
|
||||
if (!lambdaInfo.invokeMethodDescriptor.isSuspend) return
|
||||
val sources = analyzeMethodNodeBeforeInline(processingNode)
|
||||
val cfg = ControlFlowGraph.build(processingNode)
|
||||
val aload0s = processingNode.instructions.asSequence().filter { it.opcode == Opcodes.ALOAD && it.safeAs<VarInsnNode>()?.`var` == 0 }
|
||||
// Expected pattern here:
|
||||
// ALOAD 0
|
||||
// ICONST_0
|
||||
// INVOKESTATIC InlineMarker.mark
|
||||
// INVOKE* suspendingFunction(..., Continuation;)Ljava/lang/Object;
|
||||
val continuationAsParameterAload0s =
|
||||
aload0s.filter { it.next?.next?.let(::isBeforeSuspendMarker) == true && isSuspendCall(it.next?.next?.next) }
|
||||
replaceContinuationsWithFakeOnes(continuationAsParameterAload0s, processingNode)
|
||||
|
||||
val visited = hashSetOf<AbstractInsnNode>()
|
||||
fun findMeaningfulSuccs(insn: AbstractInsnNode): Collection<AbstractInsnNode> {
|
||||
if (!visited.add(insn)) return emptySet()
|
||||
val res = hashSetOf<AbstractInsnNode>()
|
||||
for (succIndex in cfg.getSuccessorsIndices(insn)) {
|
||||
val succ = processingNode.instructions[succIndex]
|
||||
if (succ.isMeaningful) res.add(succ)
|
||||
else res.addAll(findMeaningfulSuccs(succ))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// After inlining suspendCoroutineUninterceptedOrReturn there will be suspension point, which is not a MethodInsnNode.
|
||||
// So, it is incorrect to expect MethodInsnNodes only
|
||||
val suspensionPoints = processingNode.instructions.asSequence()
|
||||
.filter { isBeforeSuspendMarker(it) }
|
||||
.flatMap { findMeaningfulSuccs(it).asSequence() }
|
||||
.filter { it is MethodInsnNode }
|
||||
|
||||
val toReplace = hashSetOf<AbstractInsnNode>()
|
||||
for (suspensionPoint in suspensionPoints) {
|
||||
assert(suspensionPoint is MethodInsnNode) {
|
||||
"suspensionPoint shall be MethodInsnNode, but instead $suspensionPoint"
|
||||
}
|
||||
suspensionPoint as MethodInsnNode
|
||||
assert(Type.getReturnType(suspensionPoint.desc) == OBJECT_TYPE) {
|
||||
"suspensionPoint shall return $OBJECT_TYPE, but returns ${Type.getReturnType(suspensionPoint.desc)}"
|
||||
}
|
||||
val frame = sources[processingNode.instructions.indexOf(suspensionPoint)] ?: continue
|
||||
val paramTypes = Type.getArgumentTypes(suspensionPoint.desc)
|
||||
if (suspensionPoint.name.endsWith(JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX)) {
|
||||
// Expected pattern here:
|
||||
// ALOAD 0
|
||||
// (ICONST or other integers creating instruction)
|
||||
// (ACONST_NULL or ALOAD)
|
||||
// ICONST_0
|
||||
// INVOKESTATIC InlineMarker.mark
|
||||
// INVOKE* suspendingFunction$default(..., Continuation;ILjava/lang/Object)Ljava/lang/Object;
|
||||
assert(paramTypes.size >= 3) {
|
||||
"${suspensionPoint.name}${suspensionPoint.desc} shall have 3+ parameters"
|
||||
}
|
||||
} else {
|
||||
// Expected pattern here:
|
||||
// ALOAD 0
|
||||
// ICONST_0
|
||||
// INVOKESTATIC InlineMarker.mark
|
||||
// INVOKE* suspendingFunction(..., Continuation;)Ljava/lang/Object;
|
||||
assert(paramTypes.isNotEmpty()) {
|
||||
"${suspensionPoint.name}${suspensionPoint.desc} shall have 1+ parameters"
|
||||
}
|
||||
}
|
||||
paramTypes.reversed().asSequence().withIndex()
|
||||
.filter { it.value == languageVersionSettings.continuationAsmType() || it.value == OBJECT_TYPE }
|
||||
.flatMap { frame.getStack(frame.stackSize - it.index - 1).insns.asSequence() }
|
||||
.filter { it in aload0s }.let { toReplace.addAll(it) }
|
||||
}
|
||||
|
||||
// Expected pattern here:
|
||||
// ALOAD 0
|
||||
// ASTORE N
|
||||
// This pattern may occur after multiple inlines
|
||||
val continuationToStoreAload0s = aload0s.filter { it.next?.opcode == Opcodes.ASTORE }
|
||||
replaceContinuationsWithFakeOnes(continuationToStoreAload0s, processingNode)
|
||||
// Note, that this is not a suspension point, thus we check it separately
|
||||
toReplace.addAll(aload0s.filter { it.next?.opcode == Opcodes.ASTORE })
|
||||
// Expected pattern here:
|
||||
// ALOAD 0
|
||||
// INVOKEINTERFACE kotlin/jvm/functions/FunctionN.invoke (...,Ljava/lang/Object;)Ljava/lang/Object;
|
||||
val continuationAsLambdaParameterAload0s = aload0s.filter { isLambdaCall(it.next) }
|
||||
replaceContinuationsWithFakeOnes(continuationAsLambdaParameterAload0s, processingNode)
|
||||
toReplace.addAll(aload0s.filter { isLambdaCall(it.next) })
|
||||
replaceContinuationsWithFakeOnes(toReplace, processingNode)
|
||||
}
|
||||
|
||||
private fun isLambdaCall(invoke: AbstractInsnNode?): Boolean {
|
||||
@@ -672,7 +725,7 @@ class MethodInliner(
|
||||
}
|
||||
|
||||
private fun replaceContinuationsWithFakeOnes(
|
||||
continuations: Sequence<AbstractInsnNode>,
|
||||
continuations: Collection<AbstractInsnNode>,
|
||||
node: MethodNode
|
||||
) {
|
||||
for (toReplace in continuations) {
|
||||
|
||||
Reference in New Issue
Block a user