diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt index 6d1385f1ada..edc97f629b1 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt @@ -19,6 +19,7 @@ package kotlinx.cinterop import kotlin.native.* import kotlin.native.internal.ExportTypeInfo +import kotlin.native.internal.ExportForCppRuntime import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType @@ -63,6 +64,7 @@ internal fun Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (t @SymbolName("Kotlin_Interop_refFromObjC") external fun interpretObjCPointerOrNull(objcPtr: NativePtr): T? +@ExportForCppRuntime inline fun interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull(objcPtr)!! @SymbolName("Kotlin_Interop_refToObjC") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 8f398c386ba..8504467e4a3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -215,6 +215,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { private val packageScope by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.internalPackageName).memberScope } 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 immutableBlobOf by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.packageName).memberScope.getContributedFunctions("immutableBlobOf").single() @@ -459,6 +460,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { lateinit var codegenVisitor: CodeGeneratorVisitor var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null + var referencedFunctions: Set? = null + val isNativeLibrary: Boolean by lazy { val kind = config.configuration.get(KonanConfigKeys.PRODUCE) kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt index 1a1b563c4ec..a0b77c4b06d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.konan.ir.containsNull import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.descriptors.ClassDescriptor 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.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.IrType @@ -275,6 +276,6 @@ private object IrTypeInlineClassesSupport : InlineClassesSupport().single { it.backingField != null }.backingField!!.type } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt index a67d0970386..a7cde60ab1d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt @@ -10,13 +10,14 @@ import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name internal const val NATIVE_PTR_NAME = "NativePtr" +internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr" object KonanFqNames { val packageName = FqName("kotlin.native") val internalPackageName = FqName("kotlin.native.internal") 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") } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index 6c985b8fb6c..f9969a7c247 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization 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.Name import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition @@ -223,6 +224,7 @@ fun ConstructorDescriptor.objCConstructorIsDesignated(): Boolean { val IrConstructor.isObjCConstructor get() = this.descriptor.annotations.hasAnnotation(objCConstructorFqName) +// TODO-DCE-OBJC-INIT: Selector should be preserved by DCE. fun IrConstructor.getObjCInitMethod(): IrSimpleFunction? { return this.descriptor.annotations.findAnnotation(objCConstructorFqName)?.let { val initSelector = it.getStringValue("initSelector") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeNames.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeNames.kt index f33039bd2d9..82622dffc8b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeNames.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeNames.kt @@ -8,6 +8,7 @@ object RuntimeNames { val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler") val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo") val cCall = FqName("kotlinx.cinterop.internal.CCall") + val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp") val independent = FqName("kotlin.native.internal.Independent") val filterExceptions = FqName("kotlin.native.internal.FilterExceptions") val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal")) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index fc401f3d3ad..00132761499 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -291,13 +291,14 @@ internal val dependenciesLowerPhase = SameTypeNamedPhaseWrapper( internal val bitcodePhase = namedIrModulePhase( name = "Bitcode", description = "LLVM Bitcode generation", - lower = contextLLVMSetupPhase then - RTTIPhase then - generateDebugInfoHeaderPhase then - buildDFGPhase then + lower = buildDFGPhase then serializeDFGPhase then deserializeDFGPhase then devirtualizationPhase then + dcePhase then + contextLLVMSetupPhase then + RTTIPhase then + generateDebugInfoHeaderPhase then escapeAnalysisPhase then codegenPhase then finalizeDebugInfoPhase then diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 7e96c5dd949..79f58cd1877 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.isUnit 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.overrides import org.jetbrains.kotlin.types.SimpleType diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt index 81aafe45c19..d28621347f8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt @@ -17,11 +17,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module // This file contains some IR utilities which actually use descriptors. // 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.getExternalObjCMethodInfo() = this.descriptor.getExternalObjCMethodInfo() internal fun IrFunction.isObjCClassMethod() = this.descriptor.isObjCClassMethod() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 3f25203352f..127ef2071ff 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation 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.IrFunctionSymbol 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 enum = symbolTable.referenceClass(builtIns.enum) val nativePtr = symbolTable.referenceClass(context.nativePtr) + val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed) val nativePtrType = nativePtr.typeWith(arguments = emptyList()) + val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr) 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 invokeSuspendFunction = + symbolTable.referenceSimpleFunction( + baseContinuationImpl.descriptor.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("invokeSuspend"), NoLookupLocation.FROM_BACKEND) + .single() + ) + override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction( coroutinesIntrinsicsPackage .getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt index b01b392b6e4..5780bc43f9d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/NewIrUtils.kt @@ -23,23 +23,6 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName 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("") - 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 } -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. val IrProperty.isReal: Boolean get() = this.descriptor.kind.isReal val IrField.isReal: Boolean get() = this.descriptor.kind.isReal diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 7f4e4ed601d..fa4e850acb1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -8,7 +8,13 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.* import org.jetbrains.kotlin.backend.konan.* 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( name = "ContextLLVMSetup", @@ -71,6 +77,98 @@ internal val devirtualizationPhase = makeKonanModuleOpPhase( } ) +internal val IrFunction.longName: String + get() = "${(parent as? IrClass)?.name?.asString() ?: ""}.${(this as? IrSimpleFunction)?.name ?: ""}" + +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() + 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( // Disabled by default !!!! name = "EscapeAnalysis", @@ -81,7 +179,7 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase( val callGraph = CallGraphBuilder( context, context.moduleDFG!!, externalModulesDFG, - context.devirtualizationAnalysisResult, + context.devirtualizationAnalysisResult!!, false ).build() EscapeAnalysis.computeLifetimes( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index af4d43292b6..22b64aaa65c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.file import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization 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.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.konan.target.KonanTarget diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index a965432f9d6..265225693de 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1893,16 +1893,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map().single { it.name.asString() == "invokeSuspend" } - private fun getContinuation(): LLVMValueRef { val caller = functionGenerationContext.irFunction!! return if (caller.isSuspend) codegen.param(caller, caller.allParameters.size) // The last argument. else { - // Suspend call from non-suspend function - must be [BaseContinuationImpl]. - assert ((caller as IrSimpleFunction).overrides(invokeSuspendFunction), + // Suspend call from non-suspend function - must be [invokeSuspend]. + assert ((caller as IrSimpleFunction).overrides(context.ir.symbols.invokeSuspendFunction.owner), { "Expected 'BaseContinuationImpl.invokeSuspend' but was '$caller'" }) currentCodeContext.genGetValue(caller.dispatchReceiverParameter!!) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 06b3cb06b06..581a41040b0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -13,10 +13,7 @@ import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.isNothing -import org.jetbrains.kotlin.ir.util.defaultType -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.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.FqName @@ -127,7 +124,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : return objectNamer.getName(parent, declaration) } - return declaration.name + return declaration.nameForIrSerialization } private fun getFqName(declaration: IrDeclaration): FqName { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index d4251d95dda..7ae2c8aacf9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.* 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.ir.declarations.* 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. val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map { val implementation = it.implementation - if (implementation == null || implementation.isExternalObjCClassMethod()) { + if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) { NullPointer(int8Type) } else { implementation.entryPointAddress @@ -269,7 +267,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { // TODO: compile-time resolution limits binary compatibility. 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) }.sortedBy { it.nameSignature.value } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt index 071f1f6c19c..a885686e0ef 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt @@ -47,7 +47,7 @@ internal class CallGraph(val directEdges: Map severity) block() } - private val devirtualizedCallSites = devirtualizationAnalysisResult?.devirtualizedCallSites + private val devirtualizedCallSites = devirtualizationAnalysisResult.devirtualizedCallSites private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { if (this is DataFlowIR.FunctionSymbol.External) @@ -88,6 +88,10 @@ internal class CallGraphBuilder(val context: Context, 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) = nodes.forEach { node -> when (node) { @@ -95,42 +99,81 @@ internal class CallGraphBuilder(val context: Context, is DataFlowIR.Node.Singleton -> 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) { visitedFunctions += symbol if (gotoExternal) { addNode(symbol) - val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] - val body = function!!.body - body - .forEachCallSite { call -> - val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) } - if (devirtualizedCallSite == null) { - val callee = call.callee.resolved() - callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee)) - if (callee is DataFlowIR.FunctionSymbol.Declared - && call !is DataFlowIR.Node.VirtualCall - && !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) - } + val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] ?: return + val body = function.body + body.forEachCallSite { call -> + val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] } + when { + call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee.resolved()) + + devirtualizedCallSite != null -> { + devirtualizedCallSite.possibleCallees.forEach { + staticCall(symbol, call, it.callee.resolved()) } } - body.nodes.filterIsInstance() - .forEach { - val callee = it.symbol.resolved() - if (callee is DataFlowIR.FunctionSymbol.Declared - && !directEdges.containsKey(callee)) - dfs(callee) + + call.receiverType == DataFlowIR.Type.Virtual -> { + // Skip callsite. This can only be for invocations Any's methods on instances of ObjC classes. } + + 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 { var function = moduleDFG.functions[symbol] var local = true @@ -141,42 +184,31 @@ internal class CallGraphBuilder(val context: Context, local = false } val body = function.body - body - .forEachCallSite { call -> - val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) } - if (devirtualizedCallSite == null) { - val callee = call.callee.resolved() - if (moduleDFG.functions.containsKey(callee)) - addNode(callee) - if (local) - callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee)) - if (callee is DataFlowIR.FunctionSymbol.Declared - && call !is DataFlowIR.Node.VirtualCall - && !visitedFunctions.contains(callee)) - dfs(callee) - } else { - devirtualizedCallSite.possibleCallees.forEach { - 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() - .forEach { - val callee = it.symbol.resolved() + body.forEachCallSite { call -> + val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) } + if (devirtualizedCallSite == null) { + val callee = call.callee.resolved() + if (moduleDFG.functions.containsKey(callee)) + addNode(callee) + if (local) + callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee)) + if (callee is DataFlowIR.FunctionSymbol.Declared + && call !is DataFlowIR.Node.VirtualCall + && !visitedFunctions.contains(callee)) + dfs(callee) + } else { + devirtualizedCallSite.possibleCallees.forEach { + 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) } - + } + } } } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index d2b8fbae644..160cb42926a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -13,11 +13,11 @@ import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.* 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.ir.* import org.jetbrains.kotlin.backend.konan.llvm.functionName 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.declarations.* import org.jetbrains.kotlin.ir.expressions.* @@ -212,12 +212,16 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag } 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) { println("Analysing function ${declaration.descriptor}") println("IR: ${ir2stringWhole(declaration)}") } - analyze(declaration, it) + analyze(declaration, body) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index d40c38fc9e4..7f1bf209d6f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -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.target import org.jetbrains.kotlin.backend.konan.ir.* -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.backend.konan.llvm.* +import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.functionName +import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.symbolName import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget import org.jetbrains.kotlin.descriptors.Modality 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.isNothing import org.jetbrains.kotlin.ir.types.isUnit -import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.isSuspend +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature @@ -121,6 +118,7 @@ internal object DataFlowIR { val IS_GLOBAL_INITIALIZER = 1 val RETURNS_UNIT = 2 val RETURNS_NOTHING = 4 + val EXPLICITLY_EXPORTED = 8 } 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 returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0 val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0 + val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0 var escapes: Int? = null var pointsTo: IntArray? = null @@ -572,12 +571,6 @@ internal object DataFlowIR { 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) { is IrFunction -> mapFunction(declaration) is IrField -> mapPropertyInitializer(declaration) @@ -587,7 +580,13 @@ internal object DataFlowIR { private fun mapFunction(function: IrFunction): FunctionSymbol = function.target.let { 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 returnsNothing = !it.isSuspend && it.returnType.isNothing() var attributes = 0 @@ -595,6 +594,11 @@ internal object DataFlowIR { attributes = attributes or FunctionAttributes.RETURNS_UNIT if (returnsNothing) 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 { it.isExternal || (it.symbol in context.irBuiltIns.irBuiltInsSymbols) -> { val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt index c362808ee3f..f958db0930c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/Devirtualization.kt @@ -22,9 +22,11 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl import org.jetbrains.kotlin.ir.expressions.* 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.impl.IrReturnableBlockSymbolImpl 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.toKotlinType import org.jetbrains.kotlin.ir.util.irCall @@ -67,19 +69,22 @@ internal object Devirtualization { listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved()) else // In a library every public function and every function accessible via virtual call belongs to the rootset. - moduleDFG.functions.keys.filterIsInstance() + + moduleDFG.symbolTable.functionMap.values.filterIsInstance() + moduleDFG.symbolTable.classMap.values .filterIsInstance() .flatMap { it.vtable + it.itable.values } .filterIsInstance() .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 = - moduleDFG.functions.keys.filter { it.isGlobalInitializer } + + moduleDFG.symbolTable.functionMap.values.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): String { @@ -88,9 +93,9 @@ internal object Devirtualization { private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual]. - private class DevirtualizationAnalysis(val context: Context, - val moduleDFG: ModuleDFG, - val externalModulesDFG: ExternalModulesDFG) { + internal class DevirtualizationAnalysis(val context: Context, + val moduleDFG: ModuleDFG, + val externalModulesDFG: ExternalModulesDFG) { private val entryPoint = context.ir.symbols.entryPoint?.owner @@ -188,7 +193,7 @@ internal object Devirtualization { return this } - private inner class TypeHierarchy(types: List) { + inner class TypeHierarchy(types: List) { private val typesSubTypes = mutableMapOf>() init { @@ -229,6 +234,7 @@ internal object Devirtualization { fun search(): Set { // Rapid Type Analysis: find all instantiations and conservatively estimate call graph. + // Add all final parameters of the roots. rootSet.forEach { it.parameters @@ -238,8 +244,7 @@ internal object Devirtualization { } if (entryPoint == null) { // For library assume all public non-abstract classes could be instantiated. - moduleDFG.symbolTable.classMap.values - .asSequence() + symbolTable.classMap.values .filterIsInstance() .filter { !it.isAbstract } .forEach { addInstantiatingClass(it) } @@ -333,7 +338,7 @@ internal object Devirtualization { val function = (moduleDFG.functions[resolvedFunctionSymbol] ?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol]) - ?: error("Unknown function $resolvedFunctionSymbol") + ?: return DEBUG_OUTPUT(1) { function.debugOutput() } @@ -452,7 +457,7 @@ internal object Devirtualization { } while (cur != node) } - fun analyze(): Map { + fun analyze(): AnalysisResult { val functions = moduleDFG.functions + externalModulesDFG.functionDFGs val typeHierarchy = TypeHierarchy(symbolTable.classMap.values.filterIsInstance() + 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, @@ -719,10 +724,10 @@ internal object Devirtualization { fieldNode(constraintGraph.arrayItemField) ) } - rootSet.forEach { createFunctionConstraintGraph(it, true)!! } + rootSet.forEach { createFunctionConstraintGraph(it, true) } while (stack.isNotEmpty()) { val symbol = stack.pop() - val function = functions[symbol] ?: error("Unknown function: $symbol") + val function = functions[symbol] ?: continue val body = function.body val functionConstraintGraph = constraintGraph.functions[symbol]!! @@ -757,7 +762,7 @@ internal object Devirtualization { symbol.parameters.forEachIndexed { index, type -> val resolvedType = type.type.resolved() val node = if (!resolvedType.isFinal) - constraintGraph.virtualNode + constraintGraph.virtualNode // TODO: May be do this only for a library? else concreteClass(resolvedType) node.addEdge(parameters[index]) @@ -783,14 +788,18 @@ internal object Devirtualization { 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, edge: DataFlowIR.Edge): Node { val result = dfgNodeToConstraintNode(function, edge.node) val castToType = edge.castToType?.resolved() ?: return result - val castNode = ordinaryNode { "Cast\$${function.symbol}" } - val castEdge = createCastEdge(castNode, castToType) - result.addCastEdge(castEdge) - return castNode + return doCast(function, result, castToType) } /** @@ -809,7 +818,7 @@ internal object Devirtualization { else -> error("Unexpected argument: $argument") } - fun doCall(callee: Function, arguments: List): Node { + fun doCall(callee: Function, arguments: List, returnType: DataFlowIR.Type.Declared): Node { assert(callee.parameters.size == arguments.size) { "Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol}" + " provided ${arguments.size}" @@ -818,7 +827,7 @@ internal object Devirtualization { val argument = argumentToConstraintNode(arguments[index]) argument.addEdge(parameter) } - return callee.returns + return doCast(function, callee.returns, returnType) } fun doCall(callee: DataFlowIR.FunctionSymbol, @@ -832,20 +841,25 @@ internal object Devirtualization { val fictitiousReturnNode = ordinaryNode { "External$resolvedCallee" } if (returnType.isFinal) concreteClass(returnType).addEdge(fictitiousReturnNode) - else + else { 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 } } else { calleeConstraintGraph.throws.addEdge(function.throws) if (receiverType == null) - doCall(calleeConstraintGraph, arguments) + doCall(calleeConstraintGraph, arguments, returnType) else { val receiverNode = argumentToConstraintNode(arguments[0]) - val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" } - val castedEdge = createCastEdge(castedReceiver, receiverType) - receiverNode.addCastEdge(castedEdge) - doCall(calleeConstraintGraph, listOf(castedReceiver) + arguments.drop(1)) + doCall(calleeConstraintGraph, + listOf(doCast(function, receiverNode, receiverType)) + arguments.drop(1), + returnType) } } } @@ -923,9 +937,7 @@ internal object Devirtualization { val receiverNode = edgeToConstraintNode(node.arguments[0]) if (receiverType == DataFlowIR.Type.Virtual) constraintGraph.virtualNode.addEdge(receiverNode) - val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" } - val castedEdge = createCastEdge(castedReceiver, receiverType) - receiverNode.addCastEdge(castedEdge) + val castedReceiver = doCast(function, receiverNode, receiverType) val arguments = listOf(castedReceiver) + node.arguments.drop(1) 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. // With this we're reflecting the fact that unknown function can return anything. val virtualTypeFilter = BitSet().apply { set(VIRTUAL_TYPE_ID) } - if (!returnType.isFinal) { + if (!returnType.isFinal && entryPoint == null) { receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter)) } // And throw anything. @@ -1007,19 +1019,21 @@ internal object Devirtualization { class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List) - class AnalysisResult(val devirtualizedCallSites: Map) + class AnalysisResult(val devirtualizedCallSites: Map, + val typeHierarchy: DevirtualizationAnalysis.TypeHierarchy, + val instantiatingClasses: Set) fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) : AnalysisResult { val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze() val devirtualizedCallSites = - devirtualizationAnalysisResult + devirtualizationAnalysisResult.devirtualizedCallSites .asSequence() .filter { it.key.irCallSite != null } .associate { it.key.irCallSite!! to it.value } devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites) removeRedundantCoercions(irModule, context) - return AnalysisResult(devirtualizationAnalysisResult) + return devirtualizationAnalysisResult } /** diff --git a/runtime/src/main/kotlin/kotlin/String.kt b/runtime/src/main/kotlin/kotlin/String.kt index a8c91daa9fc..1f55e65febe 100644 --- a/runtime/src/main/kotlin/kotlin/String.kt +++ b/runtime/src/main/kotlin/kotlin/String.kt @@ -52,8 +52,8 @@ public final class String : Comparable, CharSequence { 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") -public fun Any?.toString() = this?.toString() ?: "null" \ No newline at end of file +public inline fun Any?.toString() = this?.toString() ?: "null" \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/native/internal/ObjCExportUtils.kt b/runtime/src/main/kotlin/kotlin/native/internal/ObjCExportUtils.kt index 6d5be483f50..33a7eede1e1 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/ObjCExportUtils.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/ObjCExportUtils.kt @@ -286,6 +286,7 @@ private fun Kotlin_ObjCExport_isUnchecked(exception: Throwable): Boolean = @PublishedApi @SymbolName("Kotlin_ObjCExport_trapOnUndeclaredException") +@ExportForCppRuntime internal external fun trapOnUndeclaredException(exception: Throwable) @ExportForCppRuntime diff --git a/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt index f6985352c1f..69039e1e47b 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt @@ -28,10 +28,12 @@ fun ThrowTypeCastException(): Nothing { throw TypeCastException() } +@ExportForCppRuntime fun ThrowInvalidReceiverTypeException(klass: KClass<*>): Nothing { throw RuntimeException("Unexpected receiver type: " + (klass.qualifiedName ?: "noname")) } +@ExportForCppRuntime internal fun ThrowArithmeticException() : Nothing { throw ArithmeticException() }