[K/N] Support pre-creating for objects with only constant properties

This commit is contained in:
Pavel Kunyavskiy
2021-05-13 12:12:23 +03:00
committed by Space
parent e4ecb3745b
commit 8a418161f6
3 changed files with 103 additions and 13 deletions
@@ -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 {
@@ -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"
@@ -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())
}