Implemented DCE before code generation
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
package kotlinx.cinterop
|
package kotlinx.cinterop
|
||||||
import kotlin.native.*
|
import kotlin.native.*
|
||||||
import kotlin.native.internal.ExportTypeInfo
|
import kotlin.native.internal.ExportTypeInfo
|
||||||
|
import kotlin.native.internal.ExportForCppRuntime
|
||||||
import kotlin.native.internal.TypedIntrinsic
|
import kotlin.native.internal.TypedIntrinsic
|
||||||
import kotlin.native.internal.IntrinsicType
|
import kotlin.native.internal.IntrinsicType
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ internal fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (t
|
|||||||
@SymbolName("Kotlin_Interop_refFromObjC")
|
@SymbolName("Kotlin_Interop_refFromObjC")
|
||||||
external fun <T> interpretObjCPointerOrNull(objcPtr: NativePtr): T?
|
external fun <T> interpretObjCPointerOrNull(objcPtr: NativePtr): T?
|
||||||
|
|
||||||
|
@ExportForCppRuntime
|
||||||
inline fun <T : Any> interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull<T>(objcPtr)!!
|
inline fun <T : Any> interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull<T>(objcPtr)!!
|
||||||
|
|
||||||
@SymbolName("Kotlin_Interop_refToObjC")
|
@SymbolName("Kotlin_Interop_refToObjC")
|
||||||
|
|||||||
+3
@@ -215,6 +215,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
|||||||
private val packageScope by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.internalPackageName).memberScope }
|
private val packageScope by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.internalPackageName).memberScope }
|
||||||
|
|
||||||
val nativePtr by lazy { packageScope.getContributedClassifier(NATIVE_PTR_NAME) as ClassDescriptor }
|
val nativePtr by lazy { packageScope.getContributedClassifier(NATIVE_PTR_NAME) as ClassDescriptor }
|
||||||
|
val nonNullNativePtr by lazy { packageScope.getContributedClassifier(NON_NULL_NATIVE_PTR_NAME) as ClassDescriptor }
|
||||||
val getNativeNullPtr by lazy { packageScope.getContributedFunctions("getNativeNullPtr").single() }
|
val getNativeNullPtr by lazy { packageScope.getContributedFunctions("getNativeNullPtr").single() }
|
||||||
val immutableBlobOf by lazy {
|
val immutableBlobOf by lazy {
|
||||||
builtIns.builtInsModule.getPackage(KonanFqNames.packageName).memberScope.getContributedFunctions("immutableBlobOf").single()
|
builtIns.builtInsModule.getPackage(KonanFqNames.packageName).memberScope.getContributedFunctions("immutableBlobOf").single()
|
||||||
@@ -459,6 +460,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
|||||||
lateinit var codegenVisitor: CodeGeneratorVisitor
|
lateinit var codegenVisitor: CodeGeneratorVisitor
|
||||||
var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null
|
var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null
|
||||||
|
|
||||||
|
var referencedFunctions: Set<IrFunction>? = null
|
||||||
|
|
||||||
val isNativeLibrary: Boolean by lazy {
|
val isNativeLibrary: Boolean by lazy {
|
||||||
val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
|
val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
|
||||||
kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC
|
kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC
|
||||||
|
|||||||
+3
-2
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.konan.ir.containsNull
|
|||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
@@ -275,6 +276,6 @@ private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType
|
|||||||
.firstOrNull { it.descriptor.fqNameUnsafe == InteropFqNames.nativePointed }
|
.firstOrNull { it.descriptor.fqNameUnsafe == InteropFqNames.nativePointed }
|
||||||
|
|
||||||
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
|
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
|
||||||
clazz.constructors.first { it.isPrimary }.valueParameters.single().type
|
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
|
||||||
|
?: clazz.declarations.filterIsInstance<IrProperty>().single { it.backingField != null }.backingField!!.type
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -10,13 +10,14 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
internal const val NATIVE_PTR_NAME = "NativePtr"
|
internal const val NATIVE_PTR_NAME = "NativePtr"
|
||||||
|
internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr"
|
||||||
|
|
||||||
object KonanFqNames {
|
object KonanFqNames {
|
||||||
|
|
||||||
val packageName = FqName("kotlin.native")
|
val packageName = FqName("kotlin.native")
|
||||||
val internalPackageName = FqName("kotlin.native.internal")
|
val internalPackageName = FqName("kotlin.native.internal")
|
||||||
val nativePtr = internalPackageName.child(Name.identifier(NATIVE_PTR_NAME)).toUnsafe()
|
val nativePtr = internalPackageName.child(Name.identifier(NATIVE_PTR_NAME)).toUnsafe()
|
||||||
val nonNullNativePtr = FqNameUnsafe("kotlin.native.internal.NonNullNativePtr")
|
val nonNullNativePtr = internalPackageName.child(Name.identifier(NON_NULL_NATIVE_PTR_NAME)).toUnsafe()
|
||||||
val throws = FqName("kotlin.native.Throws")
|
val throws = FqName("kotlin.native.Throws")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail
|
|||||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
|
import org.jetbrains.kotlin.ir.util.isReal
|
||||||
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.ExternalOverridabilityCondition
|
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||||
@@ -223,6 +224,7 @@ fun ConstructorDescriptor.objCConstructorIsDesignated(): Boolean {
|
|||||||
|
|
||||||
val IrConstructor.isObjCConstructor get() = this.descriptor.annotations.hasAnnotation(objCConstructorFqName)
|
val IrConstructor.isObjCConstructor get() = this.descriptor.annotations.hasAnnotation(objCConstructorFqName)
|
||||||
|
|
||||||
|
// TODO-DCE-OBJC-INIT: Selector should be preserved by DCE.
|
||||||
fun IrConstructor.getObjCInitMethod(): IrSimpleFunction? {
|
fun IrConstructor.getObjCInitMethod(): IrSimpleFunction? {
|
||||||
return this.descriptor.annotations.findAnnotation(objCConstructorFqName)?.let {
|
return this.descriptor.annotations.findAnnotation(objCConstructorFqName)?.let {
|
||||||
val initSelector = it.getStringValue("initSelector")
|
val initSelector = it.getStringValue("initSelector")
|
||||||
|
|||||||
+1
@@ -8,6 +8,7 @@ object RuntimeNames {
|
|||||||
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
||||||
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
||||||
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
||||||
|
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||||
val independent = FqName("kotlin.native.internal.Independent")
|
val independent = FqName("kotlin.native.internal.Independent")
|
||||||
val filterExceptions = FqName("kotlin.native.internal.FilterExceptions")
|
val filterExceptions = FqName("kotlin.native.internal.FilterExceptions")
|
||||||
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
|
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
|
||||||
|
|||||||
+5
-4
@@ -291,13 +291,14 @@ internal val dependenciesLowerPhase = SameTypeNamedPhaseWrapper(
|
|||||||
internal val bitcodePhase = namedIrModulePhase(
|
internal val bitcodePhase = namedIrModulePhase(
|
||||||
name = "Bitcode",
|
name = "Bitcode",
|
||||||
description = "LLVM Bitcode generation",
|
description = "LLVM Bitcode generation",
|
||||||
lower = contextLLVMSetupPhase then
|
lower = buildDFGPhase then
|
||||||
RTTIPhase then
|
|
||||||
generateDebugInfoHeaderPhase then
|
|
||||||
buildDFGPhase then
|
|
||||||
serializeDFGPhase then
|
serializeDFGPhase then
|
||||||
deserializeDFGPhase then
|
deserializeDFGPhase then
|
||||||
devirtualizationPhase then
|
devirtualizationPhase then
|
||||||
|
dcePhase then
|
||||||
|
contextLLVMSetupPhase then
|
||||||
|
RTTIPhase then
|
||||||
|
generateDebugInfoHeaderPhase then
|
||||||
escapeAnalysisPhase then
|
escapeAnalysisPhase then
|
||||||
codegenPhase then
|
codegenPhase then
|
||||||
finalizeDebugInfoPhase then
|
finalizeDebugInfoPhase then
|
||||||
|
|||||||
+1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||||
|
import org.jetbrains.kotlin.ir.util.isReal
|
||||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||||
import org.jetbrains.kotlin.ir.util.overrides
|
import org.jetbrains.kotlin.ir.util.overrides
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
|
|||||||
-5
@@ -17,11 +17,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
|||||||
// This file contains some IR utilities which actually use descriptors.
|
// This file contains some IR utilities which actually use descriptors.
|
||||||
// TODO: port this code to IR.
|
// TODO: port this code to IR.
|
||||||
|
|
||||||
internal val IrDeclaration.isAnonymousObject get() = DescriptorUtils.isAnonymousObject(this.descriptor)
|
|
||||||
internal val IrDeclaration.isLocal get() = DescriptorUtils.isLocal(this.descriptor)
|
|
||||||
|
|
||||||
internal val IrDeclaration.module get() = this.descriptor.module
|
|
||||||
|
|
||||||
internal fun IrFunction.getObjCMethodInfo() = this.descriptor.getObjCMethodInfo()
|
internal fun IrFunction.getObjCMethodInfo() = this.descriptor.getObjCMethodInfo()
|
||||||
internal fun IrFunction.getExternalObjCMethodInfo() = this.descriptor.getExternalObjCMethodInfo()
|
internal fun IrFunction.getExternalObjCMethodInfo() = this.descriptor.getExternalObjCMethodInfo()
|
||||||
internal fun IrFunction.isObjCClassMethod() = this.descriptor.isObjCClassMethod()
|
internal fun IrFunction.isObjCClassMethod() = this.descriptor.isObjCClassMethod()
|
||||||
|
|||||||
+10
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
@@ -53,7 +54,9 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
|
|||||||
val string = symbolTable.referenceClass(builtIns.string)
|
val string = symbolTable.referenceClass(builtIns.string)
|
||||||
val enum = symbolTable.referenceClass(builtIns.enum)
|
val enum = symbolTable.referenceClass(builtIns.enum)
|
||||||
val nativePtr = symbolTable.referenceClass(context.nativePtr)
|
val nativePtr = symbolTable.referenceClass(context.nativePtr)
|
||||||
|
val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed)
|
||||||
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
||||||
|
val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr)
|
||||||
|
|
||||||
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
|
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
|
||||||
|
|
||||||
@@ -351,6 +354,13 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
|
|||||||
|
|
||||||
val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
|
val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
|
||||||
|
|
||||||
|
val invokeSuspendFunction =
|
||||||
|
symbolTable.referenceSimpleFunction(
|
||||||
|
baseContinuationImpl.descriptor.unsubstitutedMemberScope
|
||||||
|
.getContributedFunctions(Name.identifier("invokeSuspend"), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single()
|
||||||
|
)
|
||||||
|
|
||||||
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
||||||
coroutinesIntrinsicsPackage
|
coroutinesIntrinsicsPackage
|
||||||
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND)
|
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND)
|
||||||
|
|||||||
-25
@@ -23,23 +23,6 @@ import org.jetbrains.kotlin.ir.util.*
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
|
||||||
val IrDeclaration.name: Name
|
|
||||||
get() = when (this) {
|
|
||||||
is IrSimpleFunction -> this.name
|
|
||||||
is IrClass -> this.name
|
|
||||||
is IrEnumEntry -> this.name
|
|
||||||
is IrProperty -> this.name
|
|
||||||
is IrLocalDelegatedProperty -> this.name
|
|
||||||
is IrField -> this.name
|
|
||||||
is IrVariable -> this.name
|
|
||||||
is IrConstructor -> SPECIAL_INIT_NAME
|
|
||||||
is IrValueParameter -> this.name
|
|
||||||
else -> error(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val SPECIAL_INIT_NAME = Name.special("<init>")
|
|
||||||
|
|
||||||
val IrField.fqNameForIrSerialization: FqName get() = this.parent.fqNameForIrSerialization.child(this.name)
|
val IrField.fqNameForIrSerialization: FqName get() = this.parent.fqNameForIrSerialization.child(this.name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,14 +49,6 @@ fun IrClass.isNothing() = this.fqNameForIrSerialization == KotlinBuiltIns.FQ_NAM
|
|||||||
|
|
||||||
fun IrClass.getSuperInterfaces() = this.superClasses.map { it.owner }.filter { it.isInterface }
|
fun IrClass.getSuperInterfaces() = this.superClasses.map { it.owner }.filter { it.isInterface }
|
||||||
|
|
||||||
val IrProperty.konanBackingField: IrField?
|
|
||||||
get() {
|
|
||||||
assert(this.isReal)
|
|
||||||
return this.backingField
|
|
||||||
}
|
|
||||||
|
|
||||||
val IrFunction.isReal get() = this.origin != IrDeclarationOrigin.FAKE_OVERRIDE
|
|
||||||
|
|
||||||
// Note: psi2ir doesn't set `origin = FAKE_OVERRIDE` for fields and properties yet.
|
// Note: psi2ir doesn't set `origin = FAKE_OVERRIDE` for fields and properties yet.
|
||||||
val IrProperty.isReal: Boolean get() = this.descriptor.kind.isReal
|
val IrProperty.isReal: Boolean get() = this.descriptor.kind.isReal
|
||||||
val IrField.isReal: Boolean get() = this.descriptor.kind.isReal
|
val IrField.isReal: Boolean get() = this.descriptor.kind.isReal
|
||||||
|
|||||||
+100
-2
@@ -8,7 +8,13 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
|
internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
|
||||||
name = "ContextLLVMSetup",
|
name = "ContextLLVMSetup",
|
||||||
@@ -71,6 +77,98 @@ internal val devirtualizationPhase = makeKonanModuleOpPhase(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val IrFunction.longName: String
|
||||||
|
get() = "${(parent as? IrClass)?.name?.asString() ?: "<root>"}.${(this as? IrSimpleFunction)?.name ?: "<init>"}"
|
||||||
|
|
||||||
|
internal val dcePhase = makeKonanModuleOpPhase(
|
||||||
|
name = "DCEPhase",
|
||||||
|
description = "Dead code elimination",
|
||||||
|
prerequisite = setOf(devirtualizationPhase),
|
||||||
|
op = { context, _ ->
|
||||||
|
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
|
||||||
|
|
||||||
|
val callGraph = CallGraphBuilder(
|
||||||
|
context, context.moduleDFG!!,
|
||||||
|
externalModulesDFG,
|
||||||
|
context.devirtualizationAnalysisResult!!,
|
||||||
|
true
|
||||||
|
).build()
|
||||||
|
|
||||||
|
val referencedFunctions = mutableSetOf<IrFunction>()
|
||||||
|
for (node in callGraph.directEdges.values) {
|
||||||
|
if (!node.symbol.isGlobalInitializer)
|
||||||
|
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
||||||
|
node.callSites.forEach {
|
||||||
|
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
|
||||||
|
referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
// TODO: Generalize somehow, not that graceful.
|
||||||
|
if (declaration.name == OperatorNameConventions.INVOKE
|
||||||
|
&& declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) {
|
||||||
|
referencedFunctions.add(declaration)
|
||||||
|
}
|
||||||
|
super.visitFunction(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitConstructor(declaration: IrConstructor) {
|
||||||
|
// TODO: NativePointed is the only inline class for which the field's type and
|
||||||
|
// the constructor parameter's type are different.
|
||||||
|
// Thus we need to conserve the constructor no matter if it was actually referenced somehow or not.
|
||||||
|
// See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why.
|
||||||
|
if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary)
|
||||||
|
referencedFunctions.add(declaration)
|
||||||
|
super.visitConstructor(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
context.getLayoutBuilder(declaration).associatedObjects.values.forEach {
|
||||||
|
assert (it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" }
|
||||||
|
referencedFunctions.add(it.constructors.single())
|
||||||
|
}
|
||||||
|
super.visitClass(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() {
|
||||||
|
override fun visitFile(declaration: IrFile): IrFile {
|
||||||
|
declaration.declarations.removeAll {
|
||||||
|
(it is IrFunction && !referencedFunctions.contains(it))
|
||||||
|
}
|
||||||
|
return super.visitFile(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
|
if (declaration == context.ir.symbols.nativePointed)
|
||||||
|
return super.visitClass(declaration)
|
||||||
|
declaration.declarations.removeAll {
|
||||||
|
(it is IrFunction && it.isReal && !referencedFunctions.contains(it))
|
||||||
|
}
|
||||||
|
return super.visitClass(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||||
|
if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
|
||||||
|
declaration.getter = null
|
||||||
|
}
|
||||||
|
if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
|
||||||
|
declaration.setter = null
|
||||||
|
}
|
||||||
|
return super.visitProperty(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
context.referencedFunctions = referencedFunctions
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||||
// Disabled by default !!!!
|
// Disabled by default !!!!
|
||||||
name = "EscapeAnalysis",
|
name = "EscapeAnalysis",
|
||||||
@@ -81,7 +179,7 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
|||||||
val callGraph = CallGraphBuilder(
|
val callGraph = CallGraphBuilder(
|
||||||
context, context.moduleDFG!!,
|
context, context.moduleDFG!!,
|
||||||
externalModulesDFG,
|
externalModulesDFG,
|
||||||
context.devirtualizationAnalysisResult,
|
context.devirtualizationAnalysisResult!!,
|
||||||
false
|
false
|
||||||
).build()
|
).build()
|
||||||
EscapeAnalysis.computeLifetimes(
|
EscapeAnalysis.computeLifetimes(
|
||||||
|
|||||||
+1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
|||||||
import org.jetbrains.kotlin.ir.util.file
|
import org.jetbrains.kotlin.ir.util.file
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
|
import org.jetbrains.kotlin.ir.util.isReal
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||||
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
|||||||
+2
-5
@@ -1893,16 +1893,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private val invokeSuspendFunction = context.ir.symbols.baseContinuationImpl.owner.declarations
|
|
||||||
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "invokeSuspend" }
|
|
||||||
|
|
||||||
private fun getContinuation(): LLVMValueRef {
|
private fun getContinuation(): LLVMValueRef {
|
||||||
val caller = functionGenerationContext.irFunction!!
|
val caller = functionGenerationContext.irFunction!!
|
||||||
return if (caller.isSuspend)
|
return if (caller.isSuspend)
|
||||||
codegen.param(caller, caller.allParameters.size) // The last argument.
|
codegen.param(caller, caller.allParameters.size) // The last argument.
|
||||||
else {
|
else {
|
||||||
// Suspend call from non-suspend function - must be [BaseContinuationImpl].
|
// Suspend call from non-suspend function - must be [invokeSuspend].
|
||||||
assert ((caller as IrSimpleFunction).overrides(invokeSuspendFunction),
|
assert ((caller as IrSimpleFunction).overrides(context.ir.symbols.invokeSuspendFunction.owner),
|
||||||
{ "Expected 'BaseContinuationImpl.invokeSuspend' but was '$caller'" })
|
{ "Expected 'BaseContinuationImpl.invokeSuspend' but was '$caller'" })
|
||||||
currentCodeContext.genGetValue(caller.dispatchReceiverParameter!!)
|
currentCodeContext.genGetValue(caller.dispatchReceiverParameter!!)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -13,10 +13,7 @@ import org.jetbrains.kotlin.backend.konan.ir.*
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.types.isNothing
|
import org.jetbrains.kotlin.ir.types.isNothing
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
|
||||||
import org.jetbrains.kotlin.ir.util.file
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -127,7 +124,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
return objectNamer.getName(parent, declaration)
|
return objectNamer.getName(parent, declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
return declaration.name
|
return declaration.nameForIrSerialization
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFqName(declaration: IrDeclaration): FqName {
|
private fun getFqName(declaration: IrDeclaration): FqName {
|
||||||
|
|||||||
+5
-4
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.konan.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
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.ir.isAnonymousObject
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isLocal
|
|
||||||
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
|
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
@@ -249,7 +247,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
// TODO: compile-time resolution limits binary compatibility.
|
// TODO: compile-time resolution limits binary compatibility.
|
||||||
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
|
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
|
||||||
val implementation = it.implementation
|
val implementation = it.implementation
|
||||||
if (implementation == null || implementation.isExternalObjCClassMethod()) {
|
if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) {
|
||||||
NullPointer(int8Type)
|
NullPointer(int8Type)
|
||||||
} else {
|
} else {
|
||||||
implementation.entryPointAddress
|
implementation.entryPointAddress
|
||||||
@@ -269,7 +267,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
// TODO: compile-time resolution limits binary compatibility.
|
// TODO: compile-time resolution limits binary compatibility.
|
||||||
val implementation = it.implementation
|
val implementation = it.implementation
|
||||||
val methodEntryPoint = implementation?.entryPointAddress
|
val methodEntryPoint =
|
||||||
|
if (implementation == null || context.referencedFunctions?.contains(implementation) == false)
|
||||||
|
null
|
||||||
|
else implementation.entryPointAddress
|
||||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||||
}.sortedBy { it.nameSignature.value }
|
}.sortedBy { it.nameSignature.value }
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-58
@@ -47,7 +47,7 @@ internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGra
|
|||||||
internal class CallGraphBuilder(val context: Context,
|
internal class CallGraphBuilder(val context: Context,
|
||||||
val moduleDFG: ModuleDFG,
|
val moduleDFG: ModuleDFG,
|
||||||
val externalModulesDFG: ExternalModulesDFG,
|
val externalModulesDFG: ExternalModulesDFG,
|
||||||
devirtualizationAnalysisResult: Devirtualization.AnalysisResult?,
|
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
||||||
val gotoExternal: Boolean) {
|
val gotoExternal: Boolean) {
|
||||||
|
|
||||||
private val DEBUG = 0
|
private val DEBUG = 0
|
||||||
@@ -56,7 +56,7 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
if (DEBUG > severity) block()
|
if (DEBUG > severity) block()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val devirtualizedCallSites = devirtualizationAnalysisResult?.devirtualizedCallSites
|
private val devirtualizedCallSites = devirtualizationAnalysisResult.devirtualizedCallSites
|
||||||
|
|
||||||
private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
|
private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
|
||||||
if (this is DataFlowIR.FunctionSymbol.External)
|
if (this is DataFlowIR.FunctionSymbol.External)
|
||||||
@@ -88,6 +88,10 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
reversedEdges.put(symbol, list)
|
reversedEdges.put(symbol, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
private val arrayGet = symbols.arrayGet[symbols.array]!!.owner
|
||||||
|
private val arraySet = symbols.arraySet[symbols.array]!!.owner
|
||||||
|
|
||||||
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit) =
|
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit) =
|
||||||
nodes.forEach { node ->
|
nodes.forEach { node ->
|
||||||
when (node) {
|
when (node) {
|
||||||
@@ -95,42 +99,81 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
|
|
||||||
is DataFlowIR.Node.Singleton ->
|
is DataFlowIR.Node.Singleton ->
|
||||||
node.constructor?.let { block(DataFlowIR.Node.Call(it, emptyList(), null)) }
|
node.constructor?.let { block(DataFlowIR.Node.Call(it, emptyList(), null)) }
|
||||||
|
|
||||||
|
is DataFlowIR.Node.ArrayRead ->
|
||||||
|
block(DataFlowIR.Node.Call(
|
||||||
|
callee = moduleDFG.symbolTable.mapFunction(arrayGet),
|
||||||
|
arguments = listOf(node.array, node.index),
|
||||||
|
irCallSite = null)
|
||||||
|
)
|
||||||
|
|
||||||
|
is DataFlowIR.Node.ArrayWrite ->
|
||||||
|
block(DataFlowIR.Node.Call(
|
||||||
|
callee = moduleDFG.symbolTable.mapFunction(arraySet),
|
||||||
|
arguments = listOf(node.array, node.index, node.value),
|
||||||
|
irCallSite = null)
|
||||||
|
)
|
||||||
|
|
||||||
|
is DataFlowIR.Node.FunctionReference ->
|
||||||
|
block(DataFlowIR.Node.Call(
|
||||||
|
callee = node.symbol,
|
||||||
|
arguments = emptyList(),
|
||||||
|
irCallSite = null
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun staticCall(caller: DataFlowIR.FunctionSymbol, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
||||||
|
callGraph.addEdge(caller, CallGraphNode.CallSite(call, false, callee))
|
||||||
|
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||||
|
&& !directEdges.containsKey(callee))
|
||||||
|
dfs(callee)
|
||||||
|
}
|
||||||
|
|
||||||
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
|
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
|
||||||
visitedFunctions += symbol
|
visitedFunctions += symbol
|
||||||
if (gotoExternal) {
|
if (gotoExternal) {
|
||||||
addNode(symbol)
|
addNode(symbol)
|
||||||
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol]
|
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] ?: return
|
||||||
val body = function!!.body
|
val body = function.body
|
||||||
body
|
body.forEachCallSite { call ->
|
||||||
.forEachCallSite { call ->
|
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] }
|
||||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
when {
|
||||||
if (devirtualizedCallSite == null) {
|
call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee.resolved())
|
||||||
val callee = call.callee.resolved()
|
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
devirtualizedCallSite != null -> {
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
devirtualizedCallSite.possibleCallees.forEach {
|
||||||
&& call !is DataFlowIR.Node.VirtualCall
|
staticCall(symbol, call, it.callee.resolved())
|
||||||
&& !directEdges.containsKey(callee))
|
|
||||||
dfs(callee)
|
|
||||||
} else {
|
|
||||||
devirtualizedCallSite.possibleCallees.forEach {
|
|
||||||
val callee = it.callee.resolved()
|
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
|
||||||
&& !directEdges.containsKey(callee))
|
|
||||||
dfs(callee)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body.nodes.filterIsInstance<DataFlowIR.Node.FunctionReference>()
|
|
||||||
.forEach {
|
call.receiverType == DataFlowIR.Type.Virtual -> {
|
||||||
val callee = it.symbol.resolved()
|
// Skip callsite. This can only be for invocations Any's methods on instances of ObjC classes.
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
|
||||||
&& !directEdges.containsKey(callee))
|
|
||||||
dfs(callee)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// Callsite has not been devirtualized - conservatively assume the worst:
|
||||||
|
// any inheritor of the receiver type is possible here.
|
||||||
|
val typeHierarcy = devirtualizationAnalysisResult.typeHierarchy
|
||||||
|
typeHierarcy.inheritorsOf(call.receiverType as DataFlowIR.Type.Declared)
|
||||||
|
.filterNot { it.isAbstract }
|
||||||
|
// TODO: Unconservative way - when we can use it?
|
||||||
|
//.filter { devirtualizationAnalysisResult.instantiatingClasses.contains(it) }
|
||||||
|
.forEach { receiverType ->
|
||||||
|
val actualCallee = when (call) {
|
||||||
|
is DataFlowIR.Node.VtableCall ->
|
||||||
|
receiverType.vtable[call.calleeVtableIndex]
|
||||||
|
|
||||||
|
is DataFlowIR.Node.ItableCall ->
|
||||||
|
receiverType.itable[call.calleeHash]!!
|
||||||
|
|
||||||
|
else -> error("Unreachable")
|
||||||
|
}
|
||||||
|
staticCall(symbol, call, actualCallee.resolved())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var function = moduleDFG.functions[symbol]
|
var function = moduleDFG.functions[symbol]
|
||||||
var local = true
|
var local = true
|
||||||
@@ -141,42 +184,31 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
local = false
|
local = false
|
||||||
}
|
}
|
||||||
val body = function.body
|
val body = function.body
|
||||||
body
|
body.forEachCallSite { call ->
|
||||||
.forEachCallSite { call ->
|
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
||||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
if (devirtualizedCallSite == null) {
|
||||||
if (devirtualizedCallSite == null) {
|
val callee = call.callee.resolved()
|
||||||
val callee = call.callee.resolved()
|
if (moduleDFG.functions.containsKey(callee))
|
||||||
if (moduleDFG.functions.containsKey(callee))
|
addNode(callee)
|
||||||
addNode(callee)
|
if (local)
|
||||||
if (local)
|
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
&& call !is DataFlowIR.Node.VirtualCall
|
||||||
&& call !is DataFlowIR.Node.VirtualCall
|
&& !visitedFunctions.contains(callee))
|
||||||
&& !visitedFunctions.contains(callee))
|
dfs(callee)
|
||||||
dfs(callee)
|
} else {
|
||||||
} else {
|
devirtualizedCallSite.possibleCallees.forEach {
|
||||||
devirtualizedCallSite.possibleCallees.forEach {
|
val callee = it.callee.resolved()
|
||||||
val callee = it.callee.resolved()
|
|
||||||
if (moduleDFG.functions.containsKey(callee))
|
|
||||||
addNode(callee)
|
|
||||||
if (local)
|
|
||||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
|
||||||
&& !visitedFunctions.contains(callee))
|
|
||||||
dfs(callee)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body.nodes.filterIsInstance<DataFlowIR.Node.FunctionReference>()
|
|
||||||
.forEach {
|
|
||||||
val callee = it.symbol.resolved()
|
|
||||||
if (moduleDFG.functions.containsKey(callee))
|
if (moduleDFG.functions.containsKey(callee))
|
||||||
addNode(callee)
|
addNode(callee)
|
||||||
|
if (local)
|
||||||
|
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
||||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||||
&& !visitedFunctions.contains(callee))
|
&& !visitedFunctions.contains(callee))
|
||||||
dfs(callee)
|
dfs(callee)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
-3
@@ -13,11 +13,11 @@ import org.jetbrains.kotlin.backend.common.pop
|
|||||||
import org.jetbrains.kotlin.backend.common.push
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.longName
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
@@ -212,12 +212,16 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunction(declaration: IrFunction) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
declaration.body?.let {
|
val body = declaration.body
|
||||||
|
if (body == null) {
|
||||||
|
// External function or intrinsic.
|
||||||
|
symbolTable.mapFunction(declaration)
|
||||||
|
} else {
|
||||||
DEBUG_OUTPUT(0) {
|
DEBUG_OUTPUT(0) {
|
||||||
println("Analysing function ${declaration.descriptor}")
|
println("Analysing function ${declaration.descriptor}")
|
||||||
println("IR: ${ir2stringWhole(declaration)}")
|
println("IR: ${ir2stringWhole(declaration)}")
|
||||||
}
|
}
|
||||||
analyze(declaration, it)
|
analyze(declaration, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-14
@@ -9,10 +9,9 @@ import org.jetbrains.kotlin.backend.konan.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.isExported
|
import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.functionName
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.symbolName
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
|
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
@@ -27,9 +26,7 @@ import org.jetbrains.kotlin.ir.types.IrType
|
|||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.types.isNothing
|
import org.jetbrains.kotlin.ir.types.isNothing
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.isInterface
|
|
||||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||||
@@ -121,6 +118,7 @@ internal object DataFlowIR {
|
|||||||
val IS_GLOBAL_INITIALIZER = 1
|
val IS_GLOBAL_INITIALIZER = 1
|
||||||
val RETURNS_UNIT = 2
|
val RETURNS_UNIT = 2
|
||||||
val RETURNS_NOTHING = 4
|
val RETURNS_NOTHING = 4
|
||||||
|
val EXPLICITLY_EXPORTED = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionParameter(val type: Type, val boxFunction: FunctionSymbol?, val unboxFunction: FunctionSymbol?)
|
class FunctionParameter(val type: Type, val boxFunction: FunctionSymbol?, val unboxFunction: FunctionSymbol?)
|
||||||
@@ -132,6 +130,7 @@ internal object DataFlowIR {
|
|||||||
val isGlobalInitializer = attributes.and(FunctionAttributes.IS_GLOBAL_INITIALIZER) != 0
|
val isGlobalInitializer = attributes.and(FunctionAttributes.IS_GLOBAL_INITIALIZER) != 0
|
||||||
val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
|
val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
|
||||||
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
|
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
|
||||||
|
val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0
|
||||||
|
|
||||||
var escapes: Int? = null
|
var escapes: Int? = null
|
||||||
var pointsTo: IntArray? = null
|
var pointsTo: IntArray? = null
|
||||||
@@ -572,12 +571,6 @@ internal object DataFlowIR {
|
|||||||
inlinedClass?.let { mapFunction(context.getUnboxFunction(it)) })
|
inlinedClass?.let { mapFunction(context.getUnboxFunction(it)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use from LlvmDeclarations.
|
|
||||||
private fun getFqName(declaration: IrDeclaration): FqName =
|
|
||||||
declaration.parent.fqNameForIrSerialization.child(declaration.name)
|
|
||||||
|
|
||||||
private val IrFunction.internalName get() = getFqName(this).asString() + "#internal"
|
|
||||||
|
|
||||||
fun mapFunction(declaration: IrDeclaration): FunctionSymbol = when (declaration) {
|
fun mapFunction(declaration: IrDeclaration): FunctionSymbol = when (declaration) {
|
||||||
is IrFunction -> mapFunction(declaration)
|
is IrFunction -> mapFunction(declaration)
|
||||||
is IrField -> mapPropertyInitializer(declaration)
|
is IrField -> mapPropertyInitializer(declaration)
|
||||||
@@ -587,7 +580,13 @@ internal object DataFlowIR {
|
|||||||
private fun mapFunction(function: IrFunction): FunctionSymbol = function.target.let {
|
private fun mapFunction(function: IrFunction): FunctionSymbol = function.target.let {
|
||||||
functionMap[it]?.let { return it }
|
functionMap[it]?.let { return it }
|
||||||
|
|
||||||
val name = if (it.isExported()) it.symbolName else it.internalName
|
val parent = it.parent
|
||||||
|
|
||||||
|
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
|
||||||
|
if (it.isRoot) "" else "$it."
|
||||||
|
}
|
||||||
|
val name = "kfun:$containingDeclarationPart${it.functionName}"
|
||||||
|
|
||||||
val returnsUnit = it is IrConstructor || (!it.isSuspend && it.returnType.isUnit())
|
val returnsUnit = it is IrConstructor || (!it.isSuspend && it.returnType.isUnit())
|
||||||
val returnsNothing = !it.isSuspend && it.returnType.isNothing()
|
val returnsNothing = !it.isSuspend && it.returnType.isNothing()
|
||||||
var attributes = 0
|
var attributes = 0
|
||||||
@@ -595,6 +594,11 @@ internal object DataFlowIR {
|
|||||||
attributes = attributes or FunctionAttributes.RETURNS_UNIT
|
attributes = attributes or FunctionAttributes.RETURNS_UNIT
|
||||||
if (returnsNothing)
|
if (returnsNothing)
|
||||||
attributes = attributes or FunctionAttributes.RETURNS_NOTHING
|
attributes = attributes or FunctionAttributes.RETURNS_NOTHING
|
||||||
|
if (it.hasAnnotation(RuntimeNames.exportForCppRuntime)
|
||||||
|
|| it.getExternalObjCMethodInfo() != null // TODO-DCE-OBJC-INIT
|
||||||
|
|| it.hasAnnotation(RuntimeNames.objCMethodImp)) {
|
||||||
|
attributes = attributes or FunctionAttributes.EXPLICITLY_EXPORTED
|
||||||
|
}
|
||||||
val symbol = when {
|
val symbol = when {
|
||||||
it.isExternal || (it.symbol in context.irBuiltIns.irBuiltInsSymbols) -> {
|
it.isExternal || (it.symbol in context.irBuiltIns.irBuiltInsSymbols) -> {
|
||||||
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES)
|
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES)
|
||||||
|
|||||||
+49
-35
@@ -22,9 +22,11 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
|||||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.irCall
|
import org.jetbrains.kotlin.ir.util.irCall
|
||||||
@@ -67,19 +69,22 @@ internal object Devirtualization {
|
|||||||
listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved())
|
listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved())
|
||||||
else
|
else
|
||||||
// In a library every public function and every function accessible via virtual call belongs to the rootset.
|
// In a library every public function and every function accessible via virtual call belongs to the rootset.
|
||||||
moduleDFG.functions.keys.filterIsInstance<DataFlowIR.FunctionSymbol.Public>() +
|
moduleDFG.symbolTable.functionMap.values.filterIsInstance<DataFlowIR.FunctionSymbol.Public>() +
|
||||||
moduleDFG.symbolTable.classMap.values
|
moduleDFG.symbolTable.classMap.values
|
||||||
.filterIsInstance<DataFlowIR.Type.Declared>()
|
.filterIsInstance<DataFlowIR.Type.Declared>()
|
||||||
.flatMap { it.vtable + it.itable.values }
|
.flatMap { it.vtable + it.itable.values }
|
||||||
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
||||||
.filter { moduleDFG.functions.containsKey(it) }
|
.filter { moduleDFG.functions.containsKey(it) }
|
||||||
// TODO: Are globals inititalizers always called whether they are actually reachable from roots or not?
|
// TODO: Are globals initializers always called whether they are actually reachable from roots or not?
|
||||||
val globalInitializers =
|
val globalInitializers =
|
||||||
moduleDFG.functions.keys.filter { it.isGlobalInitializer } +
|
moduleDFG.symbolTable.functionMap.values.filter { it.isGlobalInitializer } +
|
||||||
externalModulesDFG.functionDFGs.keys.filter { it.isGlobalInitializer }
|
externalModulesDFG.functionDFGs.keys.filter { it.isGlobalInitializer }
|
||||||
|
|
||||||
return (exportedFunctions + globalInitializers).distinct()
|
val explicitlyExportedFunctions =
|
||||||
|
moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } +
|
||||||
|
externalModulesDFG.functionDFGs.keys.filter { it.explicitlyExported }
|
||||||
|
|
||||||
|
return (exportedFunctions + globalInitializers + explicitlyExportedFunctions).distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BitSet.format(allTypes: List<DataFlowIR.Type.Declared>): String {
|
fun BitSet.format(allTypes: List<DataFlowIR.Type.Declared>): String {
|
||||||
@@ -88,9 +93,9 @@ internal object Devirtualization {
|
|||||||
|
|
||||||
private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual].
|
private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual].
|
||||||
|
|
||||||
private class DevirtualizationAnalysis(val context: Context,
|
internal class DevirtualizationAnalysis(val context: Context,
|
||||||
val moduleDFG: ModuleDFG,
|
val moduleDFG: ModuleDFG,
|
||||||
val externalModulesDFG: ExternalModulesDFG) {
|
val externalModulesDFG: ExternalModulesDFG) {
|
||||||
|
|
||||||
private val entryPoint = context.ir.symbols.entryPoint?.owner
|
private val entryPoint = context.ir.symbols.entryPoint?.owner
|
||||||
|
|
||||||
@@ -188,7 +193,7 @@ internal object Devirtualization {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class TypeHierarchy(types: List<DataFlowIR.Type.Declared>) {
|
inner class TypeHierarchy(types: List<DataFlowIR.Type.Declared>) {
|
||||||
private val typesSubTypes = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Type.Declared>>()
|
private val typesSubTypes = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Type.Declared>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -229,6 +234,7 @@ internal object Devirtualization {
|
|||||||
|
|
||||||
fun search(): Set<DataFlowIR.Type.Declared> {
|
fun search(): Set<DataFlowIR.Type.Declared> {
|
||||||
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
|
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
|
||||||
|
|
||||||
// Add all final parameters of the roots.
|
// Add all final parameters of the roots.
|
||||||
rootSet.forEach {
|
rootSet.forEach {
|
||||||
it.parameters
|
it.parameters
|
||||||
@@ -238,8 +244,7 @@ internal object Devirtualization {
|
|||||||
}
|
}
|
||||||
if (entryPoint == null) {
|
if (entryPoint == null) {
|
||||||
// For library assume all public non-abstract classes could be instantiated.
|
// For library assume all public non-abstract classes could be instantiated.
|
||||||
moduleDFG.symbolTable.classMap.values
|
symbolTable.classMap.values
|
||||||
.asSequence()
|
|
||||||
.filterIsInstance<DataFlowIR.Type.Public>()
|
.filterIsInstance<DataFlowIR.Type.Public>()
|
||||||
.filter { !it.isAbstract }
|
.filter { !it.isAbstract }
|
||||||
.forEach { addInstantiatingClass(it) }
|
.forEach { addInstantiatingClass(it) }
|
||||||
@@ -333,7 +338,7 @@ internal object Devirtualization {
|
|||||||
|
|
||||||
val function = (moduleDFG.functions[resolvedFunctionSymbol]
|
val function = (moduleDFG.functions[resolvedFunctionSymbol]
|
||||||
?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol])
|
?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol])
|
||||||
?: error("Unknown function $resolvedFunctionSymbol")
|
?: return
|
||||||
|
|
||||||
DEBUG_OUTPUT(1) { function.debugOutput() }
|
DEBUG_OUTPUT(1) { function.debugOutput() }
|
||||||
|
|
||||||
@@ -452,7 +457,7 @@ internal object Devirtualization {
|
|||||||
} while (cur != node)
|
} while (cur != node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyze(): Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite> {
|
fun analyze(): AnalysisResult {
|
||||||
val functions = moduleDFG.functions + externalModulesDFG.functionDFGs
|
val functions = moduleDFG.functions + externalModulesDFG.functionDFGs
|
||||||
val typeHierarchy = TypeHierarchy(symbolTable.classMap.values.filterIsInstance<DataFlowIR.Type.Declared>() +
|
val typeHierarchy = TypeHierarchy(symbolTable.classMap.values.filterIsInstance<DataFlowIR.Type.Declared>() +
|
||||||
externalModulesDFG.allTypes)
|
externalModulesDFG.allTypes)
|
||||||
@@ -665,7 +670,7 @@ internal object Devirtualization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.asSequence().associateBy({ it.key }, { it.value.first })
|
return AnalysisResult(result.asSequence().associateBy({ it.key }, { it.value.first }), typeHierarchy, instantiatingClasses.keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ConstraintGraphBuilder(val functionNodesMap: MutableMap<DataFlowIR.Node, Node>,
|
private inner class ConstraintGraphBuilder(val functionNodesMap: MutableMap<DataFlowIR.Node, Node>,
|
||||||
@@ -719,10 +724,10 @@ internal object Devirtualization {
|
|||||||
fieldNode(constraintGraph.arrayItemField)
|
fieldNode(constraintGraph.arrayItemField)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
rootSet.forEach { createFunctionConstraintGraph(it, true)!! }
|
rootSet.forEach { createFunctionConstraintGraph(it, true) }
|
||||||
while (stack.isNotEmpty()) {
|
while (stack.isNotEmpty()) {
|
||||||
val symbol = stack.pop()
|
val symbol = stack.pop()
|
||||||
val function = functions[symbol] ?: error("Unknown function: $symbol")
|
val function = functions[symbol] ?: continue
|
||||||
val body = function.body
|
val body = function.body
|
||||||
val functionConstraintGraph = constraintGraph.functions[symbol]!!
|
val functionConstraintGraph = constraintGraph.functions[symbol]!!
|
||||||
|
|
||||||
@@ -757,7 +762,7 @@ internal object Devirtualization {
|
|||||||
symbol.parameters.forEachIndexed { index, type ->
|
symbol.parameters.forEachIndexed { index, type ->
|
||||||
val resolvedType = type.type.resolved()
|
val resolvedType = type.type.resolved()
|
||||||
val node = if (!resolvedType.isFinal)
|
val node = if (!resolvedType.isFinal)
|
||||||
constraintGraph.virtualNode
|
constraintGraph.virtualNode // TODO: May be do this only for a library?
|
||||||
else
|
else
|
||||||
concreteClass(resolvedType)
|
concreteClass(resolvedType)
|
||||||
node.addEdge(parameters[index])
|
node.addEdge(parameters[index])
|
||||||
@@ -783,14 +788,18 @@ internal object Devirtualization {
|
|||||||
return Node.CastEdge(node, suitableTypes)
|
return Node.CastEdge(node, suitableTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun doCast(function: Function, node: Node, type: DataFlowIR.Type.Declared): Node {
|
||||||
|
val castNode = ordinaryNode { "Cast\$${function.symbol}" }
|
||||||
|
val castEdge = createCastEdge(castNode, type)
|
||||||
|
node.addCastEdge(castEdge)
|
||||||
|
return castNode
|
||||||
|
}
|
||||||
|
|
||||||
private fun edgeToConstraintNode(function: Function,
|
private fun edgeToConstraintNode(function: Function,
|
||||||
edge: DataFlowIR.Edge): Node {
|
edge: DataFlowIR.Edge): Node {
|
||||||
val result = dfgNodeToConstraintNode(function, edge.node)
|
val result = dfgNodeToConstraintNode(function, edge.node)
|
||||||
val castToType = edge.castToType?.resolved() ?: return result
|
val castToType = edge.castToType?.resolved() ?: return result
|
||||||
val castNode = ordinaryNode { "Cast\$${function.symbol}" }
|
return doCast(function, result, castToType)
|
||||||
val castEdge = createCastEdge(castNode, castToType)
|
|
||||||
result.addCastEdge(castEdge)
|
|
||||||
return castNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -809,7 +818,7 @@ internal object Devirtualization {
|
|||||||
else -> error("Unexpected argument: $argument")
|
else -> error("Unexpected argument: $argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doCall(callee: Function, arguments: List<Any>): Node {
|
fun doCall(callee: Function, arguments: List<Any>, returnType: DataFlowIR.Type.Declared): Node {
|
||||||
assert(callee.parameters.size == arguments.size) {
|
assert(callee.parameters.size == arguments.size) {
|
||||||
"Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol}" +
|
"Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol}" +
|
||||||
" provided ${arguments.size}"
|
" provided ${arguments.size}"
|
||||||
@@ -818,7 +827,7 @@ internal object Devirtualization {
|
|||||||
val argument = argumentToConstraintNode(arguments[index])
|
val argument = argumentToConstraintNode(arguments[index])
|
||||||
argument.addEdge(parameter)
|
argument.addEdge(parameter)
|
||||||
}
|
}
|
||||||
return callee.returns
|
return doCast(function, callee.returns, returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doCall(callee: DataFlowIR.FunctionSymbol,
|
fun doCall(callee: DataFlowIR.FunctionSymbol,
|
||||||
@@ -832,20 +841,25 @@ internal object Devirtualization {
|
|||||||
val fictitiousReturnNode = ordinaryNode { "External$resolvedCallee" }
|
val fictitiousReturnNode = ordinaryNode { "External$resolvedCallee" }
|
||||||
if (returnType.isFinal)
|
if (returnType.isFinal)
|
||||||
concreteClass(returnType).addEdge(fictitiousReturnNode)
|
concreteClass(returnType).addEdge(fictitiousReturnNode)
|
||||||
else
|
else {
|
||||||
constraintGraph.virtualNode.addEdge(fictitiousReturnNode)
|
constraintGraph.virtualNode.addEdge(fictitiousReturnNode)
|
||||||
|
// TODO: Unconservative way - when we can use it?
|
||||||
|
// typeHierarchy.inheritorsOf(returnType)
|
||||||
|
// .filterNot { it.isAbstract }
|
||||||
|
// .filter { instantiatingClasses.containsKey(it) }
|
||||||
|
// .forEach { concreteClass(it).addEdge(fictitiousReturnNode) }
|
||||||
|
}
|
||||||
fictitiousReturnNode
|
fictitiousReturnNode
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
calleeConstraintGraph.throws.addEdge(function.throws)
|
calleeConstraintGraph.throws.addEdge(function.throws)
|
||||||
if (receiverType == null)
|
if (receiverType == null)
|
||||||
doCall(calleeConstraintGraph, arguments)
|
doCall(calleeConstraintGraph, arguments, returnType)
|
||||||
else {
|
else {
|
||||||
val receiverNode = argumentToConstraintNode(arguments[0])
|
val receiverNode = argumentToConstraintNode(arguments[0])
|
||||||
val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" }
|
doCall(calleeConstraintGraph,
|
||||||
val castedEdge = createCastEdge(castedReceiver, receiverType)
|
listOf(doCast(function, receiverNode, receiverType)) + arguments.drop(1),
|
||||||
receiverNode.addCastEdge(castedEdge)
|
returnType)
|
||||||
doCall(calleeConstraintGraph, listOf(castedReceiver) + arguments.drop(1))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -923,9 +937,7 @@ internal object Devirtualization {
|
|||||||
val receiverNode = edgeToConstraintNode(node.arguments[0])
|
val receiverNode = edgeToConstraintNode(node.arguments[0])
|
||||||
if (receiverType == DataFlowIR.Type.Virtual)
|
if (receiverType == DataFlowIR.Type.Virtual)
|
||||||
constraintGraph.virtualNode.addEdge(receiverNode)
|
constraintGraph.virtualNode.addEdge(receiverNode)
|
||||||
val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" }
|
val castedReceiver = doCast(function, receiverNode, receiverType)
|
||||||
val castedEdge = createCastEdge(castedReceiver, receiverType)
|
|
||||||
receiverNode.addCastEdge(castedEdge)
|
|
||||||
val arguments = listOf(castedReceiver) + node.arguments.drop(1)
|
val arguments = listOf(castedReceiver) + node.arguments.drop(1)
|
||||||
|
|
||||||
val returnsNode = ordinaryNode { "VirtualCallReturns\$${function.symbol}" }
|
val returnsNode = ordinaryNode { "VirtualCallReturns\$${function.symbol}" }
|
||||||
@@ -935,7 +947,7 @@ internal object Devirtualization {
|
|||||||
// Add cast to [Virtual] edge from receiver to returns, if return type is not final.
|
// Add cast to [Virtual] edge from receiver to returns, if return type is not final.
|
||||||
// With this we're reflecting the fact that unknown function can return anything.
|
// With this we're reflecting the fact that unknown function can return anything.
|
||||||
val virtualTypeFilter = BitSet().apply { set(VIRTUAL_TYPE_ID) }
|
val virtualTypeFilter = BitSet().apply { set(VIRTUAL_TYPE_ID) }
|
||||||
if (!returnType.isFinal) {
|
if (!returnType.isFinal && entryPoint == null) {
|
||||||
receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter))
|
receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter))
|
||||||
}
|
}
|
||||||
// And throw anything.
|
// And throw anything.
|
||||||
@@ -1007,19 +1019,21 @@ internal object Devirtualization {
|
|||||||
|
|
||||||
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
|
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
|
||||||
|
|
||||||
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>)
|
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>,
|
||||||
|
val typeHierarchy: DevirtualizationAnalysis.TypeHierarchy,
|
||||||
|
val instantiatingClasses: Set<DataFlowIR.Type.Declared>)
|
||||||
|
|
||||||
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
|
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
|
||||||
: AnalysisResult {
|
: AnalysisResult {
|
||||||
val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze()
|
val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze()
|
||||||
val devirtualizedCallSites =
|
val devirtualizedCallSites =
|
||||||
devirtualizationAnalysisResult
|
devirtualizationAnalysisResult.devirtualizedCallSites
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it.key.irCallSite != null }
|
.filter { it.key.irCallSite != null }
|
||||||
.associate { it.key.irCallSite!! to it.value }
|
.associate { it.key.irCallSite!! to it.value }
|
||||||
devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites)
|
devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites)
|
||||||
removeRedundantCoercions(irModule, context)
|
removeRedundantCoercions(irModule, context)
|
||||||
return AnalysisResult(devirtualizationAnalysisResult)
|
return devirtualizationAnalysisResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ public final class String : Comparable<String>, CharSequence {
|
|||||||
external public override fun equals(other: Any?): Boolean
|
external public override fun equals(other: Any?): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String =
|
public inline operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String =
|
||||||
(this?.toString() ?: "null").plus(other?.toString() ?: "null")
|
(this?.toString() ?: "null").plus(other?.toString() ?: "null")
|
||||||
|
|
||||||
|
|
||||||
public fun Any?.toString() = this?.toString() ?: "null"
|
public inline fun Any?.toString() = this?.toString() ?: "null"
|
||||||
@@ -286,6 +286,7 @@ private fun Kotlin_ObjCExport_isUnchecked(exception: Throwable): Boolean =
|
|||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
@SymbolName("Kotlin_ObjCExport_trapOnUndeclaredException")
|
@SymbolName("Kotlin_ObjCExport_trapOnUndeclaredException")
|
||||||
|
@ExportForCppRuntime
|
||||||
internal external fun trapOnUndeclaredException(exception: Throwable)
|
internal external fun trapOnUndeclaredException(exception: Throwable)
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ fun ThrowTypeCastException(): Nothing {
|
|||||||
throw TypeCastException()
|
throw TypeCastException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime
|
||||||
fun ThrowInvalidReceiverTypeException(klass: KClass<*>): Nothing {
|
fun ThrowInvalidReceiverTypeException(klass: KClass<*>): Nothing {
|
||||||
throw RuntimeException("Unexpected receiver type: " + (klass.qualifiedName ?: "noname"))
|
throw RuntimeException("Unexpected receiver type: " + (klass.qualifiedName ?: "noname"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime
|
||||||
internal fun ThrowArithmeticException() : Nothing {
|
internal fun ThrowArithmeticException() : Nothing {
|
||||||
throw ArithmeticException()
|
throw ArithmeticException()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user