Fixes in escape analysis
This commit is contained in:
+33
-17
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.IntValue
|
import org.jetbrains.kotlin.resolve.constants.IntValue
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.*
|
import org.jetbrains.kotlin.types.typeUtil.*
|
||||||
@@ -631,7 +632,7 @@ internal object EscapeAnalysis {
|
|||||||
|
|
||||||
private class InterproceduralAnalysisResult(val functionEscapeAnalysisResults: Map<FunctionDescriptor, FunctionEscapeAnalysisResult>)
|
private class InterproceduralAnalysisResult(val functionEscapeAnalysisResults: Map<FunctionDescriptor, FunctionEscapeAnalysisResult>)
|
||||||
|
|
||||||
private class InterproceduralAnalysis(context: Context,
|
private class InterproceduralAnalysis(val context: Context,
|
||||||
val runtimeAware: RuntimeAware,
|
val runtimeAware: RuntimeAware,
|
||||||
val externalFunctionEscapeAnalysisResults: Map<String, FunctionEscapeAnalysisResult>,
|
val externalFunctionEscapeAnalysisResults: Map<String, FunctionEscapeAnalysisResult>,
|
||||||
val intraproceduralAnalysisResult: IntraproceduralAnalysisResult,
|
val intraproceduralAnalysisResult: IntraproceduralAnalysisResult,
|
||||||
@@ -884,25 +885,36 @@ internal object EscapeAnalysis {
|
|||||||
NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||||
private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
|
private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
|
||||||
|
|
||||||
|
private fun getConservativeFunctionEAResult(descriptor: FunctionDescriptor): FunctionEscapeAnalysisResult {
|
||||||
|
val parameters = descriptor.allParameters
|
||||||
|
return FunctionEscapeAnalysisResult((0..parameters.size).map {
|
||||||
|
val type = if (it < parameters.size)
|
||||||
|
parameters[it].type
|
||||||
|
else {
|
||||||
|
if (descriptor is ConstructorDescriptor)
|
||||||
|
context.builtIns.unitType
|
||||||
|
else descriptor.returnType
|
||||||
|
}
|
||||||
|
ParameterEscapeAnalysisResult(
|
||||||
|
escapes = runtimeAware.isInteresting(type), // Conservatively assume all references escape.
|
||||||
|
pointsTo = IntArray(0)
|
||||||
|
)
|
||||||
|
}.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
private fun getExternalFunctionEAResult(callSite: CallSite): FunctionEscapeAnalysisResult {
|
private fun getExternalFunctionEAResult(callSite: CallSite): FunctionEscapeAnalysisResult {
|
||||||
val callee = callSite.actualCallee
|
val callee = callSite.actualCallee
|
||||||
|
|
||||||
DEBUG_OUTPUT(0) { println("External callee: $callee") }
|
DEBUG_OUTPUT(0) { println("External callee: $callee") }
|
||||||
|
|
||||||
val parameters = callee.allParameters
|
val staticCall = !callee.isOverridable || callSite.expression !is IrCall || callSite.expression.superQualifier != null
|
||||||
val calleeEAResult =
|
val calleeEAResult =
|
||||||
if (!callee.isOverridable || callSite.expression !is IrCall || callSite.expression.superQualifier != null) {
|
if (staticCall) {
|
||||||
getExternalFunctionEAResult(callee)
|
getExternalFunctionEAResult(callee)
|
||||||
} else {
|
} else {
|
||||||
DEBUG_OUTPUT(0) { println("A virtual call") }
|
DEBUG_OUTPUT(0) { println(if (staticCall) "Unknown function from module ${callee.module}" else "A virtual call") }
|
||||||
|
|
||||||
FunctionEscapeAnalysisResult((0..parameters.size).map {
|
getConservativeFunctionEAResult(callee)
|
||||||
val type = if (it < parameters.size) parameters[it].type else callee.returnType
|
|
||||||
ParameterEscapeAnalysisResult(
|
|
||||||
escapes = runtimeAware.isInteresting(type), // Conservatively assume all references escape.
|
|
||||||
pointsTo = IntArray(0)
|
|
||||||
)
|
|
||||||
}.toTypedArray())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_OUTPUT(0) {
|
DEBUG_OUTPUT(0) {
|
||||||
@@ -923,9 +935,11 @@ internal object EscapeAnalysis {
|
|||||||
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value
|
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
|
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
|
||||||
return FunctionEscapeAnalysisResult.fromBits(
|
return if (escapesBitMask == null)
|
||||||
escapesBitMask ?: 0,
|
getConservativeFunctionEAResult(function)
|
||||||
(0..function.allParameters.size).map { pointsToBitMask?.elementAtOrNull(it)?.value ?: 0 }
|
else FunctionEscapeAnalysisResult.fromBits(
|
||||||
|
escapesBitMask,
|
||||||
|
(0..function.allParameters.size).map { pointsToBitMask?.elementAtOrNull(it)?.value ?: 0 }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -974,6 +988,11 @@ internal object EscapeAnalysis {
|
|||||||
fun addIncomingParameter(parameter: Int) {
|
fun addIncomingParameter(parameter: Int) {
|
||||||
if (kind == PointsToGraphNodeKind.ESCAPES)
|
if (kind == PointsToGraphNodeKind.ESCAPES)
|
||||||
return
|
return
|
||||||
|
if (kind == PointsToGraphNodeKind.RETURN_VALUE) {
|
||||||
|
kind = PointsToGraphNodeKind.ESCAPES
|
||||||
|
parameterPointingOnUs = null
|
||||||
|
return
|
||||||
|
}
|
||||||
if (parameterPointingOnUs == null)
|
if (parameterPointingOnUs == null)
|
||||||
parameterPointingOnUs = parameter
|
parameterPointingOnUs = parameter
|
||||||
else {
|
else {
|
||||||
@@ -1004,8 +1023,6 @@ internal object EscapeAnalysis {
|
|||||||
|
|
||||||
PointsToGraphNodeKind.RETURN_VALUE -> {
|
PointsToGraphNodeKind.RETURN_VALUE -> {
|
||||||
when {
|
when {
|
||||||
// If a value is stored into a parameter field and into the return value - consider it escapes.
|
|
||||||
it.parameterPointingOnUs != null -> Lifetime.GLOBAL
|
|
||||||
// If a value is explicitly returned.
|
// If a value is explicitly returned.
|
||||||
returnValues.contains(node) -> Lifetime.RETURN_VALUE
|
returnValues.contains(node) -> Lifetime.RETURN_VALUE
|
||||||
// A value is stored into a field of the return value.
|
// A value is stored into a field of the return value.
|
||||||
@@ -1302,7 +1319,6 @@ internal object EscapeAnalysis {
|
|||||||
externalFunctionEAResults, intraproceduralAnalysisResult, lifetimes).analyze(irModule)
|
externalFunctionEAResults, intraproceduralAnalysisResult, lifetimes).analyze(irModule)
|
||||||
if (isStdlib) { // Save only for stdlib for now.
|
if (isStdlib) { // Save only for stdlib for now.
|
||||||
interproceduralAnalysisResult.functionEscapeAnalysisResults
|
interproceduralAnalysisResult.functionEscapeAnalysisResults
|
||||||
.filterNot { it.value.isTrivial }
|
|
||||||
.filter { it.key.isExported() }
|
.filter { it.key.isExported() }
|
||||||
.forEach { functionDescriptor, functionEAResult ->
|
.forEach { functionDescriptor, functionEAResult ->
|
||||||
val functionEAResultBuilder = ModuleEscapeAnalysisResult.FunctionEAResult.newBuilder()
|
val functionEAResultBuilder = ModuleEscapeAnalysisResult.FunctionEAResult.newBuilder()
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ internal fun <E> Array<E>.resetAt(index: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SymbolName("Kotlin_Array_fillImpl")
|
@SymbolName("Kotlin_Array_fillImpl")
|
||||||
@PointsTo(0b01000) // <array> points to <value>.
|
@PointsTo(0b01000, 0, 0, 0b00001) // <array> points to <value>, <value> points to <array>.
|
||||||
external private fun fillImpl(array: Array<Any>, fromIndex: Int, toIndex: Int, value: Any?)
|
external private fun fillImpl(array: Array<Any>, fromIndex: Int, toIndex: Int, value: Any?)
|
||||||
|
|
||||||
@SymbolName("Kotlin_IntArray_fillImpl")
|
@SymbolName("Kotlin_IntArray_fillImpl")
|
||||||
@@ -213,7 +213,7 @@ internal fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SymbolName("Kotlin_Array_copyImpl")
|
@SymbolName("Kotlin_Array_copyImpl")
|
||||||
@PointsTo(0, 0, 0b000001) // <destination> points to <array>.
|
@PointsTo(0b000100, 0, 0b000001) // <array> points to <destination>, <destination> points to <array>.
|
||||||
external private fun copyImpl(array: Array<Any>, fromIndex: Int,
|
external private fun copyImpl(array: Array<Any>, fromIndex: Int,
|
||||||
destination: Array<Any>, toIndex: Int, count: Int)
|
destination: Array<Any>, toIndex: Int, count: Int)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user