Devirtualization: minor refactoring

This commit is contained in:
Igor Chevdar
2018-10-11 17:54:16 +03:00
committed by Igor Chevdar
parent 9ed0c2f1d8
commit cb811335dc
@@ -155,7 +155,8 @@ internal object Devirtualization {
class Function(val symbol: DataFlowIR.FunctionSymbol, val parameters: Array<Node>, val returns: Node, val throws: Node)
class VirtualCallSiteReceivers(val receiver: Node, val caller: DataFlowIR.FunctionSymbol, val devirtualizedCallees: List<DevirtualizedCallee>)
class VirtualCallSiteReceivers(val receiver: Node, val caller: DataFlowIR.FunctionSymbol,
val devirtualizedCallees: List<DevirtualizedCallee>)
inner class ConstraintGraph : DirectedGraph<Node, Node> {
@@ -166,7 +167,8 @@ internal object Devirtualization {
val voidNode = addNode { Node.Ordinary(it, { "Void" }) }
val virtualNode = addNode { Node.Source(it, VIRTUAL_TYPE_ID, { "Virtual" }) }
val arrayItemField = DataFlowIR.Field(null, symbolTable.mapClassReferenceType(context.irBuiltIns.anyClass.owner), 1, "Array\$Item")
val arrayItemField = DataFlowIR.Field(null,
symbolTable.mapClassReferenceType(context.irBuiltIns.anyClass.owner), 1, "Array\$Item")
val functions = mutableMapOf<DataFlowIR.FunctionSymbol, Function>()
val concreteClasses = mutableMapOf<DataFlowIR.Type.Declared, Node>()
val externalFunctions = mutableMapOf<DataFlowIR.FunctionSymbol, Node>()
@@ -204,7 +206,7 @@ internal object Devirtualization {
type.superTypes
.map { it.resolved() }
.forEach { superType ->
val subTypes = typesSubTypes.getOrPut(superType, { mutableListOf() })
val subTypes = typesSubTypes.getOrPut(superType) { mutableListOf() }
subTypes += type
processType(superType)
}
@@ -335,7 +337,9 @@ internal object Devirtualization {
DEBUG_OUTPUT(1) { println("Visiting $resolvedFunctionSymbol") }
val function = (moduleDFG.functions[resolvedFunctionSymbol] ?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol]) ?: error("Unknown function $resolvedFunctionSymbol")
val function = (moduleDFG.functions[resolvedFunctionSymbol]
?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol])
?: error("Unknown function $resolvedFunctionSymbol")
DEBUG_OUTPUT(1) { function.debugOutput() }
@@ -392,7 +396,7 @@ internal object Devirtualization {
addInstantiatingClass(vCallReturnType)
}
typesVirtualCallSites.getOrPut(receiverType, { mutableListOf() }).add(node)
typesVirtualCallSites.getOrPut(receiverType) { mutableListOf() }.add(node)
typeHierarchy.inheritorsOf(receiverType)
.filter { instantiatingClasses.contains(it) }
.forEach { processVirtualCall(node, it) }
@@ -459,12 +463,14 @@ internal object Devirtualization {
val instantiatingClasses =
InstantiationsSearcher(rootSet, typeHierarchy).search()
.asSequence()
.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 }
val nodesMap = mutableMapOf<DataFlowIR.Node, Node>()
val constraintGraphBuilder = ConstraintGraphBuilder(nodesMap, functions, typeHierarchy, instantiatingClasses, allTypes, rootSet)
val constraintGraphBuilder =
ConstraintGraphBuilder(nodesMap, functions, typeHierarchy, instantiatingClasses, allTypes, rootSet)
constraintGraphBuilder.build()
DEBUG_OUTPUT(0) {
@@ -486,8 +492,8 @@ internal object Devirtualization {
constraintGraph.nodes.forEach {
if (it is Node.Source) {
assert(it.reversedEdges?.isEmpty() ?: true, { "A source node #${it.id} has incoming edges" })
assert(it.reversedCastEdges?.isEmpty() ?: true, { "A source node #${it.id} has incoming edges" })
assert(it.reversedEdges?.isEmpty() ?: true) { "A source node #${it.id} has incoming edges" }
assert(it.reversedCastEdges?.isEmpty() ?: true) { "A source node #${it.id} has incoming edges" }
}
}
@@ -570,8 +576,9 @@ internal object Devirtualization {
println("Types of multi-node #$index")
for (node in multiNode.nodes) {
println(" Node #${node.id}")
allTypes.withIndex()
.filter { node.types[it.index] }
allTypes.asSequence()
.withIndex()
.filter { node.types[it.index] }.toList()
.forEach { println(" ${it.value}") }
}
}
@@ -585,13 +592,14 @@ internal object Devirtualization {
.flatMap { it.body.nodes.asSequence() }
.filterIsInstance<DataFlowIR.Node.VirtualCall>()
.forEach { virtualCall ->
assert (nodesMap[virtualCall] != null, { "Node for virtual call $virtualCall has not been built" })
assert (nodesMap[virtualCall] != null) { "Node for virtual call $virtualCall has not been built" }
val virtualCallSiteReceivers = constraintGraph.virtualCallSiteReceivers[virtualCall]
?: error("virtualCallSiteReceivers were not built for virtual call $virtualCall")
if (virtualCallSiteReceivers.receiver.types[VIRTUAL_TYPE_ID]) {
DEBUG_OUTPUT(0) {
println("Unable to devirtualize callsite ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.callee.toString() }")
println("Unable to devirtualize callsite " +
(virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.callee.toString()))
println(" receiver is Virtual")
printPathToType(virtualCallSiteReceivers.receiver, VIRTUAL_TYPE_ID)
}
@@ -599,9 +607,11 @@ internal object Devirtualization {
return@forEach
}
DEBUG_OUTPUT(0) {
println("Devirtualized callsite ${virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.callee.toString() }")
println("Devirtualized callsite " +
(virtualCall.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.callee.toString()))
}
val possibleReceivers = allTypes.withIndex()
val possibleReceivers = allTypes.asSequence()
.withIndex()
.filter { virtualCallSiteReceivers.receiver.types[it.index] }
.filterNot { it.value == nothing }
.map {
@@ -610,21 +620,25 @@ internal object Devirtualization {
printPathToType(virtualCallSiteReceivers.receiver, it.index)
}
it.value
}
}.toList()
val map = virtualCallSiteReceivers.devirtualizedCallees.associateBy({ it.receiverType }, { it })
result[virtualCall] = DevirtualizedCallSite(virtualCall.callee.resolved(), possibleReceivers.map { receiverType ->
assert (map[receiverType] != null) {
"Non-expected receiver type $receiverType at call site: " +
(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.irCallSite?.let { ir2stringWhole(it) } ?: virtualCall.toString() }")
devirtualizedCallee
}) to virtualCallSiteReceivers.caller
result[virtualCall] = DevirtualizedCallSite(virtualCall.callee.resolved(),
possibleReceivers.map { receiverType ->
assert(map[receiverType] != null) {
"Non-expected receiver type $receiverType at call site: " +
(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.irCallSite?.let { ir2stringWhole(it) }
?: virtualCall.toString()))
devirtualizedCallee
}) to virtualCallSiteReceivers.caller
}
DEBUG_OUTPUT(0) {
@@ -668,7 +682,11 @@ internal object Devirtualization {
private fun concreteType(type: DataFlowIR.Type.Declared): Int {
assert(!(type.isAbstract && type.isFinal)) { "Incorrect type: $type" }
return if (type.isAbstract) VIRTUAL_TYPE_ID else { instantiatingClasses[type] ?: error("Type $type is not instantiated") }
return if (type.isAbstract)
VIRTUAL_TYPE_ID
else {
instantiatingClasses[type] ?: error("Type $type is not instantiated")
}
}
private fun ordinaryNode(nameBuilder: () -> String) =
@@ -718,7 +736,7 @@ internal object Devirtualization {
DEBUG_OUTPUT(0) {
println("CONSTRAINT GRAPH FOR $symbol")
val ids = function.body.nodes.withIndex().associateBy({ it.value }, { it.index })
val ids = function.body.nodes.asSequence().withIndex().associateBy({ it.value }, { it.index })
for (node in function.body.nodes) {
println("FT NODE #${ids[node]}")
DataFlowIR.Function.printNode(node, ids)
@@ -796,7 +814,8 @@ internal object Devirtualization {
fun doCall(callee: Function, arguments: List<Any>): Node {
assert(callee.parameters.size == arguments.size) {
"Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol} provided ${arguments.size}"
"Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol}" +
" provided ${arguments.size}"
}
callee.parameters.forEachIndexed { index, parameter ->
val argument = argumentToConstraintNode(arguments[index])
@@ -857,7 +876,8 @@ internal object Devirtualization {
function.parameters[node.index]
is DataFlowIR.Node.StaticCall ->
doCall(node.callee, node.arguments, node.callee.returnParameter.type.resolved(), node.receiverType?.resolved())
doCall(node.callee, node.arguments, node.callee.returnParameter.type.resolved(),
node.receiverType?.resolved())
is DataFlowIR.Node.NewObject -> {
val returnType = node.constructedType.resolved()
@@ -931,7 +951,8 @@ internal object Devirtualization {
val devirtualizedCallees = possibleReceiverTypes.mapIndexed { index, possibleReceiverType ->
DevirtualizedCallee(possibleReceiverType, callees[index])
}
constraintGraph.virtualCallSiteReceivers[node] = VirtualCallSiteReceivers(castedReceiver, function.symbol, devirtualizedCallees)
constraintGraph.virtualCallSiteReceivers[node] =
VirtualCallSiteReceivers(castedReceiver, function.symbol, devirtualizedCallees)
returnsNode
}
@@ -1009,15 +1030,18 @@ internal object Devirtualization {
}
}
private val specialNames = listOf("<box>", "<unbox>")
// TODO: do it more reliably.
private fun IrExpression.isBoxOrUnboxCall() =
(this is IrCall && symbol.owner.name.asString().let { it == "<box>" || it == "<unbox>" })
|| (this is IrPrivateFunctionCall && dfgSymbol.name?.let { it.contains("<box>") || it.contains("<unbox>") } == true)
(this is IrCall && symbol.owner.name.asString().let { specialNames.contains(it) })
|| (this is IrPrivateFunctionCall && specialNames.any { dfgSymbol.name?.contains(it) == true })
private fun devirtualize(irModule: IrModuleFragment, context: Context,
moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val symbols = context.ir.symbols
val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val optimize = context.shouldOptimize()
fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared {
@@ -1064,7 +1088,8 @@ internal object Devirtualization {
fun <T : IrElement> IrStatementsBuilder<T>.irTemporary(value: IrExpression, tempName: String, type: IrType): IrVariable {
val originalKotlinType = type.originalKotlinType ?: type.toKotlinType()
val descriptor = IrTemporaryVariableDescriptorImpl(scope.scopeOwner, Name.identifier(tempName), originalKotlinType, false)
val descriptor = IrTemporaryVariableDescriptorImpl(
scope.scopeOwner, Name.identifier(tempName), originalKotlinType, false)
val temporary = IrVariableImpl(
value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
@@ -1083,13 +1108,13 @@ internal object Devirtualization {
else {
val coercion = expression as IrCall
PossiblyCoercedValue(
irTemporary(coercion.getValueArgument(0)!!, tempName, coercion.symbol.owner.explicitParameters.single().type)
irTemporary(coercion.getValueArgument(0)!!, tempName,
coercion.symbol.owner.explicitParameters.single().type)
, coercion.symbol)
}
class CoercionPair(val coerceFunction: DataFlowIR.FunctionSymbol.Declared, val uncoerceFunction: DataFlowIR.FunctionSymbol.Declared)
fun getTypeConversion(actualType: DataFlowIR.FunctionParameter, targetType: DataFlowIR.FunctionParameter): CoercionPair? {
fun getTypeConversion(actualType: DataFlowIR.FunctionParameter,
targetType: DataFlowIR.FunctionParameter): DataFlowIR.FunctionSymbol.Declared? {
if (actualType.boxFunction == null && targetType.boxFunction == null) return null
if (actualType.boxFunction != null && targetType.boxFunction != null) {
assert (actualType.type.resolved() == targetType.type.resolved())
@@ -1097,10 +1122,8 @@ internal object Devirtualization {
return null
}
if (actualType.boxFunction == null)
return CoercionPair(targetType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared,
targetType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared)
return CoercionPair(actualType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared,
actualType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared)
return targetType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared
return actualType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared
}
fun irDevirtualizedCall(callee: IrCall, actualType: IrType, devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
@@ -1132,11 +1155,11 @@ internal object Devirtualization {
parameters.forEachIndexed { index, value ->
val coercion = getTypeConversion(actualCallee.parameters[index], bridgeTarget.parameters[index])
val fullValue = value.getFullValue(this@irDevirtualizedCall)
putValueArgument(index, coercion?.let { irCoerce(fullValue, coercion.coerceFunction) } ?: fullValue)
putValueArgument(index, coercion?.let { irCoerce(fullValue, coercion) } ?: fullValue)
}
}
val returnCoercion = getTypeConversion(bridgeTarget.returnParameter, actualCallee.returnParameter)
irCoerce(callResult, returnCoercion?.coerceFunction)
irCoerce(callResult, returnCoercion)
}
}
@@ -1170,15 +1193,17 @@ internal object Devirtualization {
val dispatchReceiver = expression.dispatchReceiver!!
return when {
possibleCallees.isEmpty() -> irBlock(expression) {
val throwExpr = irCall(context.ir.symbols.throwInvalidReceiverTypeException.owner).apply {
putValueArgument(0, irCall(context.ir.symbols.kClassImplConstructor.owner, listOf(dispatchReceiver.type)).apply {
putValueArgument(0, irCall(context.ir.symbols.getObjectTypeInfo.owner).apply {
putValueArgument(0, dispatchReceiver)
})
})
val throwExpr = irCall(symbols.throwInvalidReceiverTypeException.owner).apply {
putValueArgument(0,
irCall(symbols.kClassImplConstructor.owner, listOf(dispatchReceiver.type)).apply {
putValueArgument(0,
irCall(symbols.getObjectTypeInfo.owner).apply {
putValueArgument(0, dispatchReceiver)
})
})
}
// Insert proper unboxing (unreachable code):
+irCoerce(throwExpr, context.ir.symbols.getTypeConversion(throwExpr.type, type))
+irCoerce(throwExpr, symbols.getTypeConversion(throwExpr.type, type))
}
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
@@ -1195,7 +1220,7 @@ internal object Devirtualization {
val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
}
val typeInfo = irTemporary(irCall(context.ir.symbols.getObjectTypeInfo).apply {
val typeInfo = irTemporary(irCall(symbols.getObjectTypeInfo).apply {
putValueArgument(0, parameters[0].getFullValue(this@irBlock))
})
@@ -1206,7 +1231,7 @@ internal object Devirtualization {
val expectedTypeInfo = IrPrivateClassReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.ir.symbols.nativePtrType,
type = symbols.nativePtrType,
symbol = dispatchReceiver.type.getErasedTypeClass(),
classType = dispatchReceiver.type,
moduleDescriptor = actualReceiverType.module!!.descriptor,
@@ -1233,9 +1258,9 @@ internal object Devirtualization {
startOffset = startOffset,
endOffset = endOffset,
condition = irTrue(),
result = irCall(context.ir.symbols.throwInvalidReceiverTypeException).apply {
result = irCall(symbols.throwInvalidReceiverTypeException).apply {
putValueArgument(0,
irCall(context.ir.symbols.kClassImplConstructor,
irCall(symbols.kClassImplConstructor,
listOf(dispatchReceiver.type)
).apply {
putValueArgument(0, irGet(typeInfo))
@@ -1278,7 +1303,8 @@ internal object Devirtualization {
if (cast == null)
expression
else with (cast) {
IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, typeOperand, typeOperandClassifier, expression)
IrTypeOperatorCallImpl(startOffset, endOffset, type, operator,
typeOperand, typeOperandClassifier, expression)
}
with (coercion) {
return IrCallImpl(startOffset, endOffset, type, symbol, descriptor, typeArgumentsCount, origin).apply {
@@ -1335,9 +1361,11 @@ internal object Devirtualization {
else expression
} ?: (expression as IrPrivateFunctionCall).let {
val argarg = it.getValueArgument(0)!!
val boxFunction = moduleDFG.symbolTable.mapFunction(context.getBoxFunction(coercionDeclaringClass)).resolved()
val unboxFunction = moduleDFG.symbolTable.mapFunction(context.getUnboxFunction(coercionDeclaringClass)).resolved()
if (it.dfgSymbol == boxFunction || it.dfgSymbol == unboxFunction)
val boxFunction = context.getBoxFunction(coercionDeclaringClass)
val unboxFunction = context.getUnboxFunction(coercionDeclaringClass)
val boxFunctionSymbol = moduleDFG.symbolTable.mapFunction(boxFunction).resolved()
val unboxFunctionSymbol = moduleDFG.symbolTable.mapFunction(unboxFunction).resolved()
if (it.dfgSymbol == boxFunctionSymbol || it.dfgSymbol == unboxFunctionSymbol)
argarg
else it
}
@@ -1345,7 +1373,9 @@ internal object Devirtualization {
}
return when (expression) {
is IrReturnableBlock -> {
val foldedReturnableBlockValues = returnableBlockValues[expression]!!.associate { it to fold(it, coercion, cast, false) }
val foldedReturnableBlockValues = returnableBlockValues[expression]!!.associate {
it to fold(it, coercion, cast, false)
}
val someoneFolded = foldedReturnableBlockValues.any { it.value.folded }
val transformedReturnableBlock =
if (!someoneFolded)