diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index 55e9e8bdbae..6101f56a044 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -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() val thrownValues = mutableListOf() + val catchParameters = mutableSetOf() private val returnableBlocks = mutableMapOf() private val suspendableExpressionStack = mutableListOf() @@ -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, val returnValues: List, - val thrownValues: List) { + val thrownValues: List, + val catchParameters: Set) { 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() - 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 { 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() 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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt index 25844caff16..d7d23282088 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt @@ -520,13 +520,14 @@ internal object DFGSerializer { } } - class Variable(val values: Array, val temp: Boolean) { + class Variable(val values: Array, 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, temp: Boolean) = - Node().also { it.variable = Variable(values, temp) } + fun variable(values: Array, type: Int, kind: DataFlowIR.VariableKind) = + Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) } fun read(data: ArraySlice): Node { val type = enumValues()[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()[variable.kind.toInt()]) } else -> error("Unknown node: $it") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index 41595ca1ddf..85a2431b5ed 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -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, val temp: Boolean) : Node() { + class Variable(values: List, val type: Type, val kind: VariableKind) : Node() { val values = mutableListOf().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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt index cb314746076..d178de3bab5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt @@ -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, val returns: Node) + class Function(val symbol: DataFlowIR.FunctionSymbol, val parameters: Array, val returns: Node, val throws: Node) class VirtualCallSiteReceivers(val receiver: Node, val caller: DataFlowIR.FunctionSymbol, val devirtualizedCallees: List) @@ -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))