Data flow graph building

This commit is contained in:
Igor Chevdar
2017-11-21 12:44:29 +03:00
parent 356400158f
commit 0a07db93ed
10 changed files with 1055 additions and 18 deletions
@@ -59,7 +59,8 @@ enum class KonanPhase(val description: String,
/* ... ... */ RETURNS_INSERTION("Returns insertion for Unit functions", AUTOBOX, LOWER_COROUTINES, LOWER_ENUMS),
/* ... */ BITCODE("LLVM BitCode Generation"),
/* ... ... */ RTTI("RTTI Generation"),
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis"),
/* ... ... */ BUILD_DFG("Data flow graph building"),
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG),
/* ... ... */ CODEGEN("Code Generation"),
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
/* */ LINK_STAGE("Link stage"),
@@ -25,6 +25,10 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
internal val FunctionDescriptor.canBeCalledVirtually: Boolean
// We check that either method is open, or one of declarations it overrides is open.
get() = isOverridable || DescriptorUtils.getAllOverriddenDeclarations(this).any { it.isOverridable }
internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor) {
val overriddenDescriptor = overriddenDescriptor.original
@@ -41,8 +45,7 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
}
// We check that either method is open, or one of declarations it overrides is open.
return (overriddenDescriptor.isOverridable
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable })
return overriddenDescriptor.canBeCalledVirtually
}
val inheritsBridge: Boolean
@@ -50,6 +53,17 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
&& OverridingUtil.overrides(descriptor.target, overriddenDescriptor)
&& descriptor.bridgeDirectionsTo(overriddenDescriptor).allNotNeeded()
fun getImplementation(context: Context): FunctionDescriptor {
val target = descriptor.target
if (!needBridge) return target
val bridgeOwner = if (inheritsBridge) {
target // Bridge is inherited from superclass.
} else {
descriptor
}
return context.specialDeclarationsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor))
}
override fun toString(): String {
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
@@ -80,6 +81,8 @@ private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
internal val FunctionDescriptor.isIntrinsic: Boolean
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
internal fun FunctionDescriptor.externalOrIntrinsic() = isExternal || isIntrinsic || (this is IrBuiltinOperatorDescriptorBase)
private val intrinsicTypes = setOf(
"kotlin.Boolean", "kotlin.Char",
"kotlin.Byte", "kotlin.Short",
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
@@ -64,6 +65,8 @@ internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
return true
}
if (DescriptorUtils.isAnonymousObject(this))
return false
if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) {
// Currently code generator can access the constructor of the singleton,
@@ -24,6 +24,9 @@ import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.backend.konan.util.getValueOrNull
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -73,6 +76,11 @@ internal fun emitLLVM(context: Context) {
generateDebugInfoHeader(context)
var moduleDFG: ModuleDFG? = null
phaser.phase(KonanPhase.BUILD_DFG) {
moduleDFG = ModuleDFGBuilder(context, irModule).build()
}
val lifetimes = mutableMapOf<IrElement, Lifetime>()
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
@@ -99,7 +99,9 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
* All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/
internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor) = context.getFields(classDescriptor)
internal fun Context.getFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
val superClass = classDescriptor.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) getFields(superClass) else emptyList()
@@ -109,7 +111,7 @@ internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor): List<Prop
/**
* Fields declared in the class.
*/
private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
private fun Context.getDeclaredFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
// TODO: Here's what is going on here:
// The existence of a backing field for a property is only described in the IR,
// but not in the PropertyDescriptor.
@@ -117,10 +119,10 @@ private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): Li
// We mark serialized properties with a Konan protobuf extension bit,
// so it is present in DeserializedPropertyDescriptor.
//
// In this function we check the presence of the backing filed
// In this function we check the presence of the backing field
// two ways: first we check IR, then we check the protobuf extension.
val irClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
val irClass = ir.moduleIndexForCodegen.classes[classDescriptor]
val fields = if (irClass != null) {
val declarations = irClass.declarations
@@ -208,17 +208,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
}
internal val OverriddenFunctionDescriptor.implementation: FunctionDescriptor
get() {
val target = descriptor.target
if (!needBridge) return target
val bridgeOwner = if (inheritsBridge) {
target // Bridge is inherited from superclass.
} else {
descriptor
}
return context.specialDeclarationsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor))
}
private val OverriddenFunctionDescriptor.implementation get() = getImplementation(context)
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
@@ -0,0 +1,580 @@
package org.jetbrains.kotlin.backend.konan.optimizations
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.common.ir.ir2stringWhole
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.target
import org.jetbrains.kotlin.backend.konan.ir.IrSuspendableExpression
import org.jetbrains.kotlin.backend.konan.ir.IrSuspensionPoint
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
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.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
private fun computeErasure(type: KotlinType, erasure: MutableList<KotlinType>) {
val descriptor = type.constructor.declarationDescriptor
when (descriptor) {
is ClassDescriptor -> erasure += type.makeNotNullable()
is TypeParameterDescriptor -> {
descriptor.upperBounds.forEach {
computeErasure(it, erasure)
}
}
else -> TODO(descriptor.toString())
}
}
internal fun KotlinType.erasure(): List<KotlinType> {
val result = mutableListOf<KotlinType>()
computeErasure(this, result)
return result
}
private fun MemberScope.getOverridingOf(function: FunctionDescriptor) = when (function) {
is PropertyGetterDescriptor ->
this.getContributedVariables(function.correspondingProperty.name, NoLookupLocation.FROM_BACKEND)
.firstOrNull { OverridingUtil.overrides(it, function.correspondingProperty) }?.getter
is PropertySetterDescriptor ->
this.getContributedVariables(function.correspondingProperty.name, NoLookupLocation.FROM_BACKEND)
.firstOrNull { OverridingUtil.overrides(it, function.correspondingProperty) }?.setter
else -> this.getContributedFunctions(function.name, NoLookupLocation.FROM_BACKEND)
.firstOrNull { OverridingUtil.overrides(it, function) }
}
private fun IrTypeOperator.isCast() =
this == IrTypeOperator.CAST || this == IrTypeOperator.IMPLICIT_CAST || this == IrTypeOperator.SAFE_CAST
private class VariableValues {
val elementData = HashMap<VariableDescriptor, MutableSet<IrExpression>>()
fun addEmpty(variable: VariableDescriptor) =
elementData.getOrPut(variable, { mutableSetOf() })
fun add(variable: VariableDescriptor, element: IrExpression) =
elementData[variable]?.add(element)
fun add(variable: VariableDescriptor, elements: Set<IrExpression>) =
elementData[variable]?.addAll(elements)
fun get(variable: VariableDescriptor): Set<IrExpression>? =
elementData[variable]
fun computeClosure() {
elementData.forEach { key, _ ->
add(key, computeValueClosure(key))
}
}
// Computes closure of all possible values for given variable.
private fun computeValueClosure(value: VariableDescriptor): Set<IrExpression> {
val result = mutableSetOf<IrExpression>()
val seen = mutableSetOf<VariableDescriptor>()
dfs(value, seen, result)
return result
}
private fun dfs(value: VariableDescriptor, seen: MutableSet<VariableDescriptor>, result: MutableSet<IrExpression>) {
seen += value
val elements = elementData[value]
?: return
for (element in elements) {
if (element !is IrGetValue)
result += element
else {
val descriptor = element.descriptor
if (descriptor is VariableDescriptor && !seen.contains(descriptor))
dfs(descriptor, seen, result)
}
}
}
}
private class ExpressionValuesExtractor(val returnableBlockValues: Map<IrReturnableBlock, List<IrExpression>>,
val suspendableExpressionValues: Map<IrSuspendableExpression, List<IrSuspensionPoint>>) {
fun forEachValue(expression: IrExpression, block: (IrExpression) -> Unit) {
when (expression) {
is IrReturnableBlock -> returnableBlockValues[expression]!!.forEach { forEachValue(it, block) }
is IrSuspendableExpression ->
(suspendableExpressionValues[expression]!! + expression.result).forEach { forEachValue(it, block) }
is IrSuspensionPoint -> {
forEachValue(expression.result, block)
forEachValue(expression.resumeResult, block)
}
is IrContainerExpression -> {
if (expression.statements.isNotEmpty())
forEachValue(expression.statements.last() as IrExpression, block)
}
is IrWhen -> expression.branches.forEach { forEachValue(it.result, block) }
is IrMemberAccessExpression -> block(expression)
is IrGetValue -> block(expression)
is IrGetField -> block(expression)
is IrVararg -> /* Sometimes, we keep vararg till codegen phase (for constant arrays). */
block(expression)
is IrConst<*> -> block(expression)
is IrTypeOperatorCall -> {
if (!expression.operator.isCast())
block(expression)
else { // Propagate cast to sub-values.
forEachValue(expression.argument) { value ->
with(expression) {
block(IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, typeOperand, value))
}
}
}
}
is IrTry -> {
forEachValue(expression.tryResult, block)
expression.catches.forEach { forEachValue(it.result, block) }
}
is IrGetObjectValue -> block(expression)
is IrFunctionReference -> block(expression)
is IrSetField -> block(expression)
else -> {
if ((expression.type.isUnit() || expression.type.isNothing())) {
block(IrGetObjectValueImpl(expression.startOffset, expression.endOffset,
expression.type, (expression.type.constructor.declarationDescriptor as ClassDescriptor)))
}
else TODO(ir2stringWhole(expression))
}
}
}
}
internal class ModuleDFG(val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
val symbolTable: DataFlowIR.SymbolTable)
internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFragment) {
private val DEBUG = 0
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
if (DEBUG > severity) block()
}
private val module = DataFlowIR.Module(irModule.descriptor)
private val symbolTable = DataFlowIR.SymbolTable(context, irModule, module)
// Possible values of a returnable block.
private val returnableBlockValues = mutableMapOf<IrReturnableBlock, MutableList<IrExpression>>()
// All suspension points within specified suspendable expression.
private val suspendableExpressionValues = mutableMapOf<IrSuspendableExpression, MutableList<IrSuspensionPoint>>()
private val expressionValuesExtractor = ExpressionValuesExtractor(returnableBlockValues, suspendableExpressionValues)
fun build(): ModuleDFG {
val functions = mutableMapOf<DataFlowIR.FunctionSymbol, DataFlowIR.Function>()
irModule.accept(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
declaration.body?.let {
DEBUG_OUTPUT(1) {
println("Analysing function ${declaration.descriptor}")
println("IR: ${ir2stringWhole(declaration)}")
}
analyze(declaration.descriptor, it)
}
}
override fun visitField(declaration: IrField) {
declaration.initializer?.let {
DEBUG_OUTPUT(1) {
println("Analysing global field ${declaration.descriptor}")
println("IR: ${ir2stringWhole(declaration)}")
}
analyze(declaration.descriptor, it)
}
}
private fun analyze(descriptor: CallableDescriptor, body: IrElement) {
// Find all interesting expressions, variables and functions.
val visitor = ElementFinderVisitor()
body.acceptVoid(visitor)
DEBUG_OUTPUT(1) {
println("FIRST PHASE")
visitor.variableValues.elementData.forEach { t, u ->
println("VAR $t:")
u.forEach {
println(" ${ir2stringWhole(it)}")
}
}
visitor.expressions.forEach { t ->
println("EXP ${ir2stringWhole(t)}")
}
}
// Compute transitive closure of possible values for variables.
visitor.variableValues.computeClosure()
DEBUG_OUTPUT(1) {
println("SECOND PHASE")
visitor.variableValues.elementData.forEach { t, u ->
println("VAR $t:")
u.forEach {
println(" ${ir2stringWhole(it)}")
}
}
}
val function = FunctionDFGBuilder(expressionValuesExtractor, visitor.variableValues,
descriptor, visitor.expressions, visitor.returnValues).build()
DEBUG_OUTPUT(1) {
function.debugOutput()
}
functions.put(function.symbol, function)
}
}, data = null)
DEBUG_OUTPUT(2) {
println("SYMBOL TABLE:")
symbolTable.classMap.forEach { descriptor, type ->
println(" DESCRIPTOR: $descriptor")
println(" TYPE: $type")
if (type !is DataFlowIR.Type.Declared)
return@forEach
println(" SUPER TYPES:")
type.superTypes.forEach { println(" $it") }
println(" VTABLE:")
type.vtable.forEach { println(" $it") }
println(" ITABLE:")
type.itable.forEach { println(" ${it.key} -> ${it.value}") }
}
}
return ModuleDFG(functions, symbolTable)
}
private inner class ElementFinderVisitor : IrElementVisitorVoid {
val expressions = mutableListOf<IrExpression>()
val variableValues = VariableValues()
val returnValues = mutableListOf<IrExpression>()
private val returnableBlocks = mutableMapOf<FunctionDescriptor, IrReturnableBlock>()
private val suspendableExpressionStack = mutableListOf<IrSuspendableExpression>()
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun assignVariable(variable: VariableDescriptor, value: IrExpression) {
expressionValuesExtractor.forEachValue(value) {
variableValues.add(variable, it)
}
}
override fun visitExpression(expression: IrExpression) {
when (expression) {
is IrMemberAccessExpression,
is IrGetField,
is IrGetObjectValue,
is IrVararg,
is IrConst<*>,
is IrTypeOperatorCall ->
expressions += expression
}
if (expression is IrReturnableBlock) {
returnableBlocks.put(expression.descriptor, expression)
returnableBlockValues.put(expression, mutableListOf())
}
if (expression is IrSuspendableExpression) {
suspendableExpressionStack.push(expression)
suspendableExpressionValues.put(expression, mutableListOf())
}
if (expression is IrSuspensionPoint)
suspendableExpressionValues[suspendableExpressionStack.peek()!!]!!.add(expression)
super.visitExpression(expression)
if (expression is IrReturnableBlock)
returnableBlocks.remove(expression.descriptor)
if (expression is IrSuspendableExpression)
suspendableExpressionStack.pop()
}
override fun visitSetField(expression: IrSetField) {
expressions += expression
super.visitSetField(expression)
}
// TODO: hack to overcome bad code in InlineConstructorsTransformation.
private val FQ_NAME_INLINE_CONSTRUCTOR = FqName("konan.internal.InlineConstructor")
override fun visitReturn(expression: IrReturn) {
val returnableBlock = returnableBlocks[expression.returnTarget]
if (returnableBlock != null) {
returnableBlockValues[returnableBlock]!!.add(expression.value)
} else { // Non-local return.
if (!expression.type.isUnit()) {
if (!expression.returnTarget.annotations.hasAnnotation(FQ_NAME_INLINE_CONSTRUCTOR)) // Not inline constructor.
returnValues += expression.value
}
}
super.visitReturn(expression)
}
override fun visitSetVariable(expression: IrSetVariable) {
super.visitSetVariable(expression)
assignVariable(expression.descriptor, expression.value)
}
override fun visitVariable(declaration: IrVariable) {
variableValues.addEmpty(declaration.descriptor)
super.visitVariable(declaration)
declaration.initializer?.let { assignVariable(declaration.descriptor, it) }
}
}
private val doResumeFunctionDescriptor = context.getInternalClass("CoroutineImpl").unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_BACKEND).single()
private val getContinuationSymbol = context.ir.symbols.getContinuation
private inner class FunctionDFGBuilder(val expressionValuesExtractor: ExpressionValuesExtractor,
val variableValues: VariableValues,
val descriptor: CallableDescriptor,
val expressions: List<IrExpression>,
val returnValues: List<IrExpression>) {
private val allParameters = (descriptor as? FunctionDescriptor)?.allParameters ?: emptyList()
private val templateParameters = allParameters.withIndex().associateBy({ it.value }, { DataFlowIR.Node.Parameter(it.index) })
private val continuationParameter =
if (descriptor.isSuspend)
DataFlowIR.Node.Parameter(allParameters.size)
else {
if (doResumeFunctionDescriptor in descriptor.overriddenDescriptors) // <this> is a CoroutineImpl inheritor.
templateParameters[descriptor.dispatchReceiverParameter!!] // It is its own continuation.
else null
}
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) }
)
fun build(): DataFlowIR.Function {
expressions.forEach { getNode(it) }
val returnsNode = DataFlowIR.Node.Variable(returnValues.map { expressionToEdge(it) }, true)
variables.forEach { descriptor, node ->
variableValues.elementData[descriptor]!!.forEach {
node.values += expressionToEdge(it)
}
}
val allNodes = nodes.values + variables.values + templateParameters.values + returnsNode +
(if (descriptor.isSuspend) listOf(continuationParameter!!) else emptyList())
return DataFlowIR.Function(
symbol = symbolTable.mapFunction(descriptor),
isGlobalInitializer = descriptor is PropertyDescriptor,
numberOfParameters = templateParameters.size + if (descriptor.isSuspend) 1 else 0,
body = DataFlowIR.FunctionBody(allNodes.distinct().toList(), returnsNode)
)
}
private fun expressionToEdge(expression: IrExpression) =
if (expression is IrTypeOperatorCall && expression.operator.isCast())
DataFlowIR.Edge(getNode(expression.argument), symbolTable.mapType(expression.typeOperand))
else DataFlowIR.Edge(getNode(expression), null)
private fun getNode(expression: IrExpression): DataFlowIR.Node {
if (expression is IrGetValue) {
val descriptor = expression.descriptor
if (descriptor is ParameterDescriptor)
return templateParameters[descriptor]!!
return variables[descriptor as VariableDescriptor]!!
}
return nodes.getOrPut(expression) {
DEBUG_OUTPUT(1) {
println("Converting expression")
println(ir2stringWhole(expression))
}
val values = mutableListOf<IrExpression>()
expressionValuesExtractor.forEachValue(expression) { values += it }
if (values.size != 1) {
DataFlowIR.Node.Variable(values.map { expressionToEdge(it) }, true)
} else {
val value = values[0]
if (value != expression) {
val edge = expressionToEdge(value)
if (edge.castToType == null)
edge.node
else
DataFlowIR.Node.Variable(listOf(edge), true)
} else {
when (value) {
is IrGetValue -> getNode(value)
is IrVararg,
is IrConst<*>,
is IrFunctionReference -> DataFlowIR.Node.Const(symbolTable.mapType(value.type))
is IrGetObjectValue -> DataFlowIR.Node.Singleton(
symbolTable.mapType(value.type),
if (value.type.isNothing()) // <Nothing> is not a singleton though its instance is get with <IrGetObject> operation.
null
else symbolTable.mapFunction(value.descriptor.constructors.single())
)
is IrCall -> {
if (value.symbol == getContinuationSymbol) {
getContinuation()
} else {
val callee = value.descriptor
val arguments = value.getArguments()
.map { expressionToEdge(it.second) }
.let {
if (callee.isSuspend)
it + DataFlowIR.Edge(getContinuation(), null)
else
it
}
if (callee is ConstructorDescriptor) {
DataFlowIR.Node.NewObject(
symbolTable.mapFunction(callee),
arguments,
symbolTable.mapClass(callee.constructedClass)
)
} else {
if (callee.isOverridable && value.superQualifier == null) {
val owner = callee.containingDeclaration as ClassDescriptor
val vTableBuilder = context.getVtableBuilder(owner)
if (owner.isInterface) {
DataFlowIR.Node.ItableCall(
symbolTable.mapFunction(callee.target),
symbolTable.mapClass(owner),
callee.functionName.localHash.value,
arguments,
symbolTable.mapType(callee.returnType!!),
value
)
} else {
val vtableIndex = vTableBuilder.vtableIndex(callee)
assert(vtableIndex >= 0, { "Unable to find function $callee in vtable of $owner" })
DataFlowIR.Node.VtableCall(
symbolTable.mapFunction(callee.target),
symbolTable.mapClass(owner),
vtableIndex,
arguments,
symbolTable.mapType(callee.returnType!!),
value
)
}
} else {
val actualCallee = (value.superQualifier?.unsubstitutedMemberScope?.getOverridingOf(callee) ?: callee).target
DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(actualCallee),
arguments,
symbolTable.mapType(actualCallee.returnType!!),
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) }
)
}
}
}
}
is IrDelegatingConstructorCall -> {
val thiz = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
(descriptor as ConstructorDescriptor).constructedClass.thisAsReceiverParameter)
val arguments = listOf(thiz) + value.getArguments().map { it.second }
DataFlowIR.Node.StaticCall(
symbolTable.mapFunction(value.descriptor),
arguments.map { expressionToEdge(it) },
symbolTable.mapClass(context.builtIns.unit),
symbolTable.mapType(thiz.type)
)
}
is IrGetField -> {
val receiver = value.receiver?.let { expressionToEdge(it) }
val receiverType = value.receiver?.let { symbolTable.mapType(it.type) }
DataFlowIR.Node.FieldRead(
receiver,
DataFlowIR.Field(
receiverType,
value.descriptor.name.asString()
)
)
}
is IrSetField -> {
val receiver = value.receiver?.let { expressionToEdge(it) }
val receiverType = value.receiver?.let { symbolTable.mapType(it.type) }
DataFlowIR.Node.FieldWrite(
receiver,
DataFlowIR.Field(
receiverType,
value.descriptor.name.asString()
),
expressionToEdge(value.value)
)
}
is IrTypeOperatorCall -> {
assert(!value.operator.isCast(), { "Casts should've been handled earlier" })
expressionToEdge(value.argument) // Put argument as a separate vertex.
DataFlowIR.Node.Const(symbolTable.mapType(value.type)) // All operators except casts are basically constants.
}
else -> TODO("Unknown expression: ${ir2stringWhole(value)}")
}
}
}
}
}
}
}
@@ -0,0 +1,434 @@
package org.jetbrains.kotlin.backend.konan.optimizations
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.externalOrIntrinsic
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.isExported
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
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.expressions.IrCall
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
internal object DataFlowIR {
abstract class Type {
// Special marker type forbidding devirtualization on its instances.
object Virtual : Declared(false, true)
class External(val name: String) : Type() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is External) return false
return name == other.name
}
override fun hashCode(): Int {
return name.hashCode()
}
override fun toString(): String {
return "ExternalType(name='$name')"
}
}
abstract class Declared(val isFinal: Boolean, val isAbstract: Boolean) : Type() {
val superTypes = mutableListOf<Type>()
val vtable = mutableListOf<FunctionSymbol>()
val itable = mutableMapOf<Long, FunctionSymbol>()
}
class Public(val name: String, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Public) return false
return name == other.name
}
override fun hashCode(): Int {
return name.hashCode()
}
override fun toString(): String {
return "PublicType(name='$name')"
}
}
class Private(val name: String, val index: Int, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Private) return false
return index == other.index
}
override fun hashCode(): Int {
return index
}
override fun toString(): String {
return "PrivateType(index=$index, name='$name')"
}
}
}
class Module(val descriptor: ModuleDescriptor) {
var numberOfFunctions = 0
}
abstract class FunctionSymbol {
class External(val name: String) : FunctionSymbol() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is External) return false
return name == other.name
}
override fun hashCode(): Int {
return name.hashCode()
}
override fun toString(): String {
return "ExternalFunction(name='$name')"
}
}
abstract class Declared(val module: Module, val symbolTableIndex: Int) : FunctionSymbol()
class Public(val name: String, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Public) return false
return name == other.name
}
override fun hashCode(): Int {
return name.hashCode()
}
override fun toString(): String {
return "PublicFunction(name='$name')"
}
}
class Private(val name: String, val index: Int, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Private) return false
return index == other.index
}
override fun hashCode(): Int {
return index
}
override fun toString(): String {
return "PrivateFunction(index=$index, name='$name')"
}
}
}
data class Field(val type: Type?, val name: String)
class Edge(val castToType: Type?) {
lateinit var node: Node
constructor(node: Node, castToType: Type?) : this(castToType) {
this.node = node
}
}
sealed class Node {
class Parameter(val index: Int) : Node()
class Const(val type: Type) : Node()
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>, val returnType: Type) : Node()
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
val receiverType: Type?) : Call(callee, arguments, returnType)
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, type: Type) : Call(constructor, arguments, type)
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
val receiverType: Type, val callSite: IrCall?) : Call(callee, arguments, returnType)
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long,
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
class FieldRead(val receiver: Edge?, val field: Field) : Node()
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node()
class Variable(values: List<Edge>, val temp: Boolean) : Node() {
val values = mutableListOf<Edge>().also { it += values }
}
}
class FunctionBody(val nodes: List<Node>, val returns: Node.Variable)
class Function(val symbol: FunctionSymbol,
val isGlobalInitializer: Boolean,
val numberOfParameters: Int,
val body: FunctionBody) {
fun printNode(node: Node, ids: Map<Node, Int>) {
when (node) {
is Node.Const ->
println(" CONST ${node.type}")
is Node.Parameter ->
println(" PARAM ${node.index}")
is Node.Singleton ->
println(" SINGLETON ${node.type}")
is Node.StaticCall -> {
println(" STATIC CALL ${node.callee}")
node.arguments.forEach {
print(" ARG #${ids[it.node]!!}")
if (it.castToType == null)
println()
else
println(" CASTED TO ${it.castToType}")
}
}
is Node.VtableCall -> {
println(" VIRTUAL CALL ${node.callee}")
println(" RECEIVER: ${node.receiverType}")
println(" VTABLE INDEX: ${node.calleeVtableIndex}")
node.arguments.forEach {
print(" ARG #${ids[it.node]!!}")
if (it.castToType == null)
println()
else
println(" CASTED TO ${it.castToType}")
}
}
is Node.ItableCall -> {
println(" INTERFACE CALL ${node.callee}")
println(" RECEIVER: ${node.receiverType}")
println(" METHOD HASH: ${node.calleeHash}")
node.arguments.forEach {
print(" ARG #${ids[it.node]!!}")
if (it.castToType == null)
println()
else
println(" CASTED TO ${it.castToType}")
}
}
is Node.NewObject -> {
println(" NEW OBJECT ${node.callee}")
println(" TYPE ${node.returnType}")
node.arguments.forEach {
print(" ARG #${ids[it.node]!!}")
if (it.castToType == null)
println()
else
println(" CASTED TO ${it.castToType}")
}
}
is Node.FieldRead -> {
println(" FIELD READ ${node.field}")
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
if (node.receiver?.castToType == null)
println()
else
println(" CASTED TO ${node.receiver.castToType}")
}
is Node.FieldWrite -> {
println(" FIELD WRITE ${node.field}")
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
if (node.receiver?.castToType == null)
println()
else
println(" CASTED TO ${node.receiver.castToType}")
print(" VALUE #${ids[node.value.node]!!}")
if (node.value.castToType == null)
println()
else
println(" CASTED TO ${node.value.castToType}")
}
is Node.Variable -> {
println(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ")
node.values.forEach {
print(" VAL #${ids[it.node]!!}")
if (it.castToType == null)
println()
else
println(" CASTED TO ${it.castToType}")
}
}
else -> {
println(" UNKNOWN: ${node::class.java}")
}
}
}
fun debugOutput() {
println("FUNCTION TEMPLATE $symbol")
println("Params: $numberOfParameters")
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
body.nodes.forEach {
println(" NODE #${ids[it]!!}")
printNode(it, ids)
}
println(" RETURNS")
printNode(body.returns, ids)
}
}
class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) {
val classMap = mutableMapOf<ClassDescriptor, Type>()
val functionMap = mutableMapOf<CallableDescriptor, FunctionSymbol>()
var privateTypeIndex = 0
var privateFunIndex = 0
var couldBeCalledVirtuallyIndex = 0
init {
irModule.accept(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
declaration.body?.let { mapFunction(declaration.descriptor) }
}
override fun visitField(declaration: IrField) {
declaration.initializer?.let { mapFunction(declaration.descriptor) }
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
mapClass(declaration.descriptor)
}
}, data = null)
}
private fun ClassDescriptor.isFinal() = modality == Modality.FINAL && kind != ClassKind.ENUM_CLASS
fun mapClass(descriptor: ClassDescriptor): Type {
// Do not try to devirtualize ObjC classes.
if (descriptor.module.name == Name.special("<forward declarations>") || descriptor.isObjCClass())
return Type.Virtual
val name = descriptor.fqNameSafe.asString()
if (descriptor.module != irModule.descriptor)
return classMap.getOrPut(descriptor) { Type.External(name) }
classMap[descriptor]?.let { return it }
val isFinal = descriptor.isFinal()
val isAbstract = descriptor.isAbstract()
val type = if (descriptor.isExported())
Type.Public(name, isFinal, isAbstract)
else
Type.Private(name, privateTypeIndex++, isFinal, isAbstract)
if (!descriptor.isInterface) {
val vtableBuilder = context.getVtableBuilder(descriptor)
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)) }
if (!isAbstract) {
vtableBuilder.methodTableEntries.forEach {
type.itable.put(
it.overriddenDescriptor.functionName.localHash.value,
mapFunction(it.getImplementation(context))
)
}
}
}
classMap.put(descriptor, type)
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
return type
}
fun mapType(type: KotlinType) =
mapClass(type.erasure().single().constructor.declarationDescriptor as ClassDescriptor)
// TODO: use from LlvmDeclarations.
private fun getFqName(descriptor: DeclarationDescriptor): FqName {
if (descriptor is PackageFragmentDescriptor) {
return descriptor.fqName
}
val containingDeclaration = descriptor.containingDeclaration
val parent = if (containingDeclaration != null) {
getFqName(containingDeclaration)
} else {
FqName.ROOT
}
val localName = descriptor.name
return parent.child(localName)
}
fun mapFunction(descriptor: CallableDescriptor) = descriptor.original.let {
functionMap.getOrPut(it) {
when (it) {
is PropertyDescriptor ->
FunctionSymbol.Private("${it.symbolName}_init", privateFunIndex++, module, -1)
is FunctionDescriptor -> {
if (it.module != irModule.descriptor || it.externalOrIntrinsic())
FunctionSymbol.External(it.symbolName)
else {
val isAbstract = it.modality == Modality.ABSTRACT
val classDescriptor = it.containingDeclaration as? ClassDescriptor
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
&& classDescriptor.kind != ClassKind.ANNOTATION_CLASS
&& (it.isOverridableOrOverrides || it.name.asString().contains("<bridge-") || !classDescriptor.isFinal())
if (placeToFunctionsTable)
++module.numberOfFunctions
val symbolTableIndex = if (!placeToFunctionsTable) -1 else couldBeCalledVirtuallyIndex++
if (it.isExported())
FunctionSymbol.Public(it.symbolName, module, symbolTableIndex)
else
FunctionSymbol.Private(getFqName(it).asString() + "#internal", privateFunIndex++, module, symbolTableIndex)
}
}
else -> error("Unknown descriptor: $it")
}
}
}
}
}
@@ -41,3 +41,5 @@ fun String.suffixIfNot(suffix: String) =
fun String.removeSuffixIfPresent(suffix: String) =
if (this.endsWith(suffix)) this.dropLast(suffix.length) else this
fun <T> Lazy<T>.getValueOrNull(): T? = if (isInitialized()) value else null