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.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.*
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.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.createValueSymbol 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, 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) { DEBUG_OUTPUT(0) {
function.debugOutput() function.debugOutput()
@@ -322,6 +319,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val variableValues = VariableValues() val variableValues = VariableValues()
val returnValues = mutableListOf<IrExpression>() val returnValues = mutableListOf<IrExpression>()
val thrownValues = mutableListOf<IrExpression>() val thrownValues = mutableListOf<IrExpression>()
val catchParameters = mutableSetOf<VariableDescriptor>()
private val returnableBlocks = mutableMapOf<FunctionDescriptor, IrReturnableBlock>() private val returnableBlocks = mutableMapOf<FunctionDescriptor, IrReturnableBlock>()
private val suspendableExpressionStack = mutableListOf<IrSuspendableExpression>() private val suspendableExpressionStack = mutableListOf<IrSuspendableExpression>()
@@ -398,6 +396,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
super.visitThrow(expression) super.visitThrow(expression)
} }
override fun visitCatch(aCatch: IrCatch) {
catchParameters.add(aCatch.parameter)
super.visitCatch(aCatch)
}
override fun visitSetVariable(expression: IrSetVariable) { override fun visitSetVariable(expression: IrSetVariable) {
super.visitSetVariable(expression) super.visitSetVariable(expression)
assignVariable(expression.descriptor, expression.value) assignVariable(expression.descriptor, expression.value)
@@ -429,7 +432,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val descriptor: CallableDescriptor, val descriptor: CallableDescriptor,
val expressions: List<IrExpression>, val expressions: List<IrExpression>,
val returnValues: 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 allParameters = (descriptor as? FunctionDescriptor)?.allParameters ?: emptyList()
private val templateParameters = allParameters.withIndex().associateBy({ it.value }, { DataFlowIR.Node.Parameter(it.index) }) 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 fun getContinuation() = continuationParameter ?: error("Function $descriptor has no continuation parameter")
private val nodes = mutableMapOf<IrExpression, DataFlowIR.Node>() private val nodes = mutableMapOf<IrExpression, DataFlowIR.Node>()
private val variables = variableValues.elementData.keys.associateBy( private val variables = variableValues.elementData.keys.associate {
{ it }, it to DataFlowIR.Node.Variable(
{ DataFlowIR.Node.Variable(mutableListOf(), false) } 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 { private fun choosePrimary(erasure: List<ClassDescriptor>): ClassDescriptor {
if (erasure.size == 1) return erasure[0] if (erasure.size == 1) return erasure[0]
@@ -459,8 +468,16 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
fun build(): DataFlowIR.Function { fun build(): DataFlowIR.Function {
expressions.forEach { getNode(it) } expressions.forEach { getNode(it) }
val returnsNode = DataFlowIR.Node.Variable(returnValues.map { expressionToEdge(it) }, true) val returnsNode = DataFlowIR.Node.Variable(
val throwsNode = DataFlowIR.Node.Variable(thrownValues.map { expressionToEdge(it) }, true) 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 -> variables.forEach { descriptor, node ->
variableValues.elementData[descriptor]!!.forEach { variableValues.elementData[descriptor]!!.forEach {
node.values += expressionToEdge(it) node.values += expressionToEdge(it)
@@ -499,7 +516,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
val values = mutableListOf<IrExpression>() val values = mutableListOf<IrExpression>()
expressionValuesExtractor.forEachValue(expression) { values += it } expressionValuesExtractor.forEachValue(expression) { values += it }
if (values.size != 1) { 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 { } else {
val value = values[0] val value = values[0]
if (value != expression) { if (value != expression) {
@@ -507,7 +528,11 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
if (edge.castToType == null) if (edge.castToType == null)
edge.node edge.node
else else
DataFlowIR.Node.Variable(listOf(edge), true) DataFlowIR.Node.Variable(
values = listOf(edge),
type = symbolTable.mapType(expression.type),
kind = DataFlowIR.VariableKind.Temporary
)
} else { } else {
when (value) { when (value) {
is IrGetValue -> getNode(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) { fun write(result: ArraySlice) {
result.writeArray(values) { it.write(this) } 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) = fun arrayWrite(array: Edge, index: Edge, value: Edge) =
Node().also { it.arrayWrite = ArrayWrite(array, index, value) } Node().also { it.arrayWrite = ArrayWrite(array, index, value) }
fun variable(values: Array<Edge>, temp: Boolean) = fun variable(values: Array<Edge>, type: Int, kind: DataFlowIR.VariableKind) =
Node().also { it.variable = Variable(values, temp) } Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) }
fun read(data: ArraySlice): Node { fun read(data: ArraySlice): Node {
val type = enumValues<NodeType>()[data.readByte().toInt()] 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)) Node.arrayWrite(buildEdge(node.array), buildEdge(node.index), buildEdge(node.value))
is DataFlowIR.Node.Variable -> 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") else -> error("Unknown node $node")
} }
@@ -1030,7 +1031,8 @@ internal object DFGSerializer {
NodeType.VARIABLE -> { NodeType.VARIABLE -> {
val variable = it.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") else -> error("Unknown node: $it")
@@ -189,6 +189,12 @@ internal object DataFlowIR {
} }
} }
enum class VariableKind {
Ordinary,
Temporary,
CatchParameter
}
sealed class Node { sealed class Node {
class Parameter(val index: Int) : 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 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 } val values = mutableListOf<Edge>().also { it += values }
} }
} }
@@ -385,7 +391,7 @@ internal object DataFlowIR {
is Node.Variable -> { is Node.Variable -> {
val result = StringBuilder() val result = StringBuilder()
result.appendln(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ") result.appendln(" ${node.kind}")
node.values.forEach { node.values.forEach {
result.append(" VAL #${ids[it.node]!!}") result.append(" VAL #${ids[it.node]!!}")
if (it.castToType == null) if (it.castToType == null)
@@ -32,8 +32,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import java.util.* import java.util.*
// TODO: Exceptions.
// Devirtualization analysis is performed using Variable Type Analysis algorithm. // 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. // See http://web.cs.ucla.edu/~palsberg/tba/papers/sundaresan-et-al-oopsla00.pdf for details.
internal object Devirtualization { internal object Devirtualization {
@@ -139,7 +137,7 @@ internal object Devirtualization {
class CastEdge(val node: Node, val suitableTypes: BitSet) 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>) 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) } body.nodes.forEach { dfgNodeToConstraintNode(functionConstraintGraph, it) }
functionNodesMap[body.returns]!!.addEdge(functionConstraintGraph.returns) functionNodesMap[body.returns]!!.addEdge(functionConstraintGraph.returns)
functionNodesMap[body.throws]!!.addEdge(functionConstraintGraph.throws)
DEBUG_OUTPUT(0) { DEBUG_OUTPUT(0) {
@@ -631,7 +630,8 @@ internal object Devirtualization {
} }
val returnsNode = ordinaryNode { "Returns\$$symbol" } 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 constraintGraph.functions[symbol] = functionConstraintGraph
stack.push(symbol) stack.push(symbol)
@@ -701,6 +701,7 @@ internal object Devirtualization {
fictitiousReturnNode fictitiousReturnNode
} }
} else { } else {
calleeConstraintGraph.throws.addEdge(function.throws)
if (receiverType == null) if (receiverType == null)
doCall(calleeConstraintGraph, arguments) doCall(calleeConstraintGraph, arguments)
else { 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] var variableNode = variables[node]
if (variableNode == null) { if (variableNode == null) {
variableNode = ordinaryNode { "Variable\$${function.symbol}" } variableNode = ordinaryNode { "Variable\$${function.symbol}" }
@@ -721,6 +722,8 @@ internal object Devirtualization {
for (value in node.values) { for (value in node.values) {
edgeToConstraintNode(value).addEdge(variableNode) edgeToConstraintNode(value).addEdge(variableNode)
} }
if (node.kind == DataFlowIR.VariableKind.CatchParameter)
function.throws.addCastEdge(createCastEdge(variableNode, node.type.resolved()))
} }
return variableNode return variableNode
} }
@@ -794,6 +797,8 @@ internal object Devirtualization {
if (!returnType.isFinal) { if (!returnType.isFinal) {
receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter)) 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. // And write to some array anything. TODO: This is conservative.
receiverNode.addCastEdge(Node.CastEdge(fieldNode(constraintGraph.arrayItemField), virtualTypeFilter)) receiverNode.addCastEdge(Node.CastEdge(fieldNode(constraintGraph.arrayItemField), virtualTypeFilter))