From 8a418161f6a0733dc708b46c2e2e455fc0377797 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Thu, 13 May 2021 12:12:23 +0300 Subject: [PATCH] [K/N] Support pre-creating for objects with only constant properties --- .../backend/konan/llvm/CodeGenerator.kt | 29 ++++--- .../backend.native/tests/build.gradle | 6 ++ .../objectDeclaration/globalConstants.kt | 81 +++++++++++++++++++ 3 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 kotlin-native/backend.native/tests/codegen/objectDeclaration/globalConstants.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 33dd364403f..611a682fa8f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -20,28 +20,31 @@ import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Native import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin -import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall -import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue -import org.jetbrains.kotlin.ir.expressions.IrReturn +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.konan.ForeignExceptionMode -private fun IrConstructor.isAnyConstructorDelegation(context: Context): Boolean { - val statements = this.body?.statements ?: return false - if (statements.size != 2) return false - val lastStatement = statements[1] - if (lastStatement !is IrReturn || - (lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false - val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false - val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false - return constructor.constructedClass.isAny() +private fun IrConstructor.isAutogeneratedSimpleConstructor(context: Context): Boolean { + if (!this.isPrimary) return false + val statements = this.body?.statements ?: return false + if (statements.size < 2) return false + val lastStatement = statements.last() + if (lastStatement !is IrReturn || + (lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false + val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false + val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false + if (!constructor.constructedClass.isAny()) return false + return statements.asSequence().take(statements.size - 1).drop(1).all { + it is IrBlock && it.origin == IrStatementOrigin.INITIALIZE_FIELD } +} // TODO: shall we memoize that property? internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean { if (!context.shouldOptimize()) return false if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true val fields = context.getLayoutBuilder(this).fields - return fields.isEmpty() && this.constructors.all { it.isAnyConstructorDelegation(context) } + return fields.all { (it.correspondingPropertySymbol?.owner?.isConst ?: false) && (it.initializer?.expression is IrConst<*>) } && + this.constructors.all { it.isAutogeneratedSimpleConstructor(context) } } internal class CodeGenerator(override val context: Context) : ContextUtils { diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 3ec6b92aa3a..a53831e39de 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -1262,6 +1262,12 @@ task localClass_localHierarchy(type: KonanLocalTest) { source = "codegen/localClass/localHierarchy.kt" } +standaloneTest("objectDeclaration_globalConstants") { + disabled = (cacheTesting != null) // Cache is not compatible with -opt. + flags = ["-opt", "-Xopt-in=kotlin.native.internal.InternalForKotlinNative", "-tr"] + source = "codegen/objectDeclaration/globalConstants.kt" +} + task localClass_objectExpressionInProperty(type: KonanLocalTest) { goldValue = "OK\n" source = "codegen/localClass/objectExpressionInProperty.kt" diff --git a/kotlin-native/backend.native/tests/codegen/objectDeclaration/globalConstants.kt b/kotlin-native/backend.native/tests/codegen/objectDeclaration/globalConstants.kt new file mode 100644 index 00000000000..989b719a985 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/objectDeclaration/globalConstants.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package codegen.objectDeclaration.globalConstants + +import kotlin.test.* +import kotlin.native.internal.* + +object EmptyClass {} + +@Test fun checkEmptyClass() { + assertTrue(EmptyClass.isPermanent()) +} + + +object ClassWithConstants { + const val A = 1 + const val B = 2L + const val C = 3.0 + const val D = 4.0f + const val E = 5.toShort() + const val F = 6.toByte() + const val G = "8" +} + +@Test fun checkInit() { + assertTrue(ClassWithConstants.isPermanent()) + assertEquals(1, ClassWithConstants.A) + assertEquals(2, ClassWithConstants.B) + assertEquals(3.0, ClassWithConstants.C) + assertEquals(4.0f, ClassWithConstants.D) + assertEquals(5, ClassWithConstants.E) + assertEquals(6, ClassWithConstants.F) + assertEquals("8", ClassWithConstants.G) + + assertEquals(1, (ClassWithConstants::A)()) + assertEquals(2, (ClassWithConstants::B)()) + assertEquals(3.0, (ClassWithConstants::C)()) + assertEquals(4.0f, (ClassWithConstants::D)()) + assertEquals(5, (ClassWithConstants::E)()) + assertEquals(6, (ClassWithConstants::F)()) + assertEquals("8", (ClassWithConstants::G)()) +} + + +var ClassWithConstructorInitialized = 0 + +object ClassWithConstructor { + init { + ClassWithConstructorInitialized += 1 + } + const val A = 1; +} + +@Test fun checkConstructor() { + assertEquals(0, ClassWithConstructorInitialized) + assertEquals(1, ClassWithConstructor.A) + assertEquals(1, ClassWithConstructorInitialized) + assertEquals(1, ClassWithConstructor.A) + assertEquals(1, ClassWithConstructorInitialized) + assertFalse(ClassWithConstructor.isPermanent()) +} + +object ClassWithField { + val x = 4 +} + +@Test fun checkField() { + assertFalse(ClassWithField.isPermanent()) +} + +object ClassWithComputedField { + val x : Int + get() = 4 +} + +@Test fun checkComputedField() { + assertTrue(ClassWithComputedField.isPermanent()) +}