Data flow graph building
This commit is contained in:
+2
-1
@@ -59,7 +59,8 @@ enum class KonanPhase(val description: String,
|
|||||||
/* ... ... */ RETURNS_INSERTION("Returns insertion for Unit functions", AUTOBOX, LOWER_COROUTINES, LOWER_ENUMS),
|
/* ... ... */ RETURNS_INSERTION("Returns insertion for Unit functions", AUTOBOX, LOWER_COROUTINES, LOWER_ENUMS),
|
||||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||||
/* ... ... */ RTTI("RTTI Generation"),
|
/* ... ... */ RTTI("RTTI Generation"),
|
||||||
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis"),
|
/* ... ... */ BUILD_DFG("Data flow graph building"),
|
||||||
|
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG),
|
||||||
/* ... ... */ CODEGEN("Code Generation"),
|
/* ... ... */ CODEGEN("Code Generation"),
|
||||||
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
|
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
|
||||||
/* */ LINK_STAGE("Link stage"),
|
/* */ LINK_STAGE("Link stage"),
|
||||||
|
|||||||
+16
-2
@@ -25,6 +25,10 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
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) {
|
internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor) {
|
||||||
val overriddenDescriptor = overriddenDescriptor.original
|
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.
|
// We check that either method is open, or one of declarations it overrides is open.
|
||||||
return (overriddenDescriptor.isOverridable
|
return overriddenDescriptor.canBeCalledVirtually
|
||||||
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val inheritsBridge: Boolean
|
val inheritsBridge: Boolean
|
||||||
@@ -50,6 +53,17 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
|
|||||||
&& OverridingUtil.overrides(descriptor.target, overriddenDescriptor)
|
&& OverridingUtil.overrides(descriptor.target, overriddenDescriptor)
|
||||||
&& descriptor.bridgeDirectionsTo(overriddenDescriptor).allNotNeeded()
|
&& 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 {
|
override fun toString(): String {
|
||||||
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
|
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
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.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
@@ -80,6 +81,8 @@ private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
|||||||
internal val FunctionDescriptor.isIntrinsic: Boolean
|
internal val FunctionDescriptor.isIntrinsic: Boolean
|
||||||
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
||||||
|
|
||||||
|
internal fun FunctionDescriptor.externalOrIntrinsic() = isExternal || isIntrinsic || (this is IrBuiltinOperatorDescriptorBase)
|
||||||
|
|
||||||
private val intrinsicTypes = setOf(
|
private val intrinsicTypes = setOf(
|
||||||
"kotlin.Boolean", "kotlin.Char",
|
"kotlin.Boolean", "kotlin.Char",
|
||||||
"kotlin.Byte", "kotlin.Short",
|
"kotlin.Byte", "kotlin.Short",
|
||||||
|
|||||||
+3
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
||||||
@@ -64,6 +65,8 @@ internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DescriptorUtils.isAnonymousObject(this))
|
||||||
|
return false
|
||||||
|
|
||||||
if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) {
|
if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) {
|
||||||
// Currently code generator can access the constructor of the singleton,
|
// Currently code generator can access the constructor of the singleton,
|
||||||
|
|||||||
+8
@@ -24,6 +24,9 @@ import org.jetbrains.kotlin.backend.common.ir.ir2string
|
|||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -73,6 +76,11 @@ internal fun emitLLVM(context: Context) {
|
|||||||
|
|
||||||
generateDebugInfoHeader(context)
|
generateDebugInfoHeader(context)
|
||||||
|
|
||||||
|
var moduleDFG: ModuleDFG? = null
|
||||||
|
phaser.phase(KonanPhase.BUILD_DFG) {
|
||||||
|
moduleDFG = ModuleDFGBuilder(context, irModule).build()
|
||||||
|
}
|
||||||
|
|
||||||
val lifetimes = mutableMapOf<IrElement, Lifetime>()
|
val lifetimes = mutableMapOf<IrElement, Lifetime>()
|
||||||
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
|
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
|
||||||
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
|
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
|
||||||
|
|||||||
+6
-4
@@ -99,7 +99,9 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
|
|||||||
* All fields of the class instance.
|
* All fields of the class instance.
|
||||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
* 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 superClass = classDescriptor.getSuperClassNotAny() // TODO: what if Any has fields?
|
||||||
val superFields = if (superClass != null) getFields(superClass) else emptyList()
|
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.
|
* 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:
|
// TODO: Here's what is going on here:
|
||||||
// The existence of a backing field for a property is only described in the IR,
|
// The existence of a backing field for a property is only described in the IR,
|
||||||
// but not in the PropertyDescriptor.
|
// 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,
|
// We mark serialized properties with a Konan protobuf extension bit,
|
||||||
// so it is present in DeserializedPropertyDescriptor.
|
// 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.
|
// 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 fields = if (irClass != null) {
|
||||||
val declarations = irClass.declarations
|
val declarations = irClass.declarations
|
||||||
|
|
||||||
|
|||||||
+1
-11
@@ -208,17 +208,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val OverriddenFunctionDescriptor.implementation: FunctionDescriptor
|
private val OverriddenFunctionDescriptor.implementation get() = getImplementation(context)
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
|
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
|
||||||
|
|
||||||
|
|||||||
+580
@@ -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)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+434
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -41,3 +41,5 @@ fun String.suffixIfNot(suffix: String) =
|
|||||||
|
|
||||||
fun String.removeSuffixIfPresent(suffix: String) =
|
fun String.removeSuffixIfPresent(suffix: String) =
|
||||||
if (this.endsWith(suffix)) this.dropLast(suffix.length) else this
|
if (this.endsWith(suffix)) this.dropLast(suffix.length) else this
|
||||||
|
|
||||||
|
fun <T> Lazy<T>.getValueOrNull(): T? = if (isInitialized()) value else null
|
||||||
Reference in New Issue
Block a user