Supported exceptions in devirtualization

This commit is contained in:
Igor Chevdar
2018-02-09 12:42:33 +03:00
parent 86e5d6c845
commit 9c62d5faa3
4 changed files with 66 additions and 28 deletions
@@ -33,10 +33,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.createValueSymbol
@@ -287,7 +284,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
}
val function = FunctionDFGBuilder(expressionValuesExtractor, visitor.variableValues,
descriptor, visitor.expressions, visitor.returnValues, visitor.thrownValues).build()
descriptor, visitor.expressions, visitor.returnValues, visitor.thrownValues, visitor.catchParameters).build()
DEBUG_OUTPUT(0) {
function.debugOutput()
@@ -322,6 +319,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val variableValues = VariableValues()
val returnValues = mutableListOf<IrExpression>()
val thrownValues = mutableListOf<IrExpression>()
val catchParameters = mutableSetOf<VariableDescriptor>()
private val returnableBlocks = mutableMapOf<FunctionDescriptor, IrReturnableBlock>()
private val suspendableExpressionStack = mutableListOf<IrSuspendableExpression>()
@@ -398,6 +396,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
super.visitThrow(expression)
}
override fun visitCatch(aCatch: IrCatch) {
catchParameters.add(aCatch.parameter)
super.visitCatch(aCatch)
}
override fun visitSetVariable(expression: IrSetVariable) {
super.visitSetVariable(expression)
assignVariable(expression.descriptor, expression.value)
@@ -429,7 +432,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val descriptor: CallableDescriptor,
val expressions: List<IrExpression>,
val returnValues: List<IrExpression>,
val thrownValues: List<IrExpression>) {
val thrownValues: List<IrExpression>,
val catchParameters: Set<VariableDescriptor>) {
private val allParameters = (descriptor as? FunctionDescriptor)?.allParameters ?: emptyList()
private val templateParameters = allParameters.withIndex().associateBy({ it.value }, { DataFlowIR.Node.Parameter(it.index) })
@@ -446,10 +450,15 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
private fun getContinuation() = continuationParameter ?: error("Function $descriptor has no continuation parameter")
private val nodes = mutableMapOf<IrExpression, DataFlowIR.Node>()
private val variables = variableValues.elementData.keys.associateBy(
{ it },
{ DataFlowIR.Node.Variable(mutableListOf(), false) }
)
private val variables = variableValues.elementData.keys.associate {
it to DataFlowIR.Node.Variable(
values = mutableListOf(),
type = symbolTable.mapType(it.type),
kind = if (catchParameters.contains(it))
DataFlowIR.VariableKind.CatchParameter
else DataFlowIR.VariableKind.Ordinary
)
}
private fun choosePrimary(erasure: List<ClassDescriptor>): ClassDescriptor {
if (erasure.size == 1) return erasure[0]
@@ -459,8 +468,16 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
fun build(): DataFlowIR.Function {
expressions.forEach { getNode(it) }
val returnsNode = DataFlowIR.Node.Variable(returnValues.map { expressionToEdge(it) }, true)
val throwsNode = DataFlowIR.Node.Variable(thrownValues.map { expressionToEdge(it) }, true)
val returnsNode = DataFlowIR.Node.Variable(
values = returnValues.map { expressionToEdge(it) },
type = symbolTable.mapType(descriptor.returnType ?: context.builtIns.unitType),
kind = DataFlowIR.VariableKind.Temporary
)
val throwsNode = DataFlowIR.Node.Variable(
values = thrownValues.map { expressionToEdge(it) },
type = symbolTable.mapClass(context.builtIns.throwable),
kind = DataFlowIR.VariableKind.Temporary
)
variables.forEach { descriptor, node ->
variableValues.elementData[descriptor]!!.forEach {
node.values += expressionToEdge(it)
@@ -499,7 +516,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val values = mutableListOf<IrExpression>()
expressionValuesExtractor.forEachValue(expression) { values += it }
if (values.size != 1) {
DataFlowIR.Node.Variable(values.map { expressionToEdge(it) }, true)
DataFlowIR.Node.Variable(
values = values.map { expressionToEdge(it) },
type = symbolTable.mapType(expression.type),
kind = DataFlowIR.VariableKind.Temporary
)
} else {
val value = values[0]
if (value != expression) {
@@ -507,7 +528,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
if (edge.castToType == null)
edge.node
else
DataFlowIR.Node.Variable(listOf(edge), true)
DataFlowIR.Node.Variable(
values = listOf(edge),
type = symbolTable.mapType(expression.type),
kind = DataFlowIR.VariableKind.Temporary
)
} else {
when (value) {
is IrGetValue -> getNode(value)
@@ -520,13 +520,14 @@ internal object DFGSerializer {
}
}
class Variable(val values: Array<Edge>, val temp: Boolean) {
class Variable(val values: Array<Edge>, val type: Int, val kind: Byte) {
constructor(data: ArraySlice) : this(data.readArray { Edge(this) }, data.readBoolean())
constructor(data: ArraySlice) : this(data.readArray { Edge(this) }, data.readInt(), data.readByte())
fun write(result: ArraySlice) {
result.writeArray(values) { it.write(this) }
result.writeBoolean(temp)
result.writeInt(type)
result.writeByte(kind)
}
}
@@ -626,8 +627,8 @@ internal object DFGSerializer {
fun arrayWrite(array: Edge, index: Edge, value: Edge) =
Node().also { it.arrayWrite = ArrayWrite(array, index, value) }
fun variable(values: Array<Edge>, temp: Boolean) =
Node().also { it.variable = Variable(values, temp) }
fun variable(values: Array<Edge>, type: Int, kind: DataFlowIR.VariableKind) =
Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) }
fun read(data: ArraySlice): Node {
val type = enumValues<NodeType>()[data.readByte().toInt()]
@@ -808,7 +809,7 @@ internal object DFGSerializer {
Node.arrayWrite(buildEdge(node.array), buildEdge(node.index), buildEdge(node.value))
is DataFlowIR.Node.Variable ->
Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), node.temp)
Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), typeMap[node.type]!!, node.kind)
else -> error("Unknown node $node")
}
@@ -1030,7 +1031,8 @@ internal object DFGSerializer {
NodeType.VARIABLE -> {
val variable = it.variable!!
DataFlowIR.Node.Variable(variable.values.map { deserializeEdge(it) }, variable.temp)
DataFlowIR.Node.Variable(variable.values.map { deserializeEdge(it) },
types[variable.type], enumValues<DataFlowIR.VariableKind>()[variable.kind.toInt()])
}
else -> error("Unknown node: $it")
@@ -189,6 +189,12 @@ internal object DataFlowIR {
}
}
enum class VariableKind {
Ordinary,
Temporary,
CatchParameter
}
sealed class Node {
class Parameter(val index: Int) : Node()
@@ -226,7 +232,7 @@ internal object DataFlowIR {
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge) : Node()
class Variable(values: List<Edge>, val temp: Boolean) : Node() {
class Variable(values: List<Edge>, val type: Type, val kind: VariableKind) : Node() {
val values = mutableListOf<Edge>().also { it += values }
}
}
@@ -385,7 +391,7 @@ internal object DataFlowIR {
is Node.Variable -> {
val result = StringBuilder()
result.appendln(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ")
result.appendln(" ${node.kind}")
node.values.forEach {
result.append(" VAL #${ids[it.node]!!}")
if (it.castToType == null)
@@ -32,8 +32,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import java.util.*
// TODO: Exceptions.
// Devirtualization analysis is performed using Variable Type Analysis algorithm.
// See http://web.cs.ucla.edu/~palsberg/tba/papers/sundaresan-et-al-oopsla00.pdf for details.
internal object Devirtualization {
@@ -139,7 +137,7 @@ internal object Devirtualization {
class CastEdge(val node: Node, val suitableTypes: BitSet)
}
class Function(val symbol: DataFlowIR.FunctionSymbol, val parameters: Array<Node>, val returns: Node)
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>)
@@ -594,6 +592,7 @@ internal object Devirtualization {
body.nodes.forEach { dfgNodeToConstraintNode(functionConstraintGraph, it) }
functionNodesMap[body.returns]!!.addEdge(functionConstraintGraph.returns)
functionNodesMap[body.throws]!!.addEdge(functionConstraintGraph.throws)
DEBUG_OUTPUT(0) {
@@ -631,7 +630,8 @@ internal object Devirtualization {
}
val returnsNode = ordinaryNode { "Returns\$$symbol" }
val functionConstraintGraph = Function(symbol, parameters, returnsNode)
val throwsNode = ordinaryNode { "Throws\$$symbol" }
val functionConstraintGraph = Function(symbol, parameters, returnsNode, throwsNode)
constraintGraph.functions[symbol] = functionConstraintGraph
stack.push(symbol)
@@ -701,6 +701,7 @@ internal object Devirtualization {
fictitiousReturnNode
}
} else {
calleeConstraintGraph.throws.addEdge(function.throws)
if (receiverType == null)
doCall(calleeConstraintGraph, arguments)
else {
@@ -713,7 +714,7 @@ internal object Devirtualization {
}
}
if (node is DataFlowIR.Node.Variable && !node.temp) {
if (node is DataFlowIR.Node.Variable && node.kind != DataFlowIR.VariableKind.Temporary) {
var variableNode = variables[node]
if (variableNode == null) {
variableNode = ordinaryNode { "Variable\$${function.symbol}" }
@@ -721,6 +722,8 @@ internal object Devirtualization {
for (value in node.values) {
edgeToConstraintNode(value).addEdge(variableNode)
}
if (node.kind == DataFlowIR.VariableKind.CatchParameter)
function.throws.addCastEdge(createCastEdge(variableNode, node.type.resolved()))
}
return variableNode
}
@@ -794,6 +797,8 @@ internal object Devirtualization {
if (!returnType.isFinal) {
receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter))
}
// And throw anything.
receiverNode.addCastEdge(Node.CastEdge(function.throws, virtualTypeFilter))
// And write to some array anything. TODO: This is conservative.
receiverNode.addCastEdge(Node.CastEdge(fieldNode(constraintGraph.arrayItemField), virtualTypeFilter))