Made escape analysis up to date with devirtualization

This commit is contained in:
Igor Chevdar
2018-05-23 17:37:51 +03:00
parent c994bd54be
commit a6bf1e7262
9 changed files with 177 additions and 106 deletions
@@ -61,10 +61,10 @@ enum class KonanPhase(val description: String,
/* ... */ BITCODE("LLVM BitCode Generation"),
/* ... ... */ RTTI("RTTI Generation"),
/* ... ... */ BUILD_DFG("Data flow graph building"),
/* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG),
/* ... ... */ DESERIALIZE_DFG("Data flow graph deserializing"),
/* ... ... */ DEVIRTUALIZATION("Devirtualization", BUILD_DFG, DESERIALIZE_DFG),
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG, enabled = false),
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG, enabled = false), // TODO: Requires devirtualization.
/* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG), // TODO: Requires escape analysis.
/* ... ... */ CODEGEN("Code Generation"),
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
/* */ LINK_STAGE("Link stage"),
@@ -189,6 +189,7 @@ class IrSuspendableExpressionImpl(startOffset: Int, endOffset: Int, type: Kotlin
}
internal interface IrPrivateFunctionCall : IrCall {
val virtualCallee: IrCall?
val dfgSymbol: DataFlowIR.FunctionSymbol.Declared
val moduleDescriptor: ModuleDescriptor
val totalFunctions: Int
@@ -200,6 +201,7 @@ internal class IrPrivateFunctionCallImpl(startOffset: Int,
type: KotlinType,
override val symbol: IrFunctionSymbol,
override val descriptor: FunctionDescriptor,
override val virtualCallee: IrCall?,
typeArgumentsCount: Int,
override val dfgSymbol: DataFlowIR.FunctionSymbol.Declared,
override val moduleDescriptor: ModuleDescriptor,
@@ -40,45 +40,39 @@ import kotlin.reflect.KProperty
internal sealed class SlotType {
// Frame local arena slot can be used.
class ARENA: SlotType()
object ARENA: SlotType()
// Return slot can be used.
class RETURN: SlotType()
// Return slot, if it is an arena, can be used.
class RETURN_IF_ARENA: SlotType()
object RETURN: SlotType()
// Return slot, if it is an arena, can be used.
object RETURN_IF_ARENA: SlotType()
// Param slot, if it is an arena, can be used.
class PARAM_IF_ARENA(val parameter: Int): SlotType()
// Params slot, if it is an arena, can be used.
class PARAMS_IF_ARENA(val parameters: IntArray, val useReturnSlot: Boolean): SlotType()
// Anonymous slot.
class ANONYMOUS: SlotType()
object ANONYMOUS: SlotType()
// Unknown slot type.
class UNKNOWN: SlotType()
companion object {
val ARENA = ARENA()
val RETURN = RETURN()
val RETURN_IF_ARENA = RETURN_IF_ARENA()
val ANONYMOUS = ANONYMOUS()
val UNKNOWN = UNKNOWN()
}
object UNKNOWN: SlotType()
}
// Lifetimes class of reference, computed by escape analysis.
internal sealed class Lifetime(val slotType: SlotType) {
// If reference is frame-local (only obtained from some call and never leaves).
class LOCAL: Lifetime(SlotType.ARENA) {
object LOCAL: Lifetime(SlotType.ARENA) {
override fun toString(): String {
return "LOCAL"
}
}
// If reference is only returned.
class RETURN_VALUE: Lifetime(SlotType.RETURN) {
object RETURN_VALUE: Lifetime(SlotType.RETURN) {
override fun toString(): String {
return "RETURN_VALUE"
}
}
// If reference is set as field of references of class RETURN_VALUE or INDIRECT_RETURN_VALUE.
class INDIRECT_RETURN_VALUE: Lifetime(SlotType.RETURN_IF_ARENA) {
object INDIRECT_RETURN_VALUE: Lifetime(SlotType.RETURN_IF_ARENA) {
override fun toString(): String {
return "INDIRECT_RETURN_VALUE"
}
@@ -91,15 +85,23 @@ internal sealed class Lifetime(val slotType: SlotType) {
}
}
// If reference is stored to the field of an incoming parameters.
class PARAMETERS_FIELD(val parameters: IntArray, val useReturnSlot: Boolean)
: Lifetime(SlotType.PARAMS_IF_ARENA(parameters, useReturnSlot)) {
override fun toString(): String {
return "PARAMETERS_FIELD(${parameters.contentToString()}, useReturnSlot='$useReturnSlot')"
}
}
// If reference refers to the global (either global object or global variable).
class GLOBAL: Lifetime(SlotType.ANONYMOUS) {
object GLOBAL: Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "GLOBAL"
}
}
// If reference used to throw.
class THROW: Lifetime(SlotType.ANONYMOUS) {
object THROW: Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "THROW"
}
@@ -107,36 +109,25 @@ internal sealed class Lifetime(val slotType: SlotType) {
// If reference used as an argument of outgoing function. Class can be improved by escape analysis
// of called function.
class ARGUMENT: Lifetime(SlotType.ANONYMOUS) {
object ARGUMENT: Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "ARGUMENT"
}
}
// If reference class is unknown.
class UNKNOWN: Lifetime(SlotType.UNKNOWN) {
object UNKNOWN: Lifetime(SlotType.UNKNOWN) {
override fun toString(): String {
return "UNKNOWN"
}
}
// If reference class is irrelevant.
class IRRELEVANT: Lifetime(SlotType.UNKNOWN) {
object IRRELEVANT: Lifetime(SlotType.UNKNOWN) {
override fun toString(): String {
return "IRRELEVANT"
}
}
companion object {
val LOCAL = LOCAL()
val RETURN_VALUE = RETURN_VALUE()
val INDIRECT_RETURN_VALUE = INDIRECT_RETURN_VALUE()
val GLOBAL = GLOBAL()
val THROW = THROW()
val ARGUMENT = ARGUMENT()
val UNKNOWN = UNKNOWN()
val IRRELEVANT = IRRELEVANT()
}
}
/**
@@ -119,7 +119,7 @@ internal fun emitLLVM(context: Context) {
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
val callGraph = CallGraphBuilder(context, moduleDFG!!, externalModulesDFG!!, devirtualizationAnalysisResult, false).build()
EscapeAnalysis.computeLifetimes(moduleDFG!!, externalModulesDFG!!, callGraph, lifetimes)
EscapeAnalysis.computeLifetimes(context, moduleDFG!!, externalModulesDFG!!, callGraph, lifetimes)
}
phaser.phase(KonanPhase.SERIALIZE_DFG) {
@@ -1993,7 +1993,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
if (callee is IrPrivateFunctionCall)
return evaluatePrivateFunctionCall(callee, argsWithContinuationIfNeeded, resultLifetime)
return evaluatePrivateFunctionCall(callee, argsWithContinuationIfNeeded, callee.virtualCallee?.let { resultLifetime(it) } ?: resultLifetime)
when {
descriptor.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
@@ -661,7 +661,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
receiverType,
name.localHash.value,
takeName { name }
)
),
value
)
}
@@ -309,26 +309,25 @@ internal object DFGSerializer {
}
}
class FunctionSymbolBase(val parameterTypes: IntArray, val returnType: Int, val attributes: Int) {
class FunctionSymbolBase(val parameterTypes: IntArray, val returnType: Int, val attributes: Int, val escapes: Int?, val pointsTo: IntArray?) {
constructor(data: ArraySlice) : this(data.readIntArray(), data.readInt(), data.readInt())
constructor(data: ArraySlice) : this(data.readIntArray(), data.readInt(), data.readInt(), data.readNullableInt(), data.readNullable { readIntArray() })
fun write(result: ArraySlice) {
result.writeIntArray(parameterTypes)
result.writeInt(returnType)
result.writeInt(attributes)
result.writeNullableInt(escapes)
result.writeNullable(pointsTo) { writeIntArray(it) }
}
}
class ExternalFunctionSymbol(val hash: Long, val escapes: Int?, val pointsTo: IntArray?, val name: String?) {
class ExternalFunctionSymbol(val hash: Long, val name: String?) {
constructor(data: ArraySlice) : this(data.readLong(), data.readNullableInt(),
data.readNullable { readIntArray() }, data.readNullableString())
constructor(data: ArraySlice) : this(data.readLong(), data.readNullableString())
fun write(result: ArraySlice) {
result.writeLong(hash)
result.writeNullableInt(escapes)
result.writeNullable(pointsTo) { writeIntArray(it) }
result.writeNullableString(name)
}
}
@@ -376,8 +375,8 @@ internal object DFGSerializer {
}
companion object {
fun external(base: FunctionSymbolBase, hash: Long, escapes: Int?, pointsTo: IntArray?, name: String?) =
FunctionSymbol(base, ExternalFunctionSymbol(hash, escapes, pointsTo, name), null, null)
fun external(base: FunctionSymbolBase, hash: Long, name: String?) =
FunctionSymbol(base, ExternalFunctionSymbol(hash, name), null, null)
fun public(base: FunctionSymbolBase, hash: Long, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(base, null, PublicFunctionSymbol(hash, index, bridgeTarget, name), null)
@@ -807,15 +806,16 @@ internal object DFGSerializer {
FunctionSymbolBase(
symbol.parameterTypes.map { typeMap[it]!! }.toIntArray(),
typeMap[symbol.returnType]!!,
symbol.attributes
symbol.attributes,
symbol.escapes,
symbol.pointsTo
)
val symbol = it.key
val bridgeTarget = (symbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] }
when (symbol) {
is DataFlowIR.FunctionSymbol.External ->
FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash,
symbol.escapes, symbol.pointsTo, symbol.name)
FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash, symbol.name)
is DataFlowIR.FunctionSymbol.Public ->
FunctionSymbol.public(buildFunctionSymbolBase(symbol), symbol.hash,
@@ -977,8 +977,7 @@ internal object DFGSerializer {
val private = it.private
when {
external != null ->
DataFlowIR.FunctionSymbol.External(external.hash,
attributes, external.escapes, external.pointsTo, external.name)
DataFlowIR.FunctionSymbol.External(external.hash, attributes, external.name)
public != null -> {
val symbolTableIndex = public.index
@@ -1000,6 +999,8 @@ internal object DFGSerializer {
}.apply {
parameterTypes = it.base.parameterTypes.map { types[it] }.toTypedArray()
returnType = types[it.base.returnType]
escapes = it.base.escapes
pointsTo = it.base.pointsTo
}
}
@@ -1110,7 +1111,7 @@ internal object DFGSerializer {
NodeType.FIELD_READ -> {
val fieldRead = it.fieldRead!!
val receiver = fieldRead.receiver?.let { deserializeEdge(it) }
DataFlowIR.Node.FieldRead(receiver, deserializeField(fieldRead.field))
DataFlowIR.Node.FieldRead(receiver, deserializeField(fieldRead.field), null)
}
NodeType.FIELD_WRITE -> {
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
@@ -138,8 +139,10 @@ internal object DataFlowIR {
val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
class External(val hash: Long, attributes: Int,
val escapes: Int?, val pointsTo: IntArray?, name: String? = null)
var escapes: Int? = null
var pointsTo: IntArray? = null
class External(val hash: Long, attributes: Int, name: String? = null)
: FunctionSymbol(attributes, name) {
override fun equals(other: Any?): Boolean {
@@ -180,7 +183,7 @@ internal object DataFlowIR {
}
override fun toString(): String {
return "PublicFunction(hash='$hash', symbolTableIndex='$symbolTableIndex', name='$name')"
return "PublicFunction(hash='$hash', name='$name', symbolTableIndex='$symbolTableIndex', escapes='$escapes', pointsTo='${pointsTo?.contentToString()})"
}
}
@@ -200,7 +203,7 @@ internal object DataFlowIR {
}
override fun toString(): String {
return "PrivateFunction(index=$index, symbolTableIndex='$symbolTableIndex', name='$name')"
return "PrivateFunction(index=$index, symbolTableIndex='$symbolTableIndex', name='$name', escapes='$escapes', pointsTo='${pointsTo?.contentToString()})"
}
}
}
@@ -251,7 +254,7 @@ internal object DataFlowIR {
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
class FieldRead(val receiver: Edge?, val field: Field) : Node()
class FieldRead(val receiver: Edge?, val field: Field, val ir: IrGetField?) : Node()
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node()
@@ -565,8 +568,10 @@ internal object DataFlowIR {
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value
@Suppress("UNCHECKED_CAST")
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
FunctionSymbol.External(name.localHash.value, attributes, escapesBitMask,
pointsToBitMask?.let { it.map { it.value }.toIntArray() }, takeName { name })
FunctionSymbol.External(name.localHash.value, attributes, takeName { name }).apply {
escapes = escapesBitMask
pointsTo = pointsToBitMask?.let { it.map { it.value }.toIntArray() }
}
}
else -> {
@@ -991,7 +991,8 @@ internal object Devirtualization {
dfgSymbol = devirtualizedCallee,
totalFunctions = devirtualizedCallee.module.numberOfFunctions,
moduleDescriptor = devirtualizedCallee.module.descriptor,
functionIndex = devirtualizedCallee.symbolTableIndex
functionIndex = devirtualizedCallee.symbolTableIndex,
virtualCallee = callee
)
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: KotlinType,
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.backend.konan.DirectedGraphCondensationBuilder
import org.jetbrains.kotlin.backend.konan.DirectedGraphMultiNode
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.backend.konan.Context
internal object EscapeAnalysis {
@@ -92,10 +93,20 @@ internal object EscapeAnalysis {
private class FunctionAnalysisResult(val function: DataFlowIR.Function,
val nodesRoles: Map<DataFlowIR.Node, Roles>)
private class IntraproceduralAnalysis(val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
private class IntraproceduralAnalysis(val context: Context,
val moduleDFG: ModuleDFG, val externalModulesDFG: ExternalModulesDFG,
val callGraph: CallGraph) {
val functions = moduleDFG.functions// TODO: use for cross-module analysis: + externalModulesDFG.functionDFGs
private fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared {
if (this is DataFlowIR.Type.Declared) return this
val hash = (this as DataFlowIR.Type.External).hash
return externalModulesDFG.publicTypes[hash] ?: error("Unable to resolve exported type $hash")
}
fun analyze(): Map<DataFlowIR.FunctionSymbol, FunctionAnalysisResult> {
val nothing = moduleDFG.symbolTable.mapClass(context.ir.symbols.nothing.owner).resolved()
return callGraph.nodes.associateBy({ it.symbol }) {
val function = functions[it.symbol]!!
val body = function.body
@@ -124,7 +135,9 @@ internal object EscapeAnalysis {
}
is DataFlowIR.Node.Singleton -> {
assignRole(node, Role.WRITTEN_TO_GLOBAL, null /* TODO */)
val type = node.type.resolved()
if (type != nothing)
assignRole(node, Role.WRITTEN_TO_GLOBAL, null /* TODO */)
}
is DataFlowIR.Node.FieldRead -> {
@@ -154,13 +167,22 @@ internal object EscapeAnalysis {
}
}
is DataFlowIR.Node.ArrayWrite -> {
assignRole(node.array.node, Role.FIELD_WRITTEN, RoleInfoEntry(node.value.node))
assignRole(node.value.node, Role.FIELD_WRITTEN, RoleInfoEntry(node.array.node))
}
is DataFlowIR.Node.ArrayRead -> {
assignRole(node.array.node, Role.FIELD_WRITTEN, RoleInfoEntry(node))
assignRole(node, Role.FIELD_WRITTEN, RoleInfoEntry(node.array.node))
}
is DataFlowIR.Node.Variable -> {
for (value in node.values) {
assignRole(node, Role.FIELD_WRITTEN, RoleInfoEntry(value.node))
assignRole(value.node, Role.FIELD_WRITTEN, RoleInfoEntry(node))
}
}
else -> TODO()
}
}
FunctionAnalysisResult(function, nodesRoles)
@@ -228,6 +250,7 @@ internal object EscapeAnalysis {
private class InterproceduralAnalysis(val callGraph: CallGraph,
val intraproceduralAnalysisResult: Map<DataFlowIR.FunctionSymbol, FunctionAnalysisResult>,
val externalModulesDFG: ExternalModulesDFG,
val lifetimes: MutableMap<IrElement, Lifetime>) {
val escapeAnalysisResults = mutableMapOf<DataFlowIR.FunctionSymbol, FunctionEscapeAnalysisResult>()
@@ -238,8 +261,12 @@ internal object EscapeAnalysis {
callGraph.directEdges.forEach { t, u ->
println(" FUN $t")
u.callSites.forEach {
val local = callGraph.directEdges.containsKey(it.actualCallee)
println(" CALLS ${if (local) "LOCAL" else "EXTERNAL"} ${it.actualCallee}")
val label = when {
it.isVirtual -> "VIRTUAL"
callGraph.directEdges.containsKey(it.actualCallee) -> "LOCAL"
else -> "EXTERNAL"
}
println(" CALLS $label ${it.actualCallee}")
}
callGraph.reversedEdges[t]!!.forEach {
println(" CALLED BY $it")
@@ -318,10 +345,31 @@ internal object EscapeAnalysis {
}
}
}
multiNode.nodes.forEach {
val escapeAnalysisResult = escapeAnalysisResults[it]!!
var escapes = 0
val pointsTo = escapeAnalysisResult.parameters.withIndex().map { (index, parameterEAResult) ->
if (parameterEAResult.escapes)
escapes = escapes or (1 shl index)
var pointsToMask = 0
parameterEAResult.pointsTo.forEach {
pointsToMask = pointsToMask or (1 shl it)
}
pointsToMask
}.toIntArray()
it.escapes = escapes
it.pointsTo = pointsTo
}
for (graph in pointsToGraphs.values) {
graph.nodes.keys
.filterIsInstance<DataFlowIR.Node.Call>()
.forEach { call -> call.irCallSite?.let { lifetimes.put(it, graph.lifetimeOf(call)) } }
for (node in graph.nodes.keys) {
val ir = when (node) {
is DataFlowIR.Node.Call -> node.irCallSite
is DataFlowIR.Node.ArrayRead -> node.irCallSite
is DataFlowIR.Node.FieldRead -> node.ir
else -> null
}
ir?.let { lifetimes.put(it, graph.lifetimeOf(node)) }
}
}
}
@@ -333,9 +381,12 @@ internal object EscapeAnalysis {
callGraph.directEdges[function]!!.callSites.forEach {
val callee = it.actualCallee
val calleeEAResult = callGraph.directEdges[callee]?.let { escapeAnalysisResults[it.symbol]!! }
?: getExternalFunctionEAResult(it)
pointsToGraph.processCall(it.call, calleeEAResult)
val calleeEAResult = if (it.isVirtual)
getExternalFunctionEAResult(it)
else
callGraph.directEdges[callee]?.let { escapeAnalysisResults[it.symbol]!! }
?: getExternalFunctionEAResult(it)
pointsToGraph.processCall(it, calleeEAResult)
}
DEBUG_OUTPUT(0) {
@@ -358,23 +409,29 @@ internal object EscapeAnalysis {
val numberOfParameters = symbol.parameterTypes.size
return FunctionEscapeAnalysisResult((0..numberOfParameters).map {
ParameterEscapeAnalysisResult(
escapes = true,
escapes = true,
pointsTo = IntArray(0)
)
}.toTypedArray())
}
private fun getExternalFunctionEAResult(callSite: CallGraphNode.CallSite): FunctionEscapeAnalysisResult {
val callee = callSite.actualCallee
private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
if (this is DataFlowIR.FunctionSymbol.External)
return externalModulesDFG.publicFunctions[this.hash] ?: this
return this
}
val calleeEAResult = if (callSite.call is DataFlowIR.Node.VirtualCall) {
private fun getExternalFunctionEAResult(callSite: CallGraphNode.CallSite): FunctionEscapeAnalysisResult {
val callee = callSite.actualCallee.resolved()
val calleeEAResult = if (callSite.isVirtual) {
DEBUG_OUTPUT(0) { println("A virtual call: $callee") }
getConservativeFunctionEAResult(callee)
} else {
callSite.call as DataFlowIR.Node.StaticCall
callee as DataFlowIR.FunctionSymbol.External
DEBUG_OUTPUT(0) { println("An external call: $callee") }
FunctionEscapeAnalysisResult.fromBits(
callee.escapes ?: 0,
@@ -408,14 +465,11 @@ internal object EscapeAnalysis {
val beingReturned = roles.has(Role.RETURN_VALUE)
var parameterPointingOnUs: Int? = null
var pointsMoreThanOneParameter = false
val parametersPointingOnUs = mutableSetOf<Int>()
fun addIncomingParameter(parameter: Int) {
if (pointsMoreThanOneParameter) return
if (parameterPointingOnUs == null)
parameterPointingOnUs = parameter
else pointsMoreThanOneParameter = true
if (kind == PointsToGraphNodeKind.ESCAPES) return
parametersPointingOnUs += parameter
}
}
@@ -431,25 +485,28 @@ internal object EscapeAnalysis {
PointsToGraphNodeKind.ESCAPES -> Lifetime.GLOBAL
PointsToGraphNodeKind.LOCAL -> {
if (it.pointsMoreThanOneParameter)
Lifetime.GLOBAL
else {
val parameterPointingOnUs = it.parameterPointingOnUs
if (parameterPointingOnUs != null)
if (it.parametersPointingOnUs.isEmpty()) {
// A value is neither stored into a global nor into any parameter nor into the return value -
// it can be allocated locally.
Lifetime.LOCAL
} else {
if (it.parametersPointingOnUs.size == 1) { // TODO: remove.
// A value is stored into a parameter field.
Lifetime.PARAMETER_FIELD(parameterPointingOnUs)
else
// A value is neither stored into a global nor into any parameter nor into the return value -
// it can be allocated locally.
Lifetime.LOCAL
Lifetime.PARAMETER_FIELD(it.parametersPointingOnUs.first())
} else {
// A value is stored into several parameters fields.
Lifetime.PARAMETERS_FIELD(it.parametersPointingOnUs.toIntArray(), false)
}
}
}
PointsToGraphNodeKind.RETURN_VALUE -> {
when {
it.parameterPointingOnUs != null -> Lifetime.GLOBAL
// If a value is explicitly returned.
returnValues.contains(node) -> Lifetime.RETURN_VALUE
it.parametersPointingOnUs.isNotEmpty() -> Lifetime.PARAMETERS_FIELD(it.parametersPointingOnUs.toIntArray(), true)
// A value is stored into a field of the return value.
else -> Lifetime.INDIRECT_RETURN_VALUE
}
@@ -515,25 +572,38 @@ internal object EscapeAnalysis {
}
}
fun processCall(callSite: DataFlowIR.Node.Call, calleeEscapeAnalysisResult: FunctionEscapeAnalysisResult) {
fun print_digraph() {
println("digraph {")
val ids = ids ?: functionAnalysisResult.function.body.nodes.withIndex().associateBy({ it.value }, { it.index })
nodes.forEach { t, u ->
u.edges.forEach {
println(" ${ids[t]} -> ${ids[it]};")
}
}
println("}")
}
fun processCall(callSite: CallGraphNode.CallSite, calleeEscapeAnalysisResult: FunctionEscapeAnalysisResult) {
val call = callSite.call
DEBUG_OUTPUT(0) {
println("Processing callSite")
println(nodeToStringWhole(callSite))
println(nodeToStringWhole(call))
println("Actual callee: ${callSite.actualCallee}")
println("Callee escape analysis result:")
println(calleeEscapeAnalysisResult.toString())
}
val arguments = if (callSite is DataFlowIR.Node.NewObject) {
(0..callSite.arguments.size).map {
if (it == 0) callSite else callSite.arguments[it - 1].node
val arguments = if (call is DataFlowIR.Node.NewObject) {
(0..call.arguments.size).map {
if (it == 0) call else call.arguments[it - 1].node
}
} else {
(0..callSite.arguments.size).map {
if (it < callSite.arguments.size) callSite.arguments[it].node else callSite
(0..call.arguments.size).map {
if (it < call.arguments.size) call.arguments[it].node else call
}
}
for (index in 0..callSite.arguments.size) {
for (index in 0..call.arguments.size) {
val parameterEAResult = calleeEscapeAnalysisResult.parameters[index]
val from = arguments[index]
if (parameterEAResult.escapes) {
@@ -660,12 +730,12 @@ internal object EscapeAnalysis {
}
}
fun computeLifetimes(moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
callGraph: CallGraph, lifetimes: MutableMap<IrElement, Lifetime>) {
assert(lifetimes.isEmpty())
val intraproceduralAnalysisResult =
IntraproceduralAnalysis(moduleDFG.functions + externalModulesDFG.functionDFGs, callGraph).analyze()
InterproceduralAnalysis(callGraph, intraproceduralAnalysisResult, lifetimes).analyze()
IntraproceduralAnalysis(context, moduleDFG, externalModulesDFG, callGraph).analyze()
InterproceduralAnalysis(callGraph, intraproceduralAnalysisResult, externalModulesDFG, lifetimes).analyze()
}
}