diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index f1cbe0cd21a..45ebb34e5cc 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -408,7 +408,6 @@ internal val bitcodePhase = NamedCompilerPhase( RTTIPhase then generateDebugInfoHeaderPhase then escapeAnalysisPhase then - localEscapeAnalysisPhase then codegenPhase then finalizeDebugInfoPhase then cStubsPhase @@ -528,8 +527,6 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { // Also see https://youtrack.jetbrains.com/issue/KT-50399 for more details. disable(checkSamSuperTypesPhase) - disable(localEscapeAnalysisPhase) - disableIf(singleCompilation, config.producePerFileCache) disableUnless(umbrellaCompilation, config.producePerFileCache) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index b9ae7da09b3..aa95545ec62 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -250,15 +250,6 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase( } ) -internal val localEscapeAnalysisPhase = makeKonanModuleOpPhase( - name = "LocalEscapeAnalysis", - description = "Local escape analysis", - prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase), - op = { context, _ -> - LocalEscapeAnalysis.computeLifetimes(context, context.moduleDFG!!, context.lifetimes) - } -) - internal val codegenPhase = makeKonanModuleOpPhase( name = "Codegen", description = "Code generation", diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt deleted file mode 100644 index 85497f8a2c0..00000000000 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/LocalEscapeAnalysis.kt +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package org.jetbrains.kotlin.backend.konan.optimizations - -import org.jetbrains.kotlin.backend.konan.llvm.Lifetime -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.logMultiple -import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems - -internal object LocalEscapeAnalysis { - private enum class EscapeState { - GLOBAL_ESCAPE, // escape through global reference - ARG_ESCAPE, // escapes through function arguments, return or throw - NO_ESCAPE // object does not escape - } - - class FunctionAnalyzer(val function: DataFlowIR.Function, val context: Context) { - // Escape states of nodes. - private val nodesEscapeStates = mutableMapOf() - // Connected objects map, escape state change of key node influences on all connected nodes states. - private val connectedObjects = mutableMapOf>() - // Maximum size of array which is allowed to allocate on stack. - // TODO: replace into KonanConfigKeys? - private val stackAllocationArraySizeLimit = 64 - - private var DataFlowIR.Node.escapeState: EscapeState - set(value) { - // Write state only if it has more limitations. - val writeState = nodesEscapeStates[this]?.let { - value < it - } ?: true - if (writeState) { - nodesEscapeStates[this] = value - } - } - get() = nodesEscapeStates.getOrDefault(this, EscapeState.NO_ESCAPE) - - private fun connectObjects(node: DataFlowIR.Node, connectedNode: DataFlowIR.Node) { - connectedObjects.getOrPut(node) { mutableSetOf() }.add(connectedNode) - } - - private fun findOutArraySize(node: DataFlowIR.Node): Int? { - if (node is DataFlowIR.Node.SimpleConst<*>) { - return node.value as? Int - } - if (node is DataFlowIR.Node.Variable) { - // In case of several possible values, it's unknown what is used. - // TODO: if all values are constants which are less limit? - if (node.values.size == 1) { - return findOutArraySize(node.values.first().node) - } - } - return null - } - - private fun evaluateEscapeState(node: DataFlowIR.Node) { - node.escapeState = EscapeState.NO_ESCAPE - when (node) { - is DataFlowIR.Node.Call -> { - val pointsToMasks = (0..node.callee.parameters.size) - .map { node.callee.pointsTo?.elementAtOrNull(it) ?: 0 } - val returnPointsToMask = pointsToMasks[node.callee.parameters.size] - node.arguments.forEachIndexed { index, arg -> - // Check information about arguments escaping. - val escapes = node.callee.escapes?.let { - it and (1 shl index) != 0 - } ?: node.callee !is DataFlowIR.FunctionSymbol.External - - // Connect with all arguments that return value points to. - if (returnPointsToMask and (1 shl index) != 0) { - connectObjects(node, arg.node) - } - - // Connect current argument with other it points to. - (0..node.callee.parameters.size).filter { pointsToMasks[index] and (1 shl it) != 0 }.forEach { - if (it == node.callee.parameters.size) { - // Argument points to this. - connectObjects(arg.node, node) - } else { - connectObjects(arg.node, node.arguments[it].node) - } - } - arg.node.escapeState = if (escapes) EscapeState.ARG_ESCAPE else EscapeState.NO_ESCAPE - } - - // Check size for array allocation. - if (node is DataFlowIR.Node.NewObject && node.constructedType is DataFlowIR.Type.Declared) { - node.constructedType.irClass?.let { irClass -> - // Work only with arrays which elements size is known. - if (irClass.isArrayWithFixedSizeItems) { - val sizeArgument = node.arguments.first().node - val arraySize = findOutArraySize(sizeArgument) - if (arraySize == null || arraySize > stackAllocationArraySizeLimit) { - node.escapeState = EscapeState.GLOBAL_ESCAPE - } - } else { - node.escapeState = EscapeState.GLOBAL_ESCAPE - } - } - } - } - is DataFlowIR.Node.Singleton -> { - node.escapeState = EscapeState.GLOBAL_ESCAPE - } - is DataFlowIR.Node.FieldRead -> { - node.receiver?.let { obj -> - connectObjects(node, obj.node) - } ?: run { node.escapeState = EscapeState.GLOBAL_ESCAPE } - } - is DataFlowIR.Node.FieldWrite -> { - node.receiver?.let { obj -> - connectObjects(obj.node, node.value.node) - } ?: run { - node.escapeState = EscapeState.GLOBAL_ESCAPE - node.value.node.escapeState = EscapeState.GLOBAL_ESCAPE - } - } - is DataFlowIR.Node.ArrayWrite -> { - connectObjects(node.array.node, node.value.node) - } - is DataFlowIR.Node.Variable -> { - node.values.forEach { - connectObjects(node, it.node) - } - } - is DataFlowIR.Node.ArrayRead -> { - // If element of array(return value) points to array(this value) and escapes then array also should escape. - if (((node.callee.pointsTo?.elementAtOrNull(node.callee.parameters.size) ?: 0) and (1 shl 0)) != 0) { - connectObjects(node, node.array.node) - } - } - is DataFlowIR.Node.Parameter -> { - node.escapeState = EscapeState.ARG_ESCAPE - } - is DataFlowIR.Node.AllocInstance, - is DataFlowIR.Node.Const, - is DataFlowIR.Node.FunctionReference, - is DataFlowIR.Node.Null, - is DataFlowIR.Node.Scope -> {} - } - } - - private inner class ConnectedObjectsVisitor { - val visitedObjects = mutableSetOf() - - fun visit(node: DataFlowIR.Node, action: (DataFlowIR.Node) -> Unit) { - action(node) - visitedObjects.add(node) - connectedObjects[node]?.forEach { - if (!visitedObjects.contains(it)) { - visit(it, action) - } - } - } - } - - private fun propagateState(state: EscapeState, visitor: ConnectedObjectsVisitor) { - connectedObjects.filter { it.key.escapeState == state }.forEach { (key, value) -> - value.forEach { obj -> - visitor.visit(obj) { it.escapeState = key.escapeState } - } - } - } - - fun analyze(lifetimes: MutableMap) { - function.body.forEachNonScopeNode { node -> - evaluateEscapeState(node) - } - function.body.returns.escapeState = EscapeState.ARG_ESCAPE - function.body.throws.escapeState = EscapeState.ARG_ESCAPE - - // Change state of connected objects. - val visitor = ConnectedObjectsVisitor() - propagateState(EscapeState.GLOBAL_ESCAPE, visitor) - propagateState(EscapeState.ARG_ESCAPE, visitor) - - nodesEscapeStates.filter { - it.value == EscapeState.NO_ESCAPE - }.forEach { (irNode, _) -> - val ir = when (irNode) { - is DataFlowIR.Node.Call -> irNode.irCallSite - else -> null - } - ir?.let { - lifetimes.put(it, Lifetime.STACK) - context.log { "$ir does not escape" } - } - } - } - } - - fun analyze(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap) { - moduleDFG.functions.forEach { (name, function) -> - context.logMultiple { - +"===============================================" - +"Visiting $name" - +"DATA FLOW IR:" - +function.debugString() - } - FunctionAnalyzer(function, context).analyze(lifetimes) - } - } - - fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap) { - context.log { "In local EA" } - assert(lifetimes.isEmpty()) - analyze(context, moduleDFG, lifetimes) - } -} \ No newline at end of file