diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt index b1c1a8774cc..af6a52fb169 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGSerializer.kt @@ -326,13 +326,14 @@ internal object DFGSerializer { } } - class ExternalFunctionSymbol(val hash: Long, val name: String?) { + class ExternalFunctionSymbol(val hash: Long, val name: String?, val isExported: Boolean) { - constructor(data: ArraySlice) : this(data.readLong(), data.readNullableString()) + constructor(data: ArraySlice) : this(data.readLong(), data.readNullableString(), data.readBoolean()) fun write(result: ArraySlice) { result.writeLong(hash) result.writeNullableString(name) + result.writeBoolean(isExported) } } @@ -379,8 +380,8 @@ internal object DFGSerializer { } companion object { - fun external(base: FunctionSymbolBase, hash: Long, name: String?) = - FunctionSymbol(base, ExternalFunctionSymbol(hash, name), null, null) + fun external(base: FunctionSymbolBase, hash: Long, name: String?, isExported: Boolean) = + FunctionSymbol(base, ExternalFunctionSymbol(hash, name, isExported), null, null) fun public(base: FunctionSymbolBase, hash: Long, index: Int, bridgeTarget: Int?, name: String?) = FunctionSymbol(base, null, PublicFunctionSymbol(hash, index, bridgeTarget, name), null) @@ -876,7 +877,7 @@ internal object DFGSerializer { val bridgeTarget = (symbol as? DataFlowIR.FunctionSymbol.Declared)?.let { functionSymbolMap[it] } when (symbol) { is DataFlowIR.FunctionSymbol.External -> - FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash, symbol.name) + FunctionSymbol.external(buildFunctionSymbolBase(symbol), symbol.hash, symbol.name, symbol.isExported) is DataFlowIR.FunctionSymbol.Public -> FunctionSymbol.public(buildFunctionSymbolBase(symbol), symbol.hash, @@ -1051,7 +1052,7 @@ internal object DFGSerializer { val private = it.private when { external != null -> - DataFlowIR.FunctionSymbol.External(external.hash, attributes, null, external.name) + DataFlowIR.FunctionSymbol.External(external.hash, attributes, null, external.name, external.isExported) public != null -> { val symbolTableIndex = public.index 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 fe47bc88cec..b0598efa378 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 @@ -132,7 +132,7 @@ internal object DataFlowIR { var escapes: Int? = null var pointsTo: IntArray? = null - class External(val hash: Long, attributes: Int, irFunction: IrFunction?, name: String? = null) + class External(val hash: Long, attributes: Int, irFunction: IrFunction?, name: String? = null, val isExported: Boolean) : FunctionSymbol(attributes, irFunction, name) { override fun equals(other: Any?): Boolean { @@ -609,7 +609,7 @@ internal object DataFlowIR { val escapesBitMask = (escapesAnnotation?.getValueArgument(0) as? IrConst)?.value @Suppress("UNCHECKED_CAST") val pointsToBitMask = (pointsToAnnotation?.getValueArgument(0) as? IrVararg)?.elements?.map { (it as IrConst).value } - FunctionSymbol.External(name.localHash.value, attributes, it, takeName { name }).apply { + FunctionSymbol.External(name.localHash.value, attributes, it, takeName { name }, it.isExported()).apply { escapes = escapesBitMask pointsTo = pointsToBitMask?.let { it.toIntArray() } } 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 85bef0b47df..1fd901399f7 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 @@ -64,14 +64,18 @@ internal object Devirtualization { val exportedFunctions = if (entryPoint != null) 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.symbolTable.functionMap.values.filterIsInstance() + - moduleDFG.symbolTable.classMap.values - .filterIsInstance() - .flatMap { it.vtable + it.itable.values } - .filterIsInstance() - .filter { moduleDFG.functions.containsKey(it) } + else { + // In a library every public function and every function accessible via virtual call belongs to the rootset. + moduleDFG.symbolTable.functionMap.values.filter { + it is DataFlowIR.FunctionSymbol.Public + || (it as? DataFlowIR.FunctionSymbol.External)?.isExported == true + } + + moduleDFG.symbolTable.classMap.values + .filterIsInstance() + .flatMap { it.vtable + it.itable.values } + .filterIsInstance() + .filter { moduleDFG.functions.containsKey(it) } + } // TODO: Are globals initializers always called whether they are actually reachable from roots or not? val globalInitializers = moduleDFG.symbolTable.functionMap.values.filter { it.isGlobalInitializer } + diff --git a/backend.native/tests/framework/values/expectedLazy.h b/backend.native/tests/framework/values/expectedLazy.h index 63bb48e7838..1354379d1b3 100644 --- a/backend.native/tests/framework/values/expectedLazy.h +++ b/backend.native/tests/framework/values/expectedLazy.h @@ -1123,6 +1123,7 @@ __attribute__((swift_name("ValuesKt"))) + (BOOL)testInterfaceTypeCheckX:(id)x __attribute__((swift_name("testInterfaceTypeCheck(x:)"))); + (int32_t)testAbstractInterfaceCallX:(id)x __attribute__((swift_name("testAbstractInterfaceCall(x:)"))); + (int32_t)testAbstractInterfaceCall2X:(id)x __attribute__((swift_name("testAbstractInterfaceCall2(x:)"))); ++ (void)fooA:(ValuesKotlinAtomicReference *)a __attribute__((swift_name("foo(a:)"))); @property (class, readonly) double dbl __attribute__((swift_name("dbl"))); @property (class, readonly) float flt __attribute__((swift_name("flt"))); @property (class, readonly) int32_t integer __attribute__((swift_name("integer"))); diff --git a/backend.native/tests/framework/values/values.kt b/backend.native/tests/framework/values/values.kt index ce7f8fe2953..eb42247a731 100644 --- a/backend.native/tests/framework/values/values.kt +++ b/backend.native/tests/framework/values/values.kt @@ -830,4 +830,6 @@ object GH3525 : GH3525Base() { class TestStringConversion { lateinit var str: Any -} \ No newline at end of file +} + +fun foo(a: kotlin.native.concurrent.AtomicReference<*>) {} \ No newline at end of file