Also check predecessors of PUSH Unit in unit tail-call optimization
This commit is contained in:
+140
-118
@@ -111,13 +111,13 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
addCompletionParameterToLVT(methodNode)
|
addCompletionParameterToLVT(methodNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
val examined = ExaminedMethodNode(
|
val examiner = MethodNodeExaminer(
|
||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
containingClassInternalName,
|
containingClassInternalName,
|
||||||
methodNode
|
methodNode
|
||||||
)
|
)
|
||||||
if (examined.allSuspensionPointsAreTailCalls(suspensionPoints)) {
|
if (examiner.allSuspensionPointsAreTailCalls(suspensionPoints)) {
|
||||||
examined.replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks()
|
examiner.replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks()
|
||||||
dropSuspensionMarkers(methodNode, suspensionPoints)
|
dropSuspensionMarkers(methodNode, suspensionPoints)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -821,62 +821,80 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO Use this in variable liveness analysis
|
// TODO Use this in variable liveness analysis
|
||||||
private class ExaminedMethodNode(
|
private class MethodNodeExaminer(
|
||||||
val languageVersionSettings: LanguageVersionSettings,
|
val languageVersionSettings: LanguageVersionSettings,
|
||||||
val containingClassInternalName: String,
|
val containingClassInternalName: String,
|
||||||
val methodNode: MethodNode
|
val methodNode: MethodNode
|
||||||
) {
|
) {
|
||||||
// DO NOT REORDER: pops and areturns collecting depends on unit instances collecting
|
private val sourceFrames: Array<Frame<SourceValue>?> =
|
||||||
// Which, in turn depends on frames and cfg
|
|
||||||
val sourceFrames: Array<Frame<SourceValue>?> =
|
|
||||||
MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter())
|
MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter())
|
||||||
val controlFlowGraph = ControlFlowGraph.build(methodNode)
|
private val controlFlowGraph = ControlFlowGraph.build(methodNode)
|
||||||
|
|
||||||
private val safeUnitInstances = collectSafeUnitInstances()
|
private val safeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||||
private val popsBeforeSafeUnitInstances = collectPopsBeforeSafeUnitInstances()
|
private val popsBeforeSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||||
private val areturnsAfterSafeUnitInstances = collectAreturnsAfterSafeUnitInstances()
|
private val areturnsAfterSafeUnitInstances = mutableSetOf<AbstractInsnNode>()
|
||||||
|
private val meaningfulSuccessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||||
|
private val meaningfulPredecessorsCache = hashMapOf<AbstractInsnNode, List<AbstractInsnNode>>()
|
||||||
|
|
||||||
private fun collectSafeUnitInstances() = methodNode.instructions.asSequence().filter { unit ->
|
init {
|
||||||
unit.isUnitInstance() &&
|
// retrieve all POP insns
|
||||||
methodNode.instructions.asSequence().any { insn ->
|
val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }
|
||||||
insn.opcode == Opcodes.POP && insn.meaningfulSuccessors().let { succs ->
|
// for each of them check that all successors are PUSH Unit
|
||||||
succs.all { it.isUnitInstance() } && unit in succs
|
val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() }
|
||||||
}
|
.filter { (_, succs) -> succs.all { it.isUnitInstance() } }
|
||||||
} && unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN }
|
.map { it.first }.toList()
|
||||||
}.toList()
|
for (pop in popsBeforeUnitInstances) {
|
||||||
|
val units = pop.meaningfulSuccessors()
|
||||||
private fun collectPopsBeforeSafeUnitInstances() = methodNode.instructions.asSequence().filter { pop ->
|
val allUnitsAreSafe = units.all { unit ->
|
||||||
pop.opcode == Opcodes.POP && pop.meaningfulSuccessors().all { it.isSafeUnitInstance() }
|
// check no other predecessor exists
|
||||||
}.toList()
|
unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } &&
|
||||||
|
// check they have only returns among successors
|
||||||
private fun collectAreturnsAfterSafeUnitInstances() = methodNode.instructions.asSequence().filter { areturn ->
|
unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN }
|
||||||
areturn.opcode == Opcodes.ARETURN && sourceFrames[areturn.index()]?.top()?.insns?.all { it.isSafeUnitInstance() } == true
|
}
|
||||||
|
if (!allUnitsAreSafe) continue
|
||||||
|
// save them all to the properties
|
||||||
|
popsBeforeSafeUnitInstances += pop
|
||||||
|
safeUnitInstances += units
|
||||||
|
units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this)
|
private fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this)
|
||||||
|
|
||||||
// GETSTATIC kotlin/Unit.INSTANCE is considered safe iff
|
// GETSTATIC kotlin/Unit.INSTANCE is considered safe iff
|
||||||
// it is part of POP, PUSH Unit, ARETURN sequence.
|
// it is part of POP, PUSH Unit, ARETURN sequence.
|
||||||
fun AbstractInsnNode.isSafeUnitInstance(): Boolean = this in safeUnitInstances
|
private fun AbstractInsnNode.isSafeUnitInstance(): Boolean = this in safeUnitInstances
|
||||||
|
|
||||||
fun AbstractInsnNode.isPopBeforeSafeUnitInstance(): Boolean = this in popsBeforeSafeUnitInstances
|
private fun AbstractInsnNode.isPopBeforeSafeUnitInstance(): Boolean = this in popsBeforeSafeUnitInstances
|
||||||
fun AbstractInsnNode.isAreturnAfterSafeUnitInstance(): Boolean = this in areturnsAfterSafeUnitInstances
|
private fun AbstractInsnNode.isAreturnAfterSafeUnitInstance(): Boolean = this in areturnsAfterSafeUnitInstances
|
||||||
|
|
||||||
private fun AbstractInsnNode.meaningfulSuccessors(): List<AbstractInsnNode> {
|
private fun AbstractInsnNode.meaningfulSuccessors(): List<AbstractInsnNode> = meaningfulSuccessorsCache.getOrPut(this) {
|
||||||
|
meaningfulSuccessorsOrPredecessors(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AbstractInsnNode.meaningfulPredecessors(): List<AbstractInsnNode> = meaningfulPredecessorsCache.getOrPut(this) {
|
||||||
|
meaningfulSuccessorsOrPredecessors(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AbstractInsnNode.meaningfulSuccessorsOrPredecessors(isSuccessors: Boolean): List<AbstractInsnNode> {
|
||||||
fun AbstractInsnNode.isMeaningful() = isMeaningful && opcode != Opcodes.NOP && opcode != Opcodes.GOTO && this !is LineNumberNode
|
fun AbstractInsnNode.isMeaningful() = isMeaningful && opcode != Opcodes.NOP && opcode != Opcodes.GOTO && this !is LineNumberNode
|
||||||
|
|
||||||
|
fun AbstractInsnNode.getIndices() =
|
||||||
|
if (isSuccessors) controlFlowGraph.getSuccessorsIndices(this)
|
||||||
|
else controlFlowGraph.getPredecessorsIndices(this)
|
||||||
|
|
||||||
val visited = arrayListOf<AbstractInsnNode>()
|
val visited = arrayListOf<AbstractInsnNode>()
|
||||||
fun dfs(insn: AbstractInsnNode) {
|
fun dfs(insn: AbstractInsnNode) {
|
||||||
if (insn in visited) return
|
if (insn in visited) return
|
||||||
visited += insn
|
visited += insn
|
||||||
if (!insn.isMeaningful()) {
|
if (!insn.isMeaningful()) {
|
||||||
for (succIndex in controlFlowGraph.getSuccessorsIndices(insn)) {
|
for (succIndex in insn.getIndices()) {
|
||||||
dfs(methodNode.instructions[succIndex])
|
dfs(methodNode.instructions[succIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (succIndex in controlFlowGraph.getSuccessorsIndices(this)) {
|
for (succIndex in getIndices()) {
|
||||||
dfs(methodNode.instructions[succIndex])
|
dfs(methodNode.instructions[succIndex])
|
||||||
}
|
}
|
||||||
return visited.filter { it.isMeaningful() }
|
return visited.filter { it.isMeaningful() }
|
||||||
@@ -887,8 +905,12 @@ private class ExaminedMethodNode(
|
|||||||
basicAnalyser.analyze(containingClassInternalName, methodNode)
|
basicAnalyser.analyze(containingClassInternalName, methodNode)
|
||||||
val typedFrames = basicAnalyser.frames
|
val typedFrames = basicAnalyser.frames
|
||||||
|
|
||||||
|
val isReferenceMap = popsBeforeSafeUnitInstances
|
||||||
|
.map { it to (!isUnreachable(it.index(), sourceFrames) && typedFrames[it.index()]?.top()?.isReference == true) }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
for (pop in popsBeforeSafeUnitInstances) {
|
for (pop in popsBeforeSafeUnitInstances) {
|
||||||
if (!isUnreachable(pop.index(), sourceFrames) && typedFrames[pop.index()]?.top()?.isReference == true) {
|
if (isReferenceMap[pop] == true) {
|
||||||
val label = Label()
|
val label = Label()
|
||||||
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
||||||
dup()
|
dup()
|
||||||
@@ -900,6 +922,90 @@ private class ExaminedMethodNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allSuspensionPointsAreTailCalls(suspensionPoints: List<SuspensionPoint>): Boolean {
|
||||||
|
val safelyReachableReturns = findSafelyReachableReturns()
|
||||||
|
|
||||||
|
val instructions = methodNode.instructions
|
||||||
|
return suspensionPoints.all { suspensionPoint ->
|
||||||
|
val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin)
|
||||||
|
val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd)
|
||||||
|
|
||||||
|
if (isUnreachable(endIndex, sourceFrames)) return@all true
|
||||||
|
|
||||||
|
val insideTryBlock = methodNode.tryCatchBlocks.any { block ->
|
||||||
|
val tryBlockStartIndex = instructions.indexOf(block.start)
|
||||||
|
val tryBlockEndIndex = instructions.indexOf(block.end)
|
||||||
|
|
||||||
|
beginIndex in tryBlockStartIndex..tryBlockEndIndex
|
||||||
|
}
|
||||||
|
if (insideTryBlock) return@all false
|
||||||
|
|
||||||
|
safelyReachableReturns[endIndex + 1]?.all { returnIndex ->
|
||||||
|
sourceFrames[returnIndex]?.top().sure {
|
||||||
|
"There must be some value on stack to return"
|
||||||
|
}.insns.any { sourceInsn ->
|
||||||
|
sourceInsn?.let(instructions::indexOf) in beginIndex..endIndex
|
||||||
|
}
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let's call an instruction safe if its execution is always invisible: stack modifications, branching, variable insns (invisible in debug)
|
||||||
|
*
|
||||||
|
* For some instruction `insn` define the result as following:
|
||||||
|
* - if there is a path leading to the non-safe instruction then result is `null`
|
||||||
|
* - Otherwise result contains all the reachable ARETURN indices
|
||||||
|
*
|
||||||
|
* @return indices of safely reachable returns for each instruction in the method node
|
||||||
|
*/
|
||||||
|
private fun findSafelyReachableReturns(): Array<Set<Int>?> {
|
||||||
|
val insns = methodNode.instructions
|
||||||
|
val reachableReturnsIndices = Array<Set<Int>?>(insns.size()) init@{ index ->
|
||||||
|
val insn = insns[index]
|
||||||
|
|
||||||
|
if (insn.opcode == Opcodes.ARETURN && !insn.isAreturnAfterSafeUnitInstance()) {
|
||||||
|
if (isUnreachable(index, sourceFrames)) return@init null
|
||||||
|
return@init setOf(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since POP, PUSH Unit, ARETURN behaves like normal return in terms of tail-call optimization, set return index to POP
|
||||||
|
if (insn.isPopBeforeSafeUnitInstance()) {
|
||||||
|
return@init setOf(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn)
|
||||||
|
|| insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance()
|
||||||
|
) {
|
||||||
|
setOf<Int>()
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed: Boolean
|
||||||
|
do {
|
||||||
|
changed = false
|
||||||
|
for (index in 0 until insns.size()) {
|
||||||
|
if (insns[index].opcode == Opcodes.ARETURN) continue
|
||||||
|
|
||||||
|
@Suppress("RemoveExplicitTypeArguments")
|
||||||
|
val newResult =
|
||||||
|
controlFlowGraph
|
||||||
|
.getSuccessorsIndices(index).plus(index)
|
||||||
|
.map(reachableReturnsIndices::get)
|
||||||
|
.fold<Set<Int>?, Set<Int>?>(mutableSetOf<Int>()) { acc, successorsResult ->
|
||||||
|
if (acc != null && successorsResult != null) acc + successorsResult else null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newResult != reachableReturnsIndices[index]) {
|
||||||
|
reachableReturnsIndices[index] = newResult
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (changed)
|
||||||
|
|
||||||
|
return reachableReturnsIndices
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun InstructionAdapter.generateContinuationConstructorCall(
|
internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||||
@@ -1031,94 +1137,10 @@ private fun getAllParameterTypes(desc: String, hasDispatchReceiver: Boolean, thi
|
|||||||
listOfNotNull(if (!hasDispatchReceiver) null else Type.getObjectType(thisName)).toTypedArray() +
|
listOfNotNull(if (!hasDispatchReceiver) null else Type.getObjectType(thisName)).toTypedArray() +
|
||||||
Type.getArgumentTypes(desc)
|
Type.getArgumentTypes(desc)
|
||||||
|
|
||||||
private fun ExaminedMethodNode.allSuspensionPointsAreTailCalls(suspensionPoints: List<SuspensionPoint>): Boolean {
|
|
||||||
val safelyReachableReturns = findSafelyReachableReturns()
|
|
||||||
|
|
||||||
val instructions = methodNode.instructions
|
|
||||||
return suspensionPoints.all { suspensionPoint ->
|
|
||||||
val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin)
|
|
||||||
val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd)
|
|
||||||
|
|
||||||
if (isUnreachable(beginIndex, sourceFrames)) return@all true
|
|
||||||
|
|
||||||
val insideTryBlock = methodNode.tryCatchBlocks.any { block ->
|
|
||||||
val tryBlockStartIndex = instructions.indexOf(block.start)
|
|
||||||
val tryBlockEndIndex = instructions.indexOf(block.end)
|
|
||||||
|
|
||||||
beginIndex in tryBlockStartIndex..tryBlockEndIndex
|
|
||||||
}
|
|
||||||
if (insideTryBlock) return@all false
|
|
||||||
|
|
||||||
safelyReachableReturns[endIndex + 1]?.all { returnIndex ->
|
|
||||||
sourceFrames[returnIndex]?.top().sure {
|
|
||||||
"There must be some value on stack to return"
|
|
||||||
}.insns.any { sourceInsn ->
|
|
||||||
sourceInsn?.let(instructions::indexOf) in beginIndex..endIndex
|
|
||||||
}
|
|
||||||
} ?: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter(Opcodes.API_VERSION) {
|
internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter(Opcodes.API_VERSION) {
|
||||||
override fun copyOperation(insn: AbstractInsnNode?, value: SourceValue?) = value
|
override fun copyOperation(insn: AbstractInsnNode?, value: SourceValue?) = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Let's call an instruction safe if its execution is always invisible: stack modifications, branching, variable insns (invisible in debug)
|
|
||||||
*
|
|
||||||
* For some instruction `insn` define the result as following:
|
|
||||||
* - if there is a path leading to the non-safe instruction then result is `null`
|
|
||||||
* - Otherwise result contains all the reachable ARETURN indices
|
|
||||||
*
|
|
||||||
* @return indices of safely reachable returns for each instruction in the method node
|
|
||||||
*/
|
|
||||||
private fun ExaminedMethodNode.findSafelyReachableReturns(): Array<Set<Int>?> {
|
|
||||||
val insns = methodNode.instructions
|
|
||||||
val reachableReturnsIndices = Array<Set<Int>?>(insns.size()) init@{ index ->
|
|
||||||
val insn = insns[index]
|
|
||||||
|
|
||||||
if (insn.opcode == Opcodes.ARETURN && !insn.isAreturnAfterSafeUnitInstance()) {
|
|
||||||
if (isUnreachable(index, sourceFrames)) return@init null
|
|
||||||
return@init setOf(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since POP, PUSH Unit, ARETURN behaves like normal return in terms of tail-call optimization, set return index to POP
|
|
||||||
if (insn.isPopBeforeSafeUnitInstance()) {
|
|
||||||
return@init setOf(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn)
|
|
||||||
|| insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance()
|
|
||||||
) {
|
|
||||||
setOf<Int>()
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed: Boolean
|
|
||||||
do {
|
|
||||||
changed = false
|
|
||||||
for (index in 0 until insns.size()) {
|
|
||||||
if (insns[index].opcode == Opcodes.ARETURN) continue
|
|
||||||
|
|
||||||
@Suppress("RemoveExplicitTypeArguments")
|
|
||||||
val newResult =
|
|
||||||
controlFlowGraph
|
|
||||||
.getSuccessorsIndices(index).plus(index)
|
|
||||||
.map(reachableReturnsIndices::get)
|
|
||||||
.fold<Set<Int>?, Set<Int>?>(mutableSetOf<Int>()) { acc, successorsResult ->
|
|
||||||
if (acc != null && successorsResult != null) acc + successorsResult else null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newResult != reachableReturnsIndices[index]) {
|
|
||||||
reachableReturnsIndices[index] = newResult
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (changed)
|
|
||||||
|
|
||||||
return reachableReturnsIndices
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether this instruction is unreachable, i.e. there is no path leading to this instruction
|
// Check whether this instruction is unreachable, i.e. there is no path leading to this instruction
|
||||||
internal fun isUnreachable(index: Int, sourceFrames: Array<Frame<SourceValue>?>): Boolean =
|
internal fun isUnreachable(index: Int, sourceFrames: Array<Frame<SourceValue>?>): Boolean =
|
||||||
sourceFrames.size <= index || sourceFrames[index] == null
|
sourceFrames.size <= index || sourceFrames[index] == null
|
||||||
|
|||||||
+6
-13
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2014 JetBrains s.r.o.
|
* Copyright 2010-2019 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.common
|
package org.jetbrains.kotlin.codegen.optimization.common
|
||||||
@@ -24,9 +13,12 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
|||||||
|
|
||||||
class ControlFlowGraph private constructor(private val insns: InsnList) {
|
class ControlFlowGraph private constructor(private val insns: InsnList) {
|
||||||
private val edges: Array<MutableList<Int>> = Array(insns.size()) { arrayListOf<Int>() }
|
private val edges: Array<MutableList<Int>> = Array(insns.size()) { arrayListOf<Int>() }
|
||||||
|
private val backwardEdges: Array<MutableList<Int>> = Array(insns.size()) { arrayListOf<Int>() }
|
||||||
|
|
||||||
fun getSuccessorsIndices(insn: AbstractInsnNode): List<Int> = getSuccessorsIndices(insns.indexOf(insn))
|
fun getSuccessorsIndices(insn: AbstractInsnNode): List<Int> = getSuccessorsIndices(insns.indexOf(insn))
|
||||||
fun getSuccessorsIndices(index: Int): List<Int> = edges[index]
|
fun getSuccessorsIndices(index: Int): List<Int> = edges[index]
|
||||||
|
fun getPredecessorsIndices(insn: AbstractInsnNode): List<Int> = getPredecessorsIndices(insns.indexOf(insn))
|
||||||
|
fun getPredecessorsIndices(index: Int): List<Int> = backwardEdges[index]
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@@ -35,6 +27,7 @@ class ControlFlowGraph private constructor(private val insns: InsnList) {
|
|||||||
|
|
||||||
fun addEdge(from: Int, to: Int) {
|
fun addEdge(from: Int, to: Int) {
|
||||||
graph.edges[from].add(to)
|
graph.edges[from].add(to)
|
||||||
|
graph.backwardEdges[to].add(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
object : MethodAnalyzer<BasicValue>("fake", node, OptimizationBasicInterpreter()) {
|
object : MethodAnalyzer<BasicValue>("fake", node, OptimizationBasicInterpreter()) {
|
||||||
|
|||||||
+27
@@ -17,7 +17,12 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
|
@kotlin.coroutines.jvm.internal.DebugMetadata
|
||||||
public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 {
|
public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
synthetic field result: java.lang.Object
|
synthetic field result: java.lang.Object
|
||||||
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1
|
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1
|
||||||
@@ -97,11 +102,28 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 {
|
|||||||
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
|
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
@kotlin.coroutines.jvm.internal.DebugMetadata
|
||||||
|
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
|
field label: int
|
||||||
|
synthetic field result: java.lang.Object
|
||||||
|
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1
|
||||||
|
public method <init>(p0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2, p1: kotlin.coroutines.Continuation): void
|
||||||
|
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
|
||||||
|
}
|
||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 {
|
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 {
|
||||||
synthetic final field $this_source$inlined: Sink
|
synthetic final field $this_source$inlined: Sink
|
||||||
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
|
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
|
||||||
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
|
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1
|
||||||
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1): void
|
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1): void
|
||||||
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
|
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
|
||||||
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
|
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
|
||||||
@@ -183,7 +205,12 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
|
@kotlin.coroutines.jvm.internal.DebugMetadata
|
||||||
public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 {
|
public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
synthetic field result: java.lang.Object
|
synthetic field result: java.lang.Object
|
||||||
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1
|
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1
|
||||||
|
|||||||
+26
@@ -17,11 +17,29 @@ public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$1 {
|
|||||||
synthetic final method setLabel(p0: int): void
|
synthetic final method setLabel(p0: int): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
|
synthetic field data: java.lang.Object
|
||||||
|
synthetic field exception: java.lang.Throwable
|
||||||
|
synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1
|
||||||
|
public method <init>(p0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2, p1: kotlin.coroutines.experimental.Continuation): void
|
||||||
|
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
|
||||||
|
synthetic final method getLabel(): int
|
||||||
|
synthetic final method setLabel(p0: int): void
|
||||||
|
}
|
||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2 {
|
public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2 {
|
||||||
synthetic final field $this_source$inlined: Sink
|
synthetic final field $this_source$inlined: Sink
|
||||||
synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1
|
synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1
|
||||||
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2
|
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2
|
||||||
|
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1
|
||||||
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$doResume$$inlined$filter$1): void
|
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$doResume$$inlined$filter$1): void
|
||||||
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
|
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
|
||||||
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||||
@@ -66,6 +84,10 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 {
|
|||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 {
|
public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
synthetic field data: java.lang.Object
|
synthetic field data: java.lang.Object
|
||||||
synthetic field exception: java.lang.Throwable
|
synthetic field exception: java.lang.Throwable
|
||||||
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1
|
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1
|
||||||
@@ -190,6 +212,10 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
|
|||||||
|
|
||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 {
|
public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 {
|
||||||
|
field L$0: java.lang.Object
|
||||||
|
field L$1: java.lang.Object
|
||||||
|
field L$2: java.lang.Object
|
||||||
|
field L$3: java.lang.Object
|
||||||
synthetic field data: java.lang.Object
|
synthetic field data: java.lang.Object
|
||||||
synthetic field exception: java.lang.Throwable
|
synthetic field exception: java.lang.Throwable
|
||||||
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1
|
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1
|
||||||
|
|||||||
Reference in New Issue
Block a user