DFG: Moved parameter & return types from body to symbol

This commit is contained in:
Igor Chevdar
2018-03-05 17:43:42 +03:00
parent e3857223eb
commit 1c2fa52eba
5 changed files with 220 additions and 207 deletions
@@ -424,7 +424,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "doResume" }.symbol .filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "doResume" }.symbol
private val getContinuationSymbol = context.ir.symbols.getContinuation private val getContinuationSymbol = context.ir.symbols.getContinuation
private val continuationType = getContinuationSymbol.descriptor.returnType!!
private val arrayGetSymbol = context.ir.symbols.arrayGet private val arrayGetSymbol = context.ir.symbols.arrayGet
private val arraySetSymbol = context.ir.symbols.arraySet private val arraySetSymbol = context.ir.symbols.arraySet
@@ -471,12 +470,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
) )
} }
private fun choosePrimary(erasure: List<ClassDescriptor>): ClassDescriptor {
if (erasure.size == 1) return erasure[0]
// A parameter with constraints - choose class if exists.
return erasure.singleOrNull { !it.isInterface } ?: context.ir.symbols.any.owner
}
fun build(): DataFlowIR.Function { fun build(): DataFlowIR.Function {
val isSuspend = descriptor is IrSimpleFunction && descriptor.isSuspend val isSuspend = descriptor is IrSimpleFunction && descriptor.isSuspend
@@ -506,14 +499,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val allNodes = nodes.values + variables.values + templateParameters.values + returnsNode + throwsNode + val allNodes = nodes.values + variables.values + templateParameters.values + returnsNode + throwsNode +
(if (isSuspend) listOf(continuationParameter!!) else emptyList()) (if (isSuspend) listOf(continuationParameter!!) else emptyList())
val parameterTypes = (allParameters.map { it.type } + (if (isSuspend) listOf(continuationType) else emptyList()))
.map { symbolTable.mapClass(choosePrimary(it.erasure(context))) }
.toTypedArray()
val returnType = if (isSuspend) context.builtIns.anyType else returnNodeType
return DataFlowIR.Function( return DataFlowIR.Function(
symbol = symbolTable.mapFunction(descriptor), symbol = symbolTable.mapFunction(descriptor),
parameterTypes = parameterTypes,
returnType = symbolTable.mapType(returnType),
body = DataFlowIR.FunctionBody(allNodes.distinct().toList(), returnsNode, throwsNode) body = DataFlowIR.FunctionBody(allNodes.distinct().toList(), returnsNode, throwsNode)
) )
} }
@@ -606,7 +593,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapClass(owner), symbolTable.mapClass(owner),
callee.functionName.localHash.value, callee.functionName.localHash.value,
arguments, arguments,
symbolTable.mapType(callee.returnType),
value value
) )
} else { } else {
@@ -617,7 +603,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapClass(owner), symbolTable.mapClass(owner),
vtableIndex, vtableIndex,
arguments, arguments,
symbolTable.mapType(callee.returnType),
value value
) )
} }
@@ -626,7 +611,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.StaticCall( DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(actualCallee), symbolTable.mapFunction(actualCallee),
arguments, arguments,
symbolTable.mapType(actualCallee.returnType),
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) }, actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) },
value value
) )
@@ -642,7 +626,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.StaticCall( DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(value.symbol.owner), symbolTable.mapFunction(value.symbol.owner),
arguments.map { expressionToEdge(it) }, arguments.map { expressionToEdge(it) },
symbolTable.mapClass(context.ir.symbols.unit.owner),
symbolTable.mapType(thiz.type), symbolTable.mapType(thiz.type),
value value
) )
@@ -300,56 +300,59 @@ internal object DFGSerializer {
} }
} }
class ExternalFunctionSymbol(val hash: Long, val numberOfParameters: Int, val isGlobalInitializer: Boolean, class FunctionSymbolBase(val parameterTypes: IntArray, val returnType: Int, val isGlobalInitializer: Boolean) {
val escapes: Int?, val pointsTo: IntArray?, val name: String?) {
constructor(data: ArraySlice) : this(data.readLong(), data.readInt(), data.readBoolean(), data.readNullableInt(), constructor(data: ArraySlice) : this(data.readIntArray(), data.readInt(), data.readBoolean())
fun write(result: ArraySlice) {
result.writeIntArray(parameterTypes)
result.writeInt(returnType)
result.writeBoolean(isGlobalInitializer)
}
}
class ExternalFunctionSymbol(val hash: Long, val escapes: Int?, val pointsTo: IntArray?, val name: String?) {
constructor(data: ArraySlice) : this(data.readLong(), data.readNullableInt(),
data.readNullable { readIntArray() }, data.readNullableString()) data.readNullable { readIntArray() }, data.readNullableString())
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeLong(hash) result.writeLong(hash)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeNullableInt(escapes) result.writeNullableInt(escapes)
result.writeNullable(pointsTo) { writeIntArray(it) } result.writeNullable(pointsTo) { writeIntArray(it) }
result.writeNullableString(name) result.writeNullableString(name)
} }
} }
class PublicFunctionSymbol(val hash: Long, val numberOfParameters: Int, val isGlobalInitializer: Boolean, class PublicFunctionSymbol(val hash: Long, val index: Int, val bridgeTarget: Int?, val name: String?) {
val index: Int, val bridgeTarget: Int?, val name: String?) {
constructor(data: ArraySlice) : this(data.readLong(), data.readInt(), data.readBoolean(), data.readInt(), constructor(data: ArraySlice) : this(data.readLong(), data.readInt(),
data.readNullableInt(), data.readNullableString()) data.readNullableInt(), data.readNullableString())
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeLong(hash) result.writeLong(hash)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeInt(index) result.writeInt(index)
result.writeNullableInt(bridgeTarget) result.writeNullableInt(bridgeTarget)
result.writeNullableString(name) result.writeNullableString(name)
} }
} }
class PrivateFunctionSymbol(val index: Int, val numberOfParameters: Int, val isGlobalInitializer: Boolean, class PrivateFunctionSymbol(val index: Int, val bridgeTarget: Int?, val name: String?) {
val bridgeTarget: Int?, val name: String?) {
constructor(data: ArraySlice) : this(data.readInt(), data.readInt(), data.readBoolean(), constructor(data: ArraySlice) : this(data.readInt(), data.readNullableInt(), data.readNullableString())
data.readNullableInt(), data.readNullableString())
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeInt(index) result.writeInt(index)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeNullableInt(bridgeTarget) result.writeNullableInt(bridgeTarget)
result.writeNullableString(name) result.writeNullableString(name)
} }
} }
class FunctionSymbol(val external: ExternalFunctionSymbol?, val public: PublicFunctionSymbol?, val private: PrivateFunctionSymbol?) { class FunctionSymbol(val base: FunctionSymbolBase, val external: ExternalFunctionSymbol?,
val public: PublicFunctionSymbol?, val private: PrivateFunctionSymbol?) {
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
base.write(result)
result.writeByte( result.writeByte(
when { when {
external != null -> 1 external != null -> 1
@@ -364,22 +367,23 @@ internal object DFGSerializer {
} }
companion object { companion object {
fun external(hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean, escapes: Int?, pointsTo: IntArray?, name: String?) = fun external(base: FunctionSymbolBase, hash: Long, escapes: Int?, pointsTo: IntArray?, name: String?) =
FunctionSymbol(ExternalFunctionSymbol(hash, numberOfParameters, isGlobalInitializer, escapes, pointsTo, name), null, null) FunctionSymbol(base, ExternalFunctionSymbol(hash, escapes, pointsTo, name), null, null)
fun public(hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean, index: Int, bridgeTarget: Int?, name: String?) = fun public(base: FunctionSymbolBase, hash: Long, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(null, PublicFunctionSymbol(hash, numberOfParameters, isGlobalInitializer, index, bridgeTarget, name), null) FunctionSymbol(base, null, PublicFunctionSymbol(hash, index, bridgeTarget, name), null)
fun private(index: Int, numberOfParameters: Int, isGlobalInitializer: Boolean, bridgeTarget: Int?, name: String?) = fun private(base: FunctionSymbolBase, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(null, null, PrivateFunctionSymbol(index, numberOfParameters, isGlobalInitializer, bridgeTarget, name)) FunctionSymbol(base, null, null, PrivateFunctionSymbol(index, bridgeTarget, name))
fun read(data: ArraySlice): FunctionSymbol { fun read(data: ArraySlice): FunctionSymbol {
val base = FunctionSymbolBase(data)
val tag = data.readByte().toInt() val tag = data.readByte().toInt()
return when (tag) { return when (tag) {
1 -> FunctionSymbol(ExternalFunctionSymbol(data), null, null) 1 -> FunctionSymbol(base, ExternalFunctionSymbol(data), null, null)
2 -> FunctionSymbol(null, PublicFunctionSymbol(data), null) 2 -> FunctionSymbol(base, null, PublicFunctionSymbol(data), null)
3 -> FunctionSymbol(null, null, PrivateFunctionSymbol(data)) 3 -> FunctionSymbol(base, null, null, PrivateFunctionSymbol(data))
else -> FunctionSymbol(null, null, null) else -> FunctionSymbol(base, null, null, null)
} }
} }
} }
@@ -434,14 +438,13 @@ internal object DFGSerializer {
} }
} }
class Call(val callee: Int, val arguments: Array<Edge>, val returnType: Int) { class Call(val callee: Int, val arguments: Array<Edge>) {
constructor(data: ArraySlice) : this(data.readInt(), data.readArray { Edge(this) }, data.readInt()) constructor(data: ArraySlice) : this(data.readInt(), data.readArray { Edge(this) })
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeInt(callee) result.writeInt(callee)
result.writeArray(arguments) { it.write(this) } result.writeArray(arguments) { it.write(this) }
result.writeInt(returnType)
} }
} }
@@ -455,12 +458,13 @@ internal object DFGSerializer {
} }
} }
class NewObject(val call: Call) { class NewObject(val call: Call, val constructedType: Int) {
constructor(data: ArraySlice) : this(Call(data)) constructor(data: ArraySlice) : this(Call(data), data.readInt())
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
call.write(result) call.write(result)
result.writeInt(constructedType)
} }
} }
@@ -629,8 +633,8 @@ internal object DFGSerializer {
fun staticCall(call: Call, receiverType: Int?) = fun staticCall(call: Call, receiverType: Int?) =
Node().also { it.staticCall = StaticCall(call, receiverType) } Node().also { it.staticCall = StaticCall(call, receiverType) }
fun newObject(call: Call) = fun newObject(call: Call, constructedType: Int) =
Node().also { it.newObject = NewObject(call) } Node().also { it.newObject = NewObject(call, constructedType) }
fun vtableCall(virtualCall: VirtualCall, calleeVtableIndex: Int) = fun vtableCall(virtualCall: VirtualCall, calleeVtableIndex: Int) =
Node().also { it.vtableCall = VtableCall(virtualCall, calleeVtableIndex) } Node().also { it.vtableCall = VtableCall(virtualCall, calleeVtableIndex) }
@@ -690,14 +694,12 @@ internal object DFGSerializer {
} }
} }
class Function(val symbol: Int, val parameterTypes: IntArray, val returnType: Int, val body: FunctionBody) { class Function(val symbol: Int, val body: FunctionBody) {
constructor(data: ArraySlice) : this(data.readInt(), data.readIntArray(), data.readInt(), FunctionBody(data)) constructor(data: ArraySlice) : this(data.readInt(), FunctionBody(data))
fun write(result: ArraySlice) { fun write(result: ArraySlice) {
result.writeInt(symbol) result.writeInt(symbol)
result.writeIntArray(parameterTypes)
result.writeInt(returnType)
body.write(result) body.write(result)
} }
} }
@@ -790,25 +792,30 @@ internal object DFGSerializer {
val functionSymbols = functionSymbolMap.entries val functionSymbols = functionSymbolMap.entries
.sortedBy { it.value } .sortedBy { it.value }
.map { .map {
val functionSymbol = it.key
val numberOfParameters = functionSymbol.numberOfParameters fun buildFunctionSymbolBase(symbol: DataFlowIR.FunctionSymbol) =
val isGlobalInitializer = functionSymbol.isGlobalInitializer FunctionSymbolBase(
val name = functionSymbol.name symbol.parameterTypes.map { typeMap[it]!! }.toIntArray(),
val bridgeTarget = (functionSymbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] } typeMap[symbol.returnType]!!,
when (functionSymbol) { symbol.isGlobalInitializer
)
val symbol = it.key
val bridgeTarget = (symbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] }
when (symbol) {
is DataFlowIR.FunctionSymbol.External -> is DataFlowIR.FunctionSymbol.External ->
FunctionSymbol.external(functionSymbol.hash, numberOfParameters, isGlobalInitializer, FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash,
functionSymbol.escapes, functionSymbol.pointsTo, name) symbol.escapes, symbol.pointsTo, symbol.name)
is DataFlowIR.FunctionSymbol.Public -> is DataFlowIR.FunctionSymbol.Public ->
FunctionSymbol.public(functionSymbol.hash, numberOfParameters, FunctionSymbol.public(buildFunctionSymbolBase(symbol), symbol.hash,
isGlobalInitializer, functionSymbol.symbolTableIndex, bridgeTarget, name) symbol.symbolTableIndex, bridgeTarget, symbol.name)
is DataFlowIR.FunctionSymbol.Private -> is DataFlowIR.FunctionSymbol.Private ->
FunctionSymbol.private(functionSymbol.symbolTableIndex, numberOfParameters, FunctionSymbol.private(buildFunctionSymbolBase(symbol), symbol.symbolTableIndex,
isGlobalInitializer, bridgeTarget, name) bridgeTarget, symbol.name)
else -> error("Unknown function symbol $functionSymbol") else -> error("Unknown function symbol $symbol")
} }
} }
.toTypedArray() .toTypedArray()
@@ -827,8 +834,7 @@ internal object DFGSerializer {
fun buildCall(call: DataFlowIR.Node.Call) = fun buildCall(call: DataFlowIR.Node.Call) =
Call( Call(
functionSymbolMap[call.callee]!!, functionSymbolMap[call.callee]!!,
call.arguments.map { buildEdge(it) }.toTypedArray(), call.arguments.map { buildEdge(it) }.toTypedArray()
typeMap[call.returnType]!!
) )
fun buildVirtualCall(virtualCall: DataFlowIR.Node.VirtualCall) = fun buildVirtualCall(virtualCall: DataFlowIR.Node.VirtualCall) =
@@ -845,7 +851,8 @@ internal object DFGSerializer {
is DataFlowIR.Node.StaticCall -> is DataFlowIR.Node.StaticCall ->
Node.staticCall(buildCall(node), node.receiverType?.let { typeMap[it]!! }) Node.staticCall(buildCall(node), node.receiverType?.let { typeMap[it]!! })
is DataFlowIR.Node.NewObject -> Node.newObject(buildCall(node)) is DataFlowIR.Node.NewObject ->
Node.newObject(buildCall(node), typeMap[node.constructedType]!!)
is DataFlowIR.Node.VtableCall -> is DataFlowIR.Node.VtableCall ->
Node.vtableCall(buildVirtualCall(node), node.calleeVtableIndex) Node.vtableCall(buildVirtualCall(node), node.calleeVtableIndex)
@@ -876,10 +883,8 @@ internal object DFGSerializer {
} }
.toTypedArray() .toTypedArray()
Function( Function(
functionSymbolMap[function.symbol]!!, symbol = functionSymbolMap[function.symbol]!!,
function.parameterTypes.map { typeMap[it]!! }.toIntArray(), body = FunctionBody(nodes, nodeMap[body.returns]!!, nodeMap[body.throws]!!)
typeMap[function.returnType]!!,
FunctionBody(nodes, nodeMap[body.returns]!!, nodeMap[body.throws]!!)
) )
} }
.toTypedArray() .toTypedArray()
@@ -953,20 +958,21 @@ internal object DFGSerializer {
} }
val functionSymbols = symbolTable.functionSymbols.map { val functionSymbols = symbolTable.functionSymbols.map {
val isGlobalInitializer = it.base.isGlobalInitializer
val external = it.external val external = it.external
val public = it.public val public = it.public
val private = it.private val private = it.private
when { when {
external != null -> external != null ->
DataFlowIR.FunctionSymbol.External(external.hash, external.numberOfParameters, DataFlowIR.FunctionSymbol.External(external.hash,
external.isGlobalInitializer, external.escapes, external.pointsTo, external.name) isGlobalInitializer, external.escapes, external.pointsTo, external.name)
public != null -> { public != null -> {
val symbolTableIndex = public.index val symbolTableIndex = public.index
if (symbolTableIndex >= 0) if (symbolTableIndex >= 0)
++module.numberOfFunctions ++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Public(public.hash, public.numberOfParameters, module, DataFlowIR.FunctionSymbol.Public(public.hash,
symbolTableIndex, public.isGlobalInitializer, null, public.name).also { module, symbolTableIndex, isGlobalInitializer, null, public.name).also {
publicFunctionsMap.put(it.hash, it) publicFunctionsMap.put(it.hash, it)
} }
} }
@@ -975,9 +981,12 @@ internal object DFGSerializer {
val symbolTableIndex = private!!.index val symbolTableIndex = private!!.index
if (symbolTableIndex >= 0) if (symbolTableIndex >= 0)
++module.numberOfFunctions ++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Private(privateFunIndex++, private.numberOfParameters, module, DataFlowIR.FunctionSymbol.Private(privateFunIndex++,
symbolTableIndex, private.isGlobalInitializer, null, private.name) module, symbolTableIndex, isGlobalInitializer, null, private.name)
} }
}.apply {
parameterTypes = it.base.parameterTypes.map { types[it] }.toTypedArray()
returnType = types[it.base.returnType]
} }
} }
@@ -1018,8 +1027,7 @@ internal object DFGSerializer {
DataFlowIR.Node.Call( DataFlowIR.Node.Call(
functionSymbols[call.callee], functionSymbols[call.callee],
call.arguments.map { deserializeEdge(it) }, call.arguments.map { deserializeEdge(it) },
types[call.returnType], irCallSite = null
null
) )
fun deserializeVirtualCall(virtualCall: VirtualCall): DataFlowIR.Node.VirtualCall { fun deserializeVirtualCall(virtualCall: VirtualCall): DataFlowIR.Node.VirtualCall {
@@ -1027,9 +1035,8 @@ internal object DFGSerializer {
return DataFlowIR.Node.VirtualCall( return DataFlowIR.Node.VirtualCall(
call.callee, call.callee,
call.arguments, call.arguments,
call.returnType,
types[virtualCall.receiverType], types[virtualCall.receiverType],
null irCallSite = null
) )
} }
@@ -1049,12 +1056,13 @@ internal object DFGSerializer {
val staticCall = it.staticCall!! val staticCall = it.staticCall!!
val call = deserializeCall(staticCall.call) val call = deserializeCall(staticCall.call)
val receiverType = staticCall.receiverType?.let { types[it] } val receiverType = staticCall.receiverType?.let { types[it] }
DataFlowIR.Node.StaticCall(call.callee, call.arguments, call.returnType, receiverType, null) DataFlowIR.Node.StaticCall(call.callee, call.arguments, receiverType, irCallSite = null)
} }
NodeType.NEW_OBJECT -> { NodeType.NEW_OBJECT -> {
val call = deserializeCall(it.newObject!!.call) val newObject = it.newObject!!
DataFlowIR.Node.NewObject(call.callee, call.arguments, call.returnType, null) val call = deserializeCall(newObject.call)
DataFlowIR.Node.NewObject(call.callee, call.arguments, types[newObject.constructedType], irCallSite = null)
} }
NodeType.VTABLE_CALL -> { NodeType.VTABLE_CALL -> {
@@ -1065,8 +1073,7 @@ internal object DFGSerializer {
virtualCall.receiverType, virtualCall.receiverType,
vtableCall.calleeVtableIndex, vtableCall.calleeVtableIndex,
virtualCall.arguments, virtualCall.arguments,
virtualCall.returnType, virtualCall.irCallSite
virtualCall.callSite
) )
} }
@@ -1078,8 +1085,7 @@ internal object DFGSerializer {
virtualCall.receiverType, virtualCall.receiverType,
itableCall.calleeHash, itableCall.calleeHash,
virtualCall.arguments, virtualCall.arguments,
virtualCall.returnType, virtualCall.irCallSite
virtualCall.callSite
) )
} }
@@ -1185,8 +1191,6 @@ internal object DFGSerializer {
val symbol = functionSymbols[it.symbol] val symbol = functionSymbols[it.symbol]
val function = DataFlowIR.Function( val function = DataFlowIR.Function(
symbol = symbol, symbol = symbol,
parameterTypes = it.parameterTypes.map { types[it] }.toTypedArray(),
returnType = types[it.returnType],
body = deserializeBody(it.body) body = deserializeBody(it.body)
) )
functions.put(symbol, function) functions.put(symbol, function)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.optimizations
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.target
import org.jetbrains.kotlin.backend.konan.irasdescriptors.* import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.isExported import org.jetbrains.kotlin.backend.konan.llvm.isExported
@@ -117,10 +118,13 @@ internal object DataFlowIR {
var numberOfClasses = 0 var numberOfClasses = 0
} }
abstract class FunctionSymbol(val numberOfParameters: Int, val isGlobalInitializer: Boolean, val name: String?) { abstract class FunctionSymbol(val isGlobalInitializer: Boolean, val name: String?) {
class External(val hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean, lateinit var parameterTypes: Array<Type>
lateinit var returnType: Type
class External(val hash: Long, isGlobalInitializer: Boolean,
val escapes: Int?, val pointsTo: IntArray?, name: String? = null) val escapes: Int?, val pointsTo: IntArray?, name: String? = null)
: FunctionSymbol(numberOfParameters, isGlobalInitializer, name) { : FunctionSymbol(isGlobalInitializer, name) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -138,15 +142,16 @@ internal object DataFlowIR {
} }
} }
abstract class Declared(numberOfParameters: Int, val module: Module, val symbolTableIndex: Int, abstract class Declared(val module: Module, val symbolTableIndex: Int,
isGlobalInitializer: Boolean, var bridgeTarget: FunctionSymbol?, name: String?) isGlobalInitializer: Boolean, var bridgeTarget: FunctionSymbol?, name: String?)
: FunctionSymbol(numberOfParameters, isGlobalInitializer, name) { : FunctionSymbol(isGlobalInitializer, name) {
} }
class Public(val hash: Long, numberOfParameters: Int, module: Module, symbolTableIndex: Int, class Public(val hash: Long, module: Module, symbolTableIndex: Int,
isGlobalInitializer: Boolean, bridgeTarget: FunctionSymbol?, name: String? = null) isGlobalInitializer: Boolean, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(numberOfParameters, module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) { : Declared(module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is Public) return false if (other !is Public) return false
@@ -163,9 +168,10 @@ internal object DataFlowIR {
} }
} }
class Private(val index: Int, numberOfParameters: Int, module: Module, symbolTableIndex: Int, class Private(val index: Int, module: Module, symbolTableIndex: Int,
isGlobalInitializer: Boolean, bridgeTarget: FunctionSymbol?, name: String? = null) isGlobalInitializer: Boolean, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(numberOfParameters, module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) { : Declared(module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is Private) return false if (other !is Private) return false
@@ -205,27 +211,27 @@ internal object DataFlowIR {
class Const(val type: Type) : Node() class Const(val type: Type) : Node()
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>, val returnType: Type, open class Call(val callee: FunctionSymbol, val arguments: List<Edge>,
open val callSite: IrFunctionAccessExpression?) : Node() open val irCallSite: IrFunctionAccessExpression?) : Node()
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type, class StaticCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type?, callSite: IrFunctionAccessExpression?) val receiverType: Type?, irCallSite: IrFunctionAccessExpression?)
: Call(callee, arguments, returnType, callSite) : Call(callee, arguments, irCallSite)
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, type: Type, override val callSite: IrCall?) class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, val constructedType: Type, override val irCallSite: IrCall?)
: Call(constructor, arguments, type, callSite) : Call(constructor, arguments, irCallSite)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type, open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type, override val callSite: IrCall?) val receiverType: Type, override val irCallSite: IrCall?)
: Call(callee, arguments, returnType, callSite) : Call(callee, arguments, irCallSite)
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int, class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
arguments: List<Edge>, returnType: Type, callSite: IrCall?) arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite) : VirtualCall(callee, arguments, receiverType, irCallSite)
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long, class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long,
arguments: List<Edge>, returnType: Type, callSite: IrCall?) arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite) : VirtualCall(callee, arguments, receiverType, irCallSite)
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node() class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
@@ -233,7 +239,7 @@ internal object DataFlowIR {
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node() class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node()
class ArrayRead(val array: Edge, val index: Edge, val callSite: IrCall?) : Node() class ArrayRead(val array: Edge, val index: Edge, val irCallSite: IrCall?) : Node()
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge) : Node() class ArrayWrite(val array: Edge, val index: Edge, val value: Edge) : Node()
@@ -244,14 +250,11 @@ internal object DataFlowIR {
class FunctionBody(val nodes: List<Node>, val returns: Node.Variable, val throws: Node.Variable) class FunctionBody(val nodes: List<Node>, val returns: Node.Variable, val throws: Node.Variable)
class Function(val symbol: FunctionSymbol, class Function(val symbol: FunctionSymbol, val body: FunctionBody) {
val parameterTypes: Array<Type>,
val returnType: Type,
val body: FunctionBody) {
fun debugOutput() { fun debugOutput() {
println("FUNCTION $symbol") println("FUNCTION $symbol")
println("Params: ${parameterTypes.contentToString()}") println("Params: ${symbol.parameterTypes.contentToString()}")
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index }) val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
body.nodes.forEach { body.nodes.forEach {
println(" NODE #${ids[it]!!}") println(" NODE #${ids[it]!!}")
@@ -320,7 +323,7 @@ internal object DataFlowIR {
is Node.NewObject -> { is Node.NewObject -> {
val result = StringBuilder() val result = StringBuilder()
result.appendln(" NEW OBJECT ${node.callee}") result.appendln(" NEW OBJECT ${node.callee}")
result.appendln(" TYPE ${node.returnType}") result.appendln(" CONSTRUCTED TYPE ${node.constructedType}")
node.arguments.forEach { node.arguments.forEach {
result.append(" ARG #${ids[it.node]!!}") result.append(" ARG #${ids[it.node]!!}")
if (it.castToType == null) if (it.castToType == null)
@@ -439,6 +442,9 @@ internal object DataFlowIR {
NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as org.jetbrains.kotlin.descriptors.ClassDescriptor NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as org.jetbrains.kotlin.descriptors.ClassDescriptor
private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single() private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
private val getContinuationSymbol = context.ir.symbols.getContinuation
private val continuationType = getContinuationSymbol.descriptor.returnType!!
var privateTypeIndex = 0 var privateTypeIndex = 0
var privateFunIndex = 0 var privateFunIndex = 0
@@ -486,6 +492,10 @@ internal object DataFlowIR {
Type.Public(name.localHash.value, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name }) Type.Public(name.localHash.value, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name })
else else
Type.Private(privateTypeIndex++, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name }) Type.Private(privateTypeIndex++, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name })
classMap[descriptor] = type
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
if (!isAbstract) { if (!isAbstract) {
val vtableBuilder = context.getVtableBuilder(descriptor) val vtableBuilder = context.getVtableBuilder(descriptor)
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) } type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
@@ -493,8 +503,6 @@ internal object DataFlowIR {
type.itable[it.overriddenDescriptor.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!) type.itable[it.overriddenDescriptor.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!)
} }
} }
classMap.put(descriptor, type)
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
return type return type
} }
@@ -512,50 +520,71 @@ internal object DataFlowIR {
private val FunctionDescriptor.internalName get() = getFqName(this).asString() + "#internal" private val FunctionDescriptor.internalName get() = getFqName(this).asString() + "#internal"
fun mapFunction(descriptor: DeclarationDescriptor): FunctionSymbol = descriptor.original.let { fun mapFunction(descriptor: DeclarationDescriptor): FunctionSymbol = when (descriptor) {
functionMap.getOrPut(it) { is FunctionDescriptor -> mapFunction(descriptor)
when (it) { is IrField -> mapPropertyInitializer(descriptor)
is IrField -> { else -> error("Unknown descriptor: $descriptor")
// A global property initializer. }
assert (it.parent !is IrClass) { "All local properties initializers should've been lowered" }
FunctionSymbol.Private(privateFunIndex++, 0, module, -1, true, null, takeName { "${it.symbolName}_init" }) private fun mapFunction(descriptor: FunctionDescriptor): FunctionSymbol = descriptor.target.let {
functionMap[it]?.let { return it }
val name = if (it.isExported()) it.symbolName else it.internalName
val symbol = when {
it.module != irModule.descriptor || it.isExternal || (it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB) -> {
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES)
val pointsToAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_POINTS_TO)
@Suppress("UNCHECKED_CAST")
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, false, escapesBitMask,
pointsToBitMask?.let { it.map { it.value }.toIntArray() }, takeName { name })
}
else -> {
val isAbstract = it is SimpleFunctionDescriptor && it.modality == Modality.ABSTRACT
val classDescriptor = it.containingDeclaration as? ClassDescriptor
val bridgeTarget = it.bridgeTarget
val isSpecialBridge = bridgeTarget.let {
it != null && BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(it.descriptor) != null
} }
val bridgeTargetSymbol = if (isSpecialBridge || bridgeTarget == null) null else mapFunction(bridgeTarget)
is FunctionDescriptor -> { val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
val name = if (it.isExported()) it.symbolName else it.internalName && classDescriptor.kind != ClassKind.ANNOTATION_CLASS
val numberOfParameters = it.allParameters.size + if (it.isSuspend) 1 else 0 && (it.isOverridableOrOverrides || bridgeTarget != null || !classDescriptor.isFinal())
if (it.module != irModule.descriptor || it.isExternal || (it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) { val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES) if (it.isExported())
val pointsToAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_POINTS_TO) FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
@Suppress("UNCHECKED_CAST") else
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
@Suppress("UNCHECKED_CAST")
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
FunctionSymbol.External(name.localHash.value, numberOfParameters, false, escapesBitMask,
pointsToBitMask?.let { it.map { it.value }.toIntArray() }, takeName { name })
} else {
val isAbstract = it is SimpleFunctionDescriptor && it.modality == Modality.ABSTRACT
val classDescriptor = it.containingDeclaration as? ClassDescriptor
val bridgeTarget = it.bridgeTarget
val isSpecialBridge = bridgeTarget.let {
it != null && BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(it.descriptor) != null
}
val bridgeTargetSymbol = if (isSpecialBridge || bridgeTarget == null) null else mapFunction(bridgeTarget)
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
&& classDescriptor.kind != ClassKind.ANNOTATION_CLASS
&& (it.isOverridableOrOverrides || bridgeTarget != null || !classDescriptor.isFinal())
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
if (it.isExported())
FunctionSymbol.Public(name.localHash.value, numberOfParameters, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
else
FunctionSymbol.Private(privateFunIndex++, numberOfParameters, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
}
}
else -> error("Unknown descriptor: $it")
} }
} }
functionMap[it] = symbol
symbol.parameterTypes =
(descriptor.allParameters.map { it.type } + (if (descriptor.isSuspend) listOf(continuationType) else emptyList()))
.map { mapClass(choosePrimary(it.erasure(context))) }
.toTypedArray()
symbol.returnType = mapType(if (descriptor.isSuspend)
context.builtIns.anyType
else
descriptor.returnType)
return symbol
}
private fun mapPropertyInitializer(descriptor: IrField): FunctionSymbol = descriptor.original.let {
functionMap[it]?.let { return it }
assert(it.parent !is IrClass) { "All local properties initializers should've been lowered" }
val symbol = FunctionSymbol.Private(privateFunIndex++, module, -1, true, null, takeName { "${it.symbolName}_init" })
functionMap[it] = symbol
symbol.parameterTypes = emptyArray()
symbol.returnType = mapClass(context.ir.symbols.unit.owner)
return symbol
} }
fun getPrivateFunctionsTableForExport() = fun getPrivateFunctionsTableForExport() =
@@ -226,8 +226,7 @@ internal object Devirtualization {
} }
} }
private inner class InstantiationsSearcher(val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>, private inner class InstantiationsSearcher(val rootSet: List<DataFlowIR.FunctionSymbol>,
val rootSet: List<DataFlowIR.FunctionSymbol>,
val typeHierarchy: TypeHierarchy) { val typeHierarchy: TypeHierarchy) {
private val visited = mutableSetOf<DataFlowIR.FunctionSymbol>() private val visited = mutableSetOf<DataFlowIR.FunctionSymbol>()
private val typesVirtualCallSites = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Node.VirtualCall>>() private val typesVirtualCallSites = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Node.VirtualCall>>()
@@ -236,13 +235,12 @@ internal object Devirtualization {
fun search(): Set<DataFlowIR.Type.Declared> { fun search(): Set<DataFlowIR.Type.Declared> {
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph. // Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
// Add all final parameters of the roots. // Add all final parameters of the roots.
rootSet.map { functions[it]!! } rootSet.forEach {
.forEach { it.parameterTypes
it.parameterTypes .map { it.resolved() }
.map { it.resolved() } .filter { it.isFinal }
.filter { it.isFinal } .forEach { addInstantiatingClass(it) }
.forEach { addInstantiatingClass(it) } }
}
if (entryPoint == null) { if (entryPoint == null) {
// For library assume all public non-abstract classes could be instantiated. // For library assume all public non-abstract classes could be instantiated.
moduleDFG.symbolTable.classMap.values moduleDFG.symbolTable.classMap.values
@@ -252,7 +250,7 @@ internal object Devirtualization {
.forEach { addInstantiatingClass(it) } .forEach { addInstantiatingClass(it) }
} }
// Traverse call graph from the roots. // Traverse call graph from the roots.
rootSet.forEach { dfs(it, functions[it]!!.returnType) } rootSet.forEach { dfs(it) }
return instantiatingClasses return instantiatingClasses
} }
@@ -281,7 +279,7 @@ internal object Devirtualization {
else -> error("Unreachable") else -> error("Unreachable")
} }
dfs(callee, virtualCall.returnType) dfs(callee)
} }
private fun checkSupertypes(type: DataFlowIR.Type.Declared, private fun checkSupertypes(type: DataFlowIR.Type.Declared,
@@ -316,13 +314,13 @@ internal object Devirtualization {
.forEach { checkSupertypes(it, inheritor, seenTypes) } .forEach { checkSupertypes(it, inheritor, seenTypes) }
} }
private fun dfs(symbol: DataFlowIR.FunctionSymbol, returnType: DataFlowIR.Type) { private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
val resolvedFunctionSymbol = symbol.resolved() val resolvedFunctionSymbol = symbol.resolved()
if (resolvedFunctionSymbol is DataFlowIR.FunctionSymbol.External) { if (resolvedFunctionSymbol is DataFlowIR.FunctionSymbol.External) {
DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") } DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") }
val resolvedReturnType = returnType.resolved() val resolvedReturnType = symbol.returnType.resolved()
if (resolvedReturnType.isFinal) { if (resolvedReturnType.isFinal) {
DEBUG_OUTPUT(1) { println("Adding return type as it is final") } DEBUG_OUTPUT(1) { println("Adding return type as it is final") }
@@ -342,25 +340,25 @@ internal object Devirtualization {
nodeLoop@for (node in function.body.nodes) { nodeLoop@for (node in function.body.nodes) {
when (node) { when (node) {
is DataFlowIR.Node.NewObject -> { is DataFlowIR.Node.NewObject -> {
addInstantiatingClass(node.returnType) addInstantiatingClass(node.constructedType)
dfs(node.callee, node.returnType) dfs(node.callee)
} }
is DataFlowIR.Node.Singleton -> { is DataFlowIR.Node.Singleton -> {
addInstantiatingClass(node.type) addInstantiatingClass(node.type)
node.constructor?.let { dfs(it, node.type) } node.constructor?.let { dfs(it) }
} }
is DataFlowIR.Node.Const -> addInstantiatingClass(node.type) is DataFlowIR.Node.Const -> addInstantiatingClass(node.type)
is DataFlowIR.Node.StaticCall -> is DataFlowIR.Node.StaticCall ->
dfs(node.callee, node.returnType) dfs(node.callee)
is DataFlowIR.Node.VirtualCall -> { is DataFlowIR.Node.VirtualCall -> {
if (node.receiverType == DataFlowIR.Type.Virtual) if (node.receiverType == DataFlowIR.Type.Virtual)
continue@nodeLoop continue@nodeLoop
val receiverType = node.receiverType.resolved() val receiverType = node.receiverType.resolved()
val vCallReturnType = node.returnType.resolved() val vCallReturnType = node.callee.returnType.resolved()
DEBUG_OUTPUT(1) { DEBUG_OUTPUT(1) {
println("Adding virtual callsite:") println("Adding virtual callsite:")
@@ -399,7 +397,7 @@ internal object Devirtualization {
val rootSet = computeRootSet(context, moduleDFG, externalModulesDFG) val rootSet = computeRootSet(context, moduleDFG, externalModulesDFG)
val instantiatingClasses = val instantiatingClasses =
InstantiationsSearcher(functions, rootSet, typeHierarchy).search() InstantiationsSearcher(rootSet, typeHierarchy).search()
.withIndex() .withIndex()
.associate { it.value to (it.index + 1 /* 0 is reserved for [DataFlowIR.Type.Virtual] */) } .associate { it.value to (it.index + 1 /* 0 is reserved for [DataFlowIR.Type.Virtual] */) }
val allTypes = listOf(DataFlowIR.Type.Virtual) + instantiatingClasses.asSequence().sortedBy { it.value }.map { it.key } val allTypes = listOf(DataFlowIR.Type.Virtual) + instantiatingClasses.asSequence().sortedBy { it.value }.map { it.key }
@@ -531,7 +529,7 @@ internal object Devirtualization {
if (virtualCallSiteReceivers == null || virtualCallSiteReceivers.receiver.types[VIRTUAL_TYPE_ID]) { if (virtualCallSiteReceivers == null || virtualCallSiteReceivers.receiver.types[VIRTUAL_TYPE_ID]) {
DEBUG_OUTPUT(0) { DEBUG_OUTPUT(0) {
println("Unable to devirtualize callsite ${virtualCall.callSite?.let { ir2stringWhole(it) } ?: virtualCall.toString() }") println("Unable to devirtualize callsite ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString() }")
println(" receiver is Virtual") println(" receiver is Virtual")
} }
@@ -542,16 +540,16 @@ internal object Devirtualization {
.map { it.value } .map { it.value }
.filter { it != nothing } .filter { it != nothing }
val map = virtualCallSiteReceivers.devirtualizedCallees.associateBy({ it.receiverType }, { it }) val map = virtualCallSiteReceivers.devirtualizedCallees.associateBy({ it.receiverType }, { it })
result[virtualCall] = DevirtualizedCallSite(possibleReceivers.map { receiverType -> result[virtualCall] = DevirtualizedCallSite(virtualCall.callee.resolved(), possibleReceivers.map { receiverType ->
assert (map[receiverType] != null) { assert (map[receiverType] != null) {
"Non-expected receiver type $receiverType at call site: " + "Non-expected receiver type $receiverType at call site: " +
(virtualCall.callSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()) (virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString())
} }
val devirtualizedCallee = map[receiverType]!! val devirtualizedCallee = map[receiverType]!!
val callee = devirtualizedCallee.callee val callee = devirtualizedCallee.callee
if (callee is DataFlowIR.FunctionSymbol.Declared && callee.symbolTableIndex < 0) if (callee is DataFlowIR.FunctionSymbol.Declared && callee.symbolTableIndex < 0)
error("Function ${devirtualizedCallee.receiverType}.$callee cannot be called virtually," + error("Function ${devirtualizedCallee.receiverType}.$callee cannot be called virtually," +
" but actually is at call site: ${virtualCall.callSite?.let { ir2stringWhole(it) } ?: virtualCall.toString() }") " but actually is at call site: ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString() }")
devirtualizedCallee devirtualizedCallee
}) to virtualCallSiteReceivers.caller }) to virtualCallSiteReceivers.caller
} }
@@ -559,10 +557,10 @@ internal object Devirtualization {
DEBUG_OUTPUT(0) { DEBUG_OUTPUT(0) {
println("Devirtualized from current module:") println("Devirtualized from current module:")
result.forEach { virtualCall, devirtualizedCallSite -> result.forEach { virtualCall, devirtualizedCallSite ->
if (virtualCall.callSite != null) { if (virtualCall.irCallSite != null) {
println("DEVIRTUALIZED") println("DEVIRTUALIZED")
println("FUNCTION: ${devirtualizedCallSite.second}") println("FUNCTION: ${devirtualizedCallSite.second}")
println("CALL SITE: ${virtualCall.callSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}") println("CALL SITE: ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}")
println("POSSIBLE RECEIVERS:") println("POSSIBLE RECEIVERS:")
devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") } devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") }
devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") } devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") }
@@ -571,10 +569,10 @@ internal object Devirtualization {
} }
println("Devirtualized from external modules:") println("Devirtualized from external modules:")
result.forEach { virtualCall, devirtualizedCallSite -> result.forEach { virtualCall, devirtualizedCallSite ->
if (virtualCall.callSite == null) { if (virtualCall.irCallSite == null) {
println("DEVIRTUALIZED") println("DEVIRTUALIZED")
println("FUNCTION: ${devirtualizedCallSite.second}") println("FUNCTION: ${devirtualizedCallSite.second}")
println("CALL SITE: ${virtualCall.callSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}") println("CALL SITE: ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString()}")
println("POSSIBLE RECEIVERS:") println("POSSIBLE RECEIVERS:")
devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") } devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") }
devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") } devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") }
@@ -647,11 +645,10 @@ internal object Devirtualization {
if (symbol is DataFlowIR.FunctionSymbol.External) return null if (symbol is DataFlowIR.FunctionSymbol.External) return null
constraintGraph.functions[symbol]?.let { return it } constraintGraph.functions[symbol]?.let { return it }
val function = functions[symbol] ?: error("Unknown function: $symbol") val parameters = Array(symbol.parameterTypes.size) { ordinaryNode { "Param#$it\$$symbol" } }
val parameters = Array(symbol.numberOfParameters) { ordinaryNode { "Param#$it\$$symbol" } }
if (isRoot) { if (isRoot) {
// Exported function from the current module. // Exported function from the current module.
function.parameterTypes.forEachIndexed { index, type -> symbol.parameterTypes.forEachIndexed { index, type ->
val resolvedType = type.resolved() val resolvedType = type.resolved()
val node = if (!resolvedType.isFinal) val node = if (!resolvedType.isFinal)
constraintGraph.virtualNode constraintGraph.virtualNode
@@ -769,10 +766,10 @@ internal object Devirtualization {
function.parameters[node.index] function.parameters[node.index]
is DataFlowIR.Node.StaticCall -> is DataFlowIR.Node.StaticCall ->
doCall(node.callee, node.arguments, node.returnType.resolved(), node.receiverType?.resolved()) doCall(node.callee, node.arguments, node.callee.returnType.resolved(), node.receiverType?.resolved())
is DataFlowIR.Node.NewObject -> { is DataFlowIR.Node.NewObject -> {
val returnType = node.returnType.resolved() val returnType = node.constructedType.resolved()
val instanceNode = concreteClass(returnType) val instanceNode = concreteClass(returnType)
doCall(node.callee, listOf(instanceNode) + node.arguments, returnType, null) doCall(node.callee, listOf(instanceNode) + node.arguments, returnType, null)
instanceNode instanceNode
@@ -812,7 +809,7 @@ internal object Devirtualization {
println() println()
} }
val returnType = node.returnType.resolved() val returnType = node.callee.returnType.resolved()
val receiverNode = edgeToConstraintNode(node.arguments[0]) val receiverNode = edgeToConstraintNode(node.arguments[0])
if (receiverType == DataFlowIR.Type.Virtual) if (receiverType == DataFlowIR.Type.Virtual)
constraintGraph.virtualNode.addEdge(receiverNode) constraintGraph.virtualNode.addEdge(receiverNode)
@@ -891,7 +888,7 @@ internal object Devirtualization {
class DevirtualizedCallee(val receiverType: DataFlowIR.Type, val callee: DataFlowIR.FunctionSymbol) class DevirtualizedCallee(val receiverType: DataFlowIR.Type, val callee: DataFlowIR.FunctionSymbol)
class DevirtualizedCallSite(val possibleCallees: List<DevirtualizedCallee>) class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>) class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>)
@@ -901,8 +898,8 @@ internal object Devirtualization {
val devirtualizedCallSites = val devirtualizedCallSites =
devirtualizationAnalysisResult devirtualizationAnalysisResult
.asSequence() .asSequence()
.filter { it.key.callSite != null } .filter { it.key.irCallSite != null }
.associate { it.key.callSite!! to it.value } .associate { it.key.irCallSite!! to it.value }
Devirtualization.devirtualize(irModule, context, devirtualizedCallSites) Devirtualization.devirtualize(irModule, context, devirtualizedCallSites)
return AnalysisResult(devirtualizationAnalysisResult) return AnalysisResult(devirtualizationAnalysisResult)
} }
@@ -266,7 +266,7 @@ internal object EscapeAnalysis {
} }
for (functionSymbol in callGraph.directEdges.keys) { for (functionSymbol in callGraph.directEdges.keys) {
val numberOfParameters = intraproceduralAnalysisResult[functionSymbol]!!.function.parameterTypes.size val numberOfParameters = functionSymbol.parameterTypes.size
escapeAnalysisResults[functionSymbol] = FunctionEscapeAnalysisResult( escapeAnalysisResults[functionSymbol] = FunctionEscapeAnalysisResult(
// Assume no edges at the beginning. // Assume no edges at the beginning.
// Then iteratively add needed. // Then iteratively add needed.
@@ -321,7 +321,7 @@ internal object EscapeAnalysis {
for (graph in pointsToGraphs.values) { for (graph in pointsToGraphs.values) {
graph.nodes.keys graph.nodes.keys
.filterIsInstance<DataFlowIR.Node.Call>() .filterIsInstance<DataFlowIR.Node.Call>()
.forEach { call -> call.callSite?.let { lifetimes.put(it, graph.lifetimeOf(call)) } } .forEach { call -> call.irCallSite?.let { lifetimes.put(it, graph.lifetimeOf(call)) } }
} }
} }
@@ -355,7 +355,7 @@ internal object EscapeAnalysis {
} }
private fun getConservativeFunctionEAResult(symbol: DataFlowIR.FunctionSymbol): FunctionEscapeAnalysisResult { private fun getConservativeFunctionEAResult(symbol: DataFlowIR.FunctionSymbol): FunctionEscapeAnalysisResult {
val numberOfParameters = symbol.numberOfParameters val numberOfParameters = symbol.parameterTypes.size
return FunctionEscapeAnalysisResult((0..numberOfParameters).map { return FunctionEscapeAnalysisResult((0..numberOfParameters).map {
ParameterEscapeAnalysisResult( ParameterEscapeAnalysisResult(
escapes = true, escapes = true,
@@ -378,7 +378,7 @@ internal object EscapeAnalysis {
FunctionEscapeAnalysisResult.fromBits( FunctionEscapeAnalysisResult.fromBits(
callee.escapes ?: 0, callee.escapes ?: 0,
(0..callee.numberOfParameters).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 } (0..callee.parameterTypes.size).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 }
) )
} }