[K/N] Remove LocalEscapeAnalysis

It was disabled for a long time because we have
a proper one now.
This commit is contained in:
Sergey Bogolepov
2022-10-04 09:27:01 +03:00
committed by Space Team
parent 2efcc21d21
commit 94c705d608
3 changed files with 0 additions and 225 deletions
@@ -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)
@@ -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",
@@ -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<DataFlowIR.Node, EscapeState>()
// Connected objects map, escape state change of key node influences on all connected nodes states.
private val connectedObjects = mutableMapOf<DataFlowIR.Node, MutableSet<DataFlowIR.Node>>()
// 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<DataFlowIR.Node>()
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<IrElement, Lifetime>) {
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<IrElement, Lifetime>) {
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<IrElement, Lifetime>) {
context.log { "In local EA" }
assert(lifetimes.isEmpty())
analyze(context, moduleDFG, lifetimes)
}
}