Supported exceptions in devirtualization
This commit is contained in:
+39
-14
@@ -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)
|
||||
|
||||
+9
-7
@@ -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")
|
||||
|
||||
+8
-2
@@ -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)
|
||||
|
||||
+10
-5
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user