diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt index a8baf55563c..d47c2168810 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt @@ -558,7 +558,6 @@ internal object EscapeAnalysis { } } - val nonTrivialComponent = nodes.size > 1 val pointsToGraphs = mutableMapOf() val toAnalyze = mutableSetOf() toAnalyze.addAll(nodes) @@ -574,33 +573,40 @@ internal object EscapeAnalysis { val pointsToGraph = PointsToGraph(function) pointsToGraphs[function] = pointsToGraph - analyze(callGraph, pointsToGraph, function) - val endResult = escapeAnalysisResults[function]!! - if (startResult == endResult) { - context.log { "Escape analysis is not changed" } - } else { - context.log { "Escape analysis was refined:\n$endResult" } - if (with(DivergenceResolutionParams) { - // A heuristic: the majority of functions have their points-to graph size linear in number of IR (or DFG) nodes, - // there are exceptions but it's a trade-off we have to make. - // The trick with [NegligibleSize] handles functions that basically delegate their work to other functions. - val numberOfNodes = intraproceduralAnalysisResults[function]!!.function.body.allScopes.sumOf { it.nodes.size } - val maxAllowedGraphSize = NegligibleSize + numberOfNodes * SwellingFactor + val maxAllowedGraphSize = with(DivergenceResolutionParams) { + // A heuristic: the majority of functions have their points-to graph size linear in number of IR (or DFG) nodes, + // there are exceptions but it's a trade-off we have to make. + // The trick with [NegligibleSize] handles functions that basically delegate their work to other functions. + val numberOfNodes = intraproceduralAnalysisResults[function]!!.function.body.allScopes.sumOf { it.nodes.size } + NegligibleSize + numberOfNodes * SwellingFactor + } - numberOfRuns[function]!! > MaxAttempts - || (nonTrivialComponent && pointsToGraph.allNodes.size > maxAllowedGraphSize) - } - ) { - // TODO: suboptimal. May be it is possible somehow handle the entire component at once? - context.log { - "WARNING: Escape analysis for $function seems not to be converging." + - " Assuming conservative results." - } - escapeAnalysisResults[function] = FunctionEscapeAnalysisResult.pessimistic(function.parameters.size) - nodes.remove(function) + var analyzedSuccessfully = analyze(callGraph, pointsToGraph, function, maxAllowedGraphSize) + var processCallers = true + if (analyzedSuccessfully) { + val endResult = escapeAnalysisResults[function]!! + if (startResult == endResult) { + context.log { "Escape analysis is not changed" } + processCallers = false + } else { + context.log { "Escape analysis was refined:\n$endResult" } + if (numberOfRuns[function]!! > DivergenceResolutionParams.MaxAttempts) + analyzedSuccessfully = false } + } + if (!analyzedSuccessfully) { + // TODO: suboptimal. May be it is possible somehow handle the entire component at once? + context.log { + "WARNING: Escape analysis for $function seems not to be converging." + + " Assuming conservative results." + } + escapeAnalysisResults[function] = FunctionEscapeAnalysisResult.pessimistic(function.parameters.size) + nodes.remove(function) + } + + if (processCallers) { callGraph.reversedEdges[function]?.forEach { if (nodes.contains(it)) toAnalyze.add(it) @@ -659,19 +665,27 @@ internal object EscapeAnalysis { private fun arraySize(itemSize: Int, length: Int): Long = pointerSize /* typeinfo */ + 4 /* size */ + itemSize * length.toLong() - private fun analyze(callGraph: CallGraph, pointsToGraph: PointsToGraph, function: DataFlowIR.FunctionSymbol.Declared) { - context.log {"Before calls analysis" } + private fun analyze( + callGraph: CallGraph, + pointsToGraph: PointsToGraph, + function: DataFlowIR.FunctionSymbol.Declared, + maxAllowedGraphSize: Int + ): Boolean { + context.log { "Before calls analysis" } pointsToGraph.log() pointsToGraph.logDigraph(false) callGraph.directEdges[function]!!.callSites.forEach { val callee = it.actualCallee val calleeEAResult = if (it.isVirtual) - getExternalFunctionEAResult(it) - else - callGraph.directEdges[callee]?.let { escapeAnalysisResults[it.symbol]!! } - ?: getExternalFunctionEAResult(it) + getExternalFunctionEAResult(it) + else + callGraph.directEdges[callee]?.let { escapeAnalysisResults[it.symbol]!! } + ?: getExternalFunctionEAResult(it) pointsToGraph.processCall(it, calleeEAResult) + + if (pointsToGraph.allNodes.size > maxAllowedGraphSize) + return false } context.log { "After calls analysis" } @@ -686,6 +700,8 @@ internal object EscapeAnalysis { pointsToGraph.logDigraph(true) escapeAnalysisResults[function] = eaResult + + return true } private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {