diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 2fe48d50a9a..6fd4d4e0d41 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaserState import org.jetbrains.kotlin.backend.common.phaser.namedUnitPhase import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis +import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering import org.jetbrains.kotlin.backend.konan.optimizations.* @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal val contextLLVMSetupPhase = makeKonanModuleOpPhase( name = "ContextLLVMSetup", @@ -199,6 +201,11 @@ internal val dcePhase = makeKonanModuleOpPhase( } }) + // TODO: Bridge function normally calls it's target, but it could be optimized out by Autoboxing and InlinePropertyAccessor + // lowerings. But Devirtualization doesn't handle this correctly, and can replace bridge call with call of it's target. + // So it's safer not to remove target if bridge is preserved, even if target itself is not called directly + referencedFunctions.addAll(referencedFunctions.mapNotNull { it.origin.safeAs()?.bridgeTarget }) + context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() { override fun visitFile(declaration: IrFile): IrFile { declaration.declarations.removeAll { diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index a2636a090ee..6ddb025be8c 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -2955,6 +2955,14 @@ standaloneTest("devirtualization_getter_looking_as_box_function") { source = "codegen/devirtualization/getter_looking_as_box_function.kt" } +standaloneTest("devirtualization_inline_getter") { + disabled = (cacheTesting != null) // Cache is not compatible with -opt. + useGoldenData = true + flags = ["-opt"] + source = "codegen/devirtualization/inline_getter.kt" +} + + standaloneTest("devirtualization_anonymousObject") { disabled = (cacheTesting != null) // Cache is not compatible with -opt. useGoldenData = true diff --git a/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.kt b/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.kt new file mode 100644 index 00000000000..6f4f37969a8 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.kt @@ -0,0 +1,13 @@ +interface Base { val id: Int } + +inline class Child(override val id: Int = 1) : Base + +interface Base2 { val prop: Base } +class Child2(override val prop: Child) : Base2 + +fun main() { + val x : Base = Child(5) + println(x.id) + val y : Base2 = Child2(Child(5)) + println(y.prop) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.out b/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.out new file mode 100644 index 00000000000..ddafff7f120 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/devirtualization/inline_getter.out @@ -0,0 +1,2 @@ +5 +Child(id=5)