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
private val getContinuationSymbol = context.ir.symbols.getContinuation
private val continuationType = getContinuationSymbol.descriptor.returnType!!
private val arrayGetSymbol = context.ir.symbols.arrayGet
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 {
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 +
(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(
symbol = symbolTable.mapFunction(descriptor),
parameterTypes = parameterTypes,
returnType = symbolTable.mapType(returnType),
body = DataFlowIR.FunctionBody(allNodes.distinct().toList(), returnsNode, throwsNode)
)
}
@@ -606,7 +593,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapClass(owner),
callee.functionName.localHash.value,
arguments,
symbolTable.mapType(callee.returnType),
value
)
} else {
@@ -617,7 +603,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapClass(owner),
vtableIndex,
arguments,
symbolTable.mapType(callee.returnType),
value
)
}
@@ -626,7 +611,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(actualCallee),
arguments,
symbolTable.mapType(actualCallee.returnType),
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) },
value
)
@@ -642,7 +626,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(value.symbol.owner),
arguments.map { expressionToEdge(it) },
symbolTable.mapClass(context.ir.symbols.unit.owner),
symbolTable.mapType(thiz.type),
value
)
@@ -300,56 +300,59 @@ internal object DFGSerializer {
}
}
class ExternalFunctionSymbol(val hash: Long, val numberOfParameters: Int, val isGlobalInitializer: Boolean,
val escapes: Int?, val pointsTo: IntArray?, val name: String?) {
class FunctionSymbolBase(val parameterTypes: IntArray, val returnType: Int, val isGlobalInitializer: Boolean) {
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())
fun write(result: ArraySlice) {
result.writeLong(hash)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeNullableInt(escapes)
result.writeNullable(pointsTo) { writeIntArray(it) }
result.writeNullableString(name)
}
}
class PublicFunctionSymbol(val hash: Long, val numberOfParameters: Int, val isGlobalInitializer: Boolean,
val index: Int, val bridgeTarget: Int?, val name: String?) {
class PublicFunctionSymbol(val hash: Long, 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())
fun write(result: ArraySlice) {
result.writeLong(hash)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeInt(index)
result.writeNullableInt(bridgeTarget)
result.writeNullableString(name)
}
}
class PrivateFunctionSymbol(val index: Int, val numberOfParameters: Int, val isGlobalInitializer: Boolean,
val bridgeTarget: Int?, val name: String?) {
class PrivateFunctionSymbol(val index: Int, val bridgeTarget: Int?, val name: String?) {
constructor(data: ArraySlice) : this(data.readInt(), data.readInt(), data.readBoolean(),
data.readNullableInt(), data.readNullableString())
constructor(data: ArraySlice) : this(data.readInt(), data.readNullableInt(), data.readNullableString())
fun write(result: ArraySlice) {
result.writeInt(index)
result.writeInt(numberOfParameters)
result.writeBoolean(isGlobalInitializer)
result.writeNullableInt(bridgeTarget)
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) {
base.write(result)
result.writeByte(
when {
external != null -> 1
@@ -364,22 +367,23 @@ internal object DFGSerializer {
}
companion object {
fun external(hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean, escapes: Int?, pointsTo: IntArray?, name: String?) =
FunctionSymbol(ExternalFunctionSymbol(hash, numberOfParameters, isGlobalInitializer, escapes, pointsTo, name), null, null)
fun external(base: FunctionSymbolBase, hash: Long, escapes: Int?, pointsTo: IntArray?, name: String?) =
FunctionSymbol(base, ExternalFunctionSymbol(hash, escapes, pointsTo, name), null, null)
fun public(hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(null, PublicFunctionSymbol(hash, numberOfParameters, isGlobalInitializer, index, bridgeTarget, name), null)
fun public(base: FunctionSymbolBase, hash: Long, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(base, null, PublicFunctionSymbol(hash, index, bridgeTarget, name), null)
fun private(index: Int, numberOfParameters: Int, isGlobalInitializer: Boolean, bridgeTarget: Int?, name: String?) =
FunctionSymbol(null, null, PrivateFunctionSymbol(index, numberOfParameters, isGlobalInitializer, bridgeTarget, name))
fun private(base: FunctionSymbolBase, index: Int, bridgeTarget: Int?, name: String?) =
FunctionSymbol(base, null, null, PrivateFunctionSymbol(index, bridgeTarget, name))
fun read(data: ArraySlice): FunctionSymbol {
val base = FunctionSymbolBase(data)
val tag = data.readByte().toInt()
return when (tag) {
1 -> FunctionSymbol(ExternalFunctionSymbol(data), null, null)
2 -> FunctionSymbol(null, PublicFunctionSymbol(data), null)
3 -> FunctionSymbol(null, null, PrivateFunctionSymbol(data))
else -> FunctionSymbol(null, null, null)
1 -> FunctionSymbol(base, ExternalFunctionSymbol(data), null, null)
2 -> FunctionSymbol(base, null, PublicFunctionSymbol(data), null)
3 -> FunctionSymbol(base, null, null, PrivateFunctionSymbol(data))
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) {
result.writeInt(callee)
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) {
call.write(result)
result.writeInt(constructedType)
}
}
@@ -629,8 +633,8 @@ internal object DFGSerializer {
fun staticCall(call: Call, receiverType: Int?) =
Node().also { it.staticCall = StaticCall(call, receiverType) }
fun newObject(call: Call) =
Node().also { it.newObject = NewObject(call) }
fun newObject(call: Call, constructedType: Int) =
Node().also { it.newObject = NewObject(call, constructedType) }
fun vtableCall(virtualCall: VirtualCall, calleeVtableIndex: Int) =
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) {
result.writeInt(symbol)
result.writeIntArray(parameterTypes)
result.writeInt(returnType)
body.write(result)
}
}
@@ -790,25 +792,30 @@ internal object DFGSerializer {
val functionSymbols = functionSymbolMap.entries
.sortedBy { it.value }
.map {
val functionSymbol = it.key
val numberOfParameters = functionSymbol.numberOfParameters
val isGlobalInitializer = functionSymbol.isGlobalInitializer
val name = functionSymbol.name
val bridgeTarget = (functionSymbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] }
when (functionSymbol) {
fun buildFunctionSymbolBase(symbol: DataFlowIR.FunctionSymbol) =
FunctionSymbolBase(
symbol.parameterTypes.map { typeMap[it]!! }.toIntArray(),
typeMap[symbol.returnType]!!,
symbol.isGlobalInitializer
)
val symbol = it.key
val bridgeTarget = (symbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] }
when (symbol) {
is DataFlowIR.FunctionSymbol.External ->
FunctionSymbol.external(functionSymbol.hash, numberOfParameters, isGlobalInitializer,
functionSymbol.escapes, functionSymbol.pointsTo, name)
FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash,
symbol.escapes, symbol.pointsTo, symbol.name)
is DataFlowIR.FunctionSymbol.Public ->
FunctionSymbol.public(functionSymbol.hash, numberOfParameters,
isGlobalInitializer, functionSymbol.symbolTableIndex, bridgeTarget, name)
FunctionSymbol.public(buildFunctionSymbolBase(symbol), symbol.hash,
symbol.symbolTableIndex, bridgeTarget, symbol.name)
is DataFlowIR.FunctionSymbol.Private ->
FunctionSymbol.private(functionSymbol.symbolTableIndex, numberOfParameters,
isGlobalInitializer, bridgeTarget, name)
FunctionSymbol.private(buildFunctionSymbolBase(symbol), symbol.symbolTableIndex,
bridgeTarget, symbol.name)
else -> error("Unknown function symbol $functionSymbol")
else -> error("Unknown function symbol $symbol")
}
}
.toTypedArray()
@@ -827,8 +834,7 @@ internal object DFGSerializer {
fun buildCall(call: DataFlowIR.Node.Call) =
Call(
functionSymbolMap[call.callee]!!,
call.arguments.map { buildEdge(it) }.toTypedArray(),
typeMap[call.returnType]!!
call.arguments.map { buildEdge(it) }.toTypedArray()
)
fun buildVirtualCall(virtualCall: DataFlowIR.Node.VirtualCall) =
@@ -845,7 +851,8 @@ internal object DFGSerializer {
is DataFlowIR.Node.StaticCall ->
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 ->
Node.vtableCall(buildVirtualCall(node), node.calleeVtableIndex)
@@ -876,10 +883,8 @@ internal object DFGSerializer {
}
.toTypedArray()
Function(
functionSymbolMap[function.symbol]!!,
function.parameterTypes.map { typeMap[it]!! }.toIntArray(),
typeMap[function.returnType]!!,
FunctionBody(nodes, nodeMap[body.returns]!!, nodeMap[body.throws]!!)
symbol = functionSymbolMap[function.symbol]!!,
body = FunctionBody(nodes, nodeMap[body.returns]!!, nodeMap[body.throws]!!)
)
}
.toTypedArray()
@@ -953,20 +958,21 @@ internal object DFGSerializer {
}
val functionSymbols = symbolTable.functionSymbols.map {
val isGlobalInitializer = it.base.isGlobalInitializer
val external = it.external
val public = it.public
val private = it.private
when {
external != null ->
DataFlowIR.FunctionSymbol.External(external.hash, external.numberOfParameters,
external.isGlobalInitializer, external.escapes, external.pointsTo, external.name)
DataFlowIR.FunctionSymbol.External(external.hash,
isGlobalInitializer, external.escapes, external.pointsTo, external.name)
public != null -> {
val symbolTableIndex = public.index
if (symbolTableIndex >= 0)
++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Public(public.hash, public.numberOfParameters, module,
symbolTableIndex, public.isGlobalInitializer, null, public.name).also {
DataFlowIR.FunctionSymbol.Public(public.hash,
module, symbolTableIndex, isGlobalInitializer, null, public.name).also {
publicFunctionsMap.put(it.hash, it)
}
}
@@ -975,9 +981,12 @@ internal object DFGSerializer {
val symbolTableIndex = private!!.index
if (symbolTableIndex >= 0)
++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Private(privateFunIndex++, private.numberOfParameters, module,
symbolTableIndex, private.isGlobalInitializer, null, private.name)
DataFlowIR.FunctionSymbol.Private(privateFunIndex++,
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(
functionSymbols[call.callee],
call.arguments.map { deserializeEdge(it) },
types[call.returnType],
null
irCallSite = null
)
fun deserializeVirtualCall(virtualCall: VirtualCall): DataFlowIR.Node.VirtualCall {
@@ -1027,9 +1035,8 @@ internal object DFGSerializer {
return DataFlowIR.Node.VirtualCall(
call.callee,
call.arguments,
call.returnType,
types[virtualCall.receiverType],
null
irCallSite = null
)
}
@@ -1049,12 +1056,13 @@ internal object DFGSerializer {
val staticCall = it.staticCall!!
val call = deserializeCall(staticCall.call)
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 -> {
val call = deserializeCall(it.newObject!!.call)
DataFlowIR.Node.NewObject(call.callee, call.arguments, call.returnType, null)
val newObject = it.newObject!!
val call = deserializeCall(newObject.call)
DataFlowIR.Node.NewObject(call.callee, call.arguments, types[newObject.constructedType], irCallSite = null)
}
NodeType.VTABLE_CALL -> {
@@ -1065,8 +1073,7 @@ internal object DFGSerializer {
virtualCall.receiverType,
vtableCall.calleeVtableIndex,
virtualCall.arguments,
virtualCall.returnType,
virtualCall.callSite
virtualCall.irCallSite
)
}
@@ -1078,8 +1085,7 @@ internal object DFGSerializer {
virtualCall.receiverType,
itableCall.calleeHash,
virtualCall.arguments,
virtualCall.returnType,
virtualCall.callSite
virtualCall.irCallSite
)
}
@@ -1185,8 +1191,6 @@ internal object DFGSerializer {
val symbol = functionSymbols[it.symbol]
val function = DataFlowIR.Function(
symbol = symbol,
parameterTypes = it.parameterTypes.map { types[it] }.toTypedArray(),
returnType = types[it.returnType],
body = deserializeBody(it.body)
)
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.descriptors.isAbstract
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.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.isExported
@@ -117,10 +118,13 @@ internal object DataFlowIR {
var numberOfClasses = 0
}
abstract class FunctionSymbol(val numberOfParameters: Int, val isGlobalInitializer: Boolean, val name: String?) {
class External(val hash: Long, numberOfParameters: Int, isGlobalInitializer: Boolean,
abstract class FunctionSymbol(val isGlobalInitializer: Boolean, val name: String?) {
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)
: FunctionSymbol(numberOfParameters, isGlobalInitializer, name) {
: FunctionSymbol(isGlobalInitializer, name) {
override fun equals(other: Any?): Boolean {
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?)
: 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)
: Declared(numberOfParameters, module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
: Declared(module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
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)
: Declared(numberOfParameters, module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
: Declared(module, symbolTableIndex, isGlobalInitializer, bridgeTarget, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Private) return false
@@ -205,27 +211,27 @@ internal object DataFlowIR {
class Const(val type: Type) : Node()
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>, val returnType: Type,
open val callSite: IrFunctionAccessExpression?) : Node()
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>,
open val irCallSite: IrFunctionAccessExpression?) : Node()
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
val receiverType: Type?, callSite: IrFunctionAccessExpression?)
: Call(callee, arguments, returnType, callSite)
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type?, irCallSite: IrFunctionAccessExpression?)
: Call(callee, arguments, irCallSite)
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, type: Type, override val callSite: IrCall?)
: Call(constructor, arguments, type, callSite)
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, val constructedType: Type, override val irCallSite: IrCall?)
: Call(constructor, arguments, irCallSite)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
val receiverType: Type, override val callSite: IrCall?)
: Call(callee, arguments, returnType, callSite)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>,
val receiverType: Type, override val irCallSite: IrCall?)
: Call(callee, arguments, irCallSite)
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, irCallSite)
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long,
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
arguments: List<Edge>, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, irCallSite)
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 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()
@@ -244,14 +250,11 @@ internal object DataFlowIR {
class FunctionBody(val nodes: List<Node>, val returns: Node.Variable, val throws: Node.Variable)
class Function(val symbol: FunctionSymbol,
val parameterTypes: Array<Type>,
val returnType: Type,
val body: FunctionBody) {
class Function(val symbol: FunctionSymbol, val body: FunctionBody) {
fun debugOutput() {
println("FUNCTION $symbol")
println("Params: ${parameterTypes.contentToString()}")
println("Params: ${symbol.parameterTypes.contentToString()}")
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
body.nodes.forEach {
println(" NODE #${ids[it]!!}")
@@ -320,7 +323,7 @@ internal object DataFlowIR {
is Node.NewObject -> {
val result = StringBuilder()
result.appendln(" NEW OBJECT ${node.callee}")
result.appendln(" TYPE ${node.returnType}")
result.appendln(" CONSTRUCTED TYPE ${node.constructedType}")
node.arguments.forEach {
result.append(" ARG #${ids[it.node]!!}")
if (it.castToType == null)
@@ -439,6 +442,9 @@ internal object DataFlowIR {
NAME_POINTS_TO, NoLookupLocation.FROM_BACKEND) as org.jetbrains.kotlin.descriptors.ClassDescriptor
private val pointsToOnWhomDescriptor = pointsToAnnotationDescriptor.unsubstitutedPrimaryConstructor!!.valueParameters.single()
private val getContinuationSymbol = context.ir.symbols.getContinuation
private val continuationType = getContinuationSymbol.descriptor.returnType!!
var privateTypeIndex = 0
var privateFunIndex = 0
@@ -486,6 +492,10 @@ internal object DataFlowIR {
Type.Public(name.localHash.value, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name })
else
Type.Private(privateTypeIndex++, isFinal, isAbstract, correspondingValueType, module, symbolTableIndex, takeName { name })
classMap[descriptor] = type
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
if (!isAbstract) {
val vtableBuilder = context.getVtableBuilder(descriptor)
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)!!)
}
}
classMap.put(descriptor, type)
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
return type
}
@@ -512,50 +520,71 @@ internal object DataFlowIR {
private val FunctionDescriptor.internalName get() = getFqName(this).asString() + "#internal"
fun mapFunction(descriptor: DeclarationDescriptor): FunctionSymbol = descriptor.original.let {
functionMap.getOrPut(it) {
when (it) {
is IrField -> {
// 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" })
fun mapFunction(descriptor: DeclarationDescriptor): FunctionSymbol = when (descriptor) {
is FunctionDescriptor -> mapFunction(descriptor)
is IrField -> mapPropertyInitializer(descriptor)
else -> error("Unknown descriptor: $descriptor")
}
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
}
is FunctionDescriptor -> {
val name = if (it.isExported()) it.symbolName else it.internalName
val numberOfParameters = it.allParameters.size + if (it.isSuspend) 1 else 0
if (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, 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")
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, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
else
FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, false, bridgeTargetSymbol, takeName { name })
}
}
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() =
@@ -226,8 +226,7 @@ internal object Devirtualization {
}
}
private inner class InstantiationsSearcher(val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
val rootSet: List<DataFlowIR.FunctionSymbol>,
private inner class InstantiationsSearcher(val rootSet: List<DataFlowIR.FunctionSymbol>,
val typeHierarchy: TypeHierarchy) {
private val visited = mutableSetOf<DataFlowIR.FunctionSymbol>()
private val typesVirtualCallSites = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Node.VirtualCall>>()
@@ -236,13 +235,12 @@ internal object Devirtualization {
fun search(): Set<DataFlowIR.Type.Declared> {
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
// Add all final parameters of the roots.
rootSet.map { functions[it]!! }
.forEach {
it.parameterTypes
.map { it.resolved() }
.filter { it.isFinal }
.forEach { addInstantiatingClass(it) }
}
rootSet.forEach {
it.parameterTypes
.map { it.resolved() }
.filter { it.isFinal }
.forEach { addInstantiatingClass(it) }
}
if (entryPoint == null) {
// For library assume all public non-abstract classes could be instantiated.
moduleDFG.symbolTable.classMap.values
@@ -252,7 +250,7 @@ internal object Devirtualization {
.forEach { addInstantiatingClass(it) }
}
// Traverse call graph from the roots.
rootSet.forEach { dfs(it, functions[it]!!.returnType) }
rootSet.forEach { dfs(it) }
return instantiatingClasses
}
@@ -281,7 +279,7 @@ internal object Devirtualization {
else -> error("Unreachable")
}
dfs(callee, virtualCall.returnType)
dfs(callee)
}
private fun checkSupertypes(type: DataFlowIR.Type.Declared,
@@ -316,13 +314,13 @@ internal object Devirtualization {
.forEach { checkSupertypes(it, inheritor, seenTypes) }
}
private fun dfs(symbol: DataFlowIR.FunctionSymbol, returnType: DataFlowIR.Type) {
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
val resolvedFunctionSymbol = symbol.resolved()
if (resolvedFunctionSymbol is DataFlowIR.FunctionSymbol.External) {
DEBUG_OUTPUT(1) { println("Function $resolvedFunctionSymbol is external") }
val resolvedReturnType = returnType.resolved()
val resolvedReturnType = symbol.returnType.resolved()
if (resolvedReturnType.isFinal) {
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) {
when (node) {
is DataFlowIR.Node.NewObject -> {
addInstantiatingClass(node.returnType)
dfs(node.callee, node.returnType)
addInstantiatingClass(node.constructedType)
dfs(node.callee)
}
is DataFlowIR.Node.Singleton -> {
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.StaticCall ->
dfs(node.callee, node.returnType)
dfs(node.callee)
is DataFlowIR.Node.VirtualCall -> {
if (node.receiverType == DataFlowIR.Type.Virtual)
continue@nodeLoop
val receiverType = node.receiverType.resolved()
val vCallReturnType = node.returnType.resolved()
val vCallReturnType = node.callee.returnType.resolved()
DEBUG_OUTPUT(1) {
println("Adding virtual callsite:")
@@ -399,7 +397,7 @@ internal object Devirtualization {
val rootSet = computeRootSet(context, moduleDFG, externalModulesDFG)
val instantiatingClasses =
InstantiationsSearcher(functions, rootSet, typeHierarchy).search()
InstantiationsSearcher(rootSet, typeHierarchy).search()
.withIndex()
.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 }
@@ -531,7 +529,7 @@ internal object Devirtualization {
if (virtualCallSiteReceivers == null || virtualCallSiteReceivers.receiver.types[VIRTUAL_TYPE_ID]) {
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")
}
@@ -542,16 +540,16 @@ internal object Devirtualization {
.map { it.value }
.filter { it != nothing }
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) {
"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 callee = devirtualizedCallee.callee
if (callee is DataFlowIR.FunctionSymbol.Declared && callee.symbolTableIndex < 0)
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
}) to virtualCallSiteReceivers.caller
}
@@ -559,10 +557,10 @@ internal object Devirtualization {
DEBUG_OUTPUT(0) {
println("Devirtualized from current module:")
result.forEach { virtualCall, devirtualizedCallSite ->
if (virtualCall.callSite != null) {
if (virtualCall.irCallSite != null) {
println("DEVIRTUALIZED")
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:")
devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") }
devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") }
@@ -571,10 +569,10 @@ internal object Devirtualization {
}
println("Devirtualized from external modules:")
result.forEach { virtualCall, devirtualizedCallSite ->
if (virtualCall.callSite == null) {
if (virtualCall.irCallSite == null) {
println("DEVIRTUALIZED")
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:")
devirtualizedCallSite.first.possibleCallees.forEach { println(" TYPE: ${it.receiverType}") }
devirtualizedCallSite.first.possibleCallees.forEach { println(" FUN: ${it.callee}") }
@@ -647,11 +645,10 @@ internal object Devirtualization {
if (symbol is DataFlowIR.FunctionSymbol.External) return null
constraintGraph.functions[symbol]?.let { return it }
val function = functions[symbol] ?: error("Unknown function: $symbol")
val parameters = Array(symbol.numberOfParameters) { ordinaryNode { "Param#$it\$$symbol" } }
val parameters = Array(symbol.parameterTypes.size) { ordinaryNode { "Param#$it\$$symbol" } }
if (isRoot) {
// Exported function from the current module.
function.parameterTypes.forEachIndexed { index, type ->
symbol.parameterTypes.forEachIndexed { index, type ->
val resolvedType = type.resolved()
val node = if (!resolvedType.isFinal)
constraintGraph.virtualNode
@@ -769,10 +766,10 @@ internal object Devirtualization {
function.parameters[node.index]
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 -> {
val returnType = node.returnType.resolved()
val returnType = node.constructedType.resolved()
val instanceNode = concreteClass(returnType)
doCall(node.callee, listOf(instanceNode) + node.arguments, returnType, null)
instanceNode
@@ -812,7 +809,7 @@ internal object Devirtualization {
println()
}
val returnType = node.returnType.resolved()
val returnType = node.callee.returnType.resolved()
val receiverNode = edgeToConstraintNode(node.arguments[0])
if (receiverType == DataFlowIR.Type.Virtual)
constraintGraph.virtualNode.addEdge(receiverNode)
@@ -891,7 +888,7 @@ internal object Devirtualization {
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>)
@@ -901,8 +898,8 @@ internal object Devirtualization {
val devirtualizedCallSites =
devirtualizationAnalysisResult
.asSequence()
.filter { it.key.callSite != null }
.associate { it.key.callSite!! to it.value }
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
Devirtualization.devirtualize(irModule, context, devirtualizedCallSites)
return AnalysisResult(devirtualizationAnalysisResult)
}
@@ -266,7 +266,7 @@ internal object EscapeAnalysis {
}
for (functionSymbol in callGraph.directEdges.keys) {
val numberOfParameters = intraproceduralAnalysisResult[functionSymbol]!!.function.parameterTypes.size
val numberOfParameters = functionSymbol.parameterTypes.size
escapeAnalysisResults[functionSymbol] = FunctionEscapeAnalysisResult(
// Assume no edges at the beginning.
// Then iteratively add needed.
@@ -321,7 +321,7 @@ internal object EscapeAnalysis {
for (graph in pointsToGraphs.values) {
graph.nodes.keys
.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 {
val numberOfParameters = symbol.numberOfParameters
val numberOfParameters = symbol.parameterTypes.size
return FunctionEscapeAnalysisResult((0..numberOfParameters).map {
ParameterEscapeAnalysisResult(
escapes = true,
@@ -378,7 +378,7 @@ internal object EscapeAnalysis {
FunctionEscapeAnalysisResult.fromBits(
callee.escapes ?: 0,
(0..callee.numberOfParameters).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 }
(0..callee.parameterTypes.size).map { callee.pointsTo?.elementAtOrNull(it) ?: 0 }
)
}