Made escape analysis up to date with devirtualization
This commit is contained in:
+2
-2
@@ -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"),
|
||||
|
||||
+2
@@ -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,
|
||||
|
||||
+24
-33
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -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 ->
|
||||
|
||||
+2
-1
@@ -661,7 +661,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
receiverType,
|
||||
name.localHash.value,
|
||||
takeName { name }
|
||||
)
|
||||
),
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+16
-15
@@ -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 -> {
|
||||
|
||||
+12
-7
@@ -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 -> {
|
||||
|
||||
+2
-1
@@ -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,
|
||||
|
||||
+115
-45
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user