[K/N] Support pre-creating for objects with only constant properties
This commit is contained in:
+16
-13
@@ -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.Native
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable
|
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable
|
||||||
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrReturn
|
|
||||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
|
|
||||||
private fun IrConstructor.isAnyConstructorDelegation(context: Context): Boolean {
|
private fun IrConstructor.isAutogeneratedSimpleConstructor(context: Context): Boolean {
|
||||||
val statements = this.body?.statements ?: return false
|
if (!this.isPrimary) return false
|
||||||
if (statements.size != 2) return false
|
val statements = this.body?.statements ?: return false
|
||||||
val lastStatement = statements[1]
|
if (statements.size < 2) return false
|
||||||
if (lastStatement !is IrReturn ||
|
val lastStatement = statements.last()
|
||||||
(lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false
|
if (lastStatement !is IrReturn ||
|
||||||
val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false
|
(lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false
|
||||||
val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false
|
val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false
|
||||||
return constructor.constructedClass.isAny()
|
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?
|
// TODO: shall we memoize that property?
|
||||||
internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean {
|
internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean {
|
||||||
if (!context.shouldOptimize()) return false
|
if (!context.shouldOptimize()) return false
|
||||||
if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true
|
if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true
|
||||||
val fields = context.getLayoutBuilder(this).fields
|
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 {
|
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||||
|
|||||||
@@ -1262,6 +1262,12 @@ task localClass_localHierarchy(type: KonanLocalTest) {
|
|||||||
source = "codegen/localClass/localHierarchy.kt"
|
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) {
|
task localClass_objectExpressionInProperty(type: KonanLocalTest) {
|
||||||
goldValue = "OK\n"
|
goldValue = "OK\n"
|
||||||
source = "codegen/localClass/objectExpressionInProperty.kt"
|
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())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user