From ce44403d1b85c5a9fa8e6f78150112f11e19b836 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Thu, 26 Aug 2021 20:13:18 +0500 Subject: [PATCH] [K/N][IR][codegen] Made lazy init logic JVM-like Now a file will be initialized only if a top level function has been called rather than any function like was before the commit --- .../kotlin/backend/common/ir/IrUtils.kt | 2 +- .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 4 +++ .../backend/konan/lower/DelegationLowering.kt | 1 + .../konan/lower/FileInitializersLowering.kt | 23 +++++++-------- .../backend.native/tests/build.gradle | 5 ++++ ...alNotInitedAfterAccessingClassInternals.kt | 29 +++++++++++++++++++ 6 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 kotlin-native/backend.native/tests/codegen/initializers/globalNotInitedAfterAccessingClassInternals.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index 9b3ea6279dd..b4ca9bc0f12 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -404,7 +404,7 @@ fun IrFunction.isMethodOfAny(): Boolean = else -> false } -fun IrClass.simpleFunctions() = declarations.flatMap { +fun IrDeclarationContainer.simpleFunctions() = declarations.flatMap { when (it) { is IrSimpleFunction -> listOf(it) is IrProperty -> listOfNotNull(it.getter, it.setter) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index ca8a2b8f22a..15ca02ebf0d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -487,6 +487,10 @@ internal class KonanSymbols( context.builtIns.builtInsModule.findClassAcrossModuleDependencies( ClassId.topLevel(KonanFqNames.sharedImmutable))!!) + val eagerInitialization = symbolTable.referenceClass( + context.builtIns.builtInsModule.findClassAcrossModuleDependencies( + ClassId.topLevel(KonanFqNames.eagerInitialization))!!) + private fun topLevelClass(fqName: FqName): IrClassSymbol = irBuiltIns.findClass(fqName.shortName(), fqName.parent())!! private fun internalFunction(name: String): IrSimpleFunctionSymbol = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index 0ec9d24a6d4..402e51e7954 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -97,6 +97,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa ).apply { parent = irFile annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.sharedImmutable.owner) + annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.eagerInitialization.owner) } irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FileInitializersLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FileInitializersLowering.kt index 055ec120bf2..62bd947a10d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FileInitializersLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FileInitializersLowering.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.ir.simpleFunctions import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanFqNames @@ -81,20 +82,16 @@ internal class FileInitializersLowering(val context: Context) : FileLoweringPass ) else null - irFile.transformChildrenVoid(object: IrElementTransformerVoid() { - override fun visitFunction(declaration: IrFunction): IrStatement { - declaration.transformChildrenVoid(this) - val body = declaration.body ?: return declaration - val statements = (body as IrBlockBody).statements - context.createIrBuilder(declaration.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run { - // The order of calling initializers: first global, then thread-local. - // It is ok for a thread local top level property to reference a global, but not vice versa. - threadLocalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) } - globalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) } - } - return declaration + irFile.simpleFunctions().forEach { + val body = it.body ?: return@forEach + val statements = (body as IrBlockBody).statements + context.createIrBuilder(it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run { + // The order of calling initializers: first global, then thread-local. + // It is ok for a thread local top level property to reference a global, but not vice versa. + threadLocalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) } + globalInitFunction?.let { statements.add(0, irCallFileInitializer(it.symbol)) } } - }) + } } private fun buildInitFileFunction(irFile: IrFile, name: String, origin: IrDeclarationOrigin) = context.irFactory.buildFun { diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 129e66fc8f9..42b67ae8819 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -1481,6 +1481,11 @@ standaloneTest("initializers_globalInitedAfterAccessingFile") { source = "codegen/initializers/globalInitedAfterAccessingFile.kt" } +standaloneTest("initializers_globalNotInitedAfterAccessingClassInternals") { + enabled = project.globalTestArgs.contains('-Xir-property-lazy-initialization=enable') || isExperimentalMM + source = "codegen/initializers/globalNotInitedAfterAccessingClassInternals.kt" +} + standaloneTest("initializers_globalInitedBeforeThreadLocal") { source = "codegen/initializers/globalInitedBeforeThreadLocal.kt" } diff --git a/kotlin-native/backend.native/tests/codegen/initializers/globalNotInitedAfterAccessingClassInternals.kt b/kotlin-native/backend.native/tests/codegen/initializers/globalNotInitedAfterAccessingClassInternals.kt new file mode 100644 index 00000000000..2bbab4a4a5b --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/initializers/globalNotInitedAfterAccessingClassInternals.kt @@ -0,0 +1,29 @@ +/* + * 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. + */ + +// FILE: lib.kt +var z = false + +// FILE: lib2.kt +import kotlin.test.* + +val x = foo() + +private fun foo(): Int { + z = true + return 42 +} + +class C { + fun bar() = 117 +} + +// FILE: main.kt +import kotlin.test.* + +fun main() { + C().bar() + assertFalse(z) +}