[K/N][IR][codegen] Implemented eager initialization
Properties marked with @EagerInitialization will be initialized at program/worker start even in lazy initialization mode
This commit is contained in:
+1
@@ -33,4 +33,5 @@ object KonanFqNames {
|
||||
val hasFinalizer = FqName("kotlin.native.internal.HasFinalizer")
|
||||
val hasFreezeHook = FqName("kotlin.native.internal.HasFreezeHook")
|
||||
val gcUnsafeCall = FqName("kotlin.native.internal.GCUnsafeCall")
|
||||
val eagerInitialization = FqName("kotlin.native.EagerInitialization")
|
||||
}
|
||||
|
||||
+12
-10
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.coverage.LLVMCoverageInstrumentation
|
||||
import org.jetbrains.kotlin.backend.konan.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
||||
@@ -370,6 +371,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
using(VariableScope()) usingVariableScope@{
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { it.storageKind == FieldStorageKind.SHARED_FROZEN }
|
||||
.filterNot { it.shouldBeInitializedEagerly }
|
||||
.forEach { initGlobalField(it) }
|
||||
ret(null)
|
||||
}
|
||||
@@ -390,11 +392,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
positionAtEnd(bbInitGlobals)
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { it.storageKind == FieldStorageKind.GLOBAL }
|
||||
.filterNot { it.shouldBeInitializedEagerly }
|
||||
.forEach { initGlobalField(it) }
|
||||
br(bbInitThreadLocals)
|
||||
positionAtEnd(bbInitThreadLocals)
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { it.storageKind == FieldStorageKind.THREAD_LOCAL }
|
||||
.filterNot { it.shouldBeInitializedEagerly }
|
||||
.forEach { initThreadLocalField(it) }
|
||||
ret(null)
|
||||
}
|
||||
@@ -492,11 +496,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
// Globals initializers may contain accesses to objects, so visit them first.
|
||||
appendingTo(bbInit) {
|
||||
if (!context.useLazyFileInitializers()) {
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filterNot { it.storageKind == FieldStorageKind.THREAD_LOCAL }
|
||||
.forEach { initGlobalField(it) }
|
||||
}
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { !context.useLazyFileInitializers() || it.shouldBeInitializedEagerly }
|
||||
.filterNot { it.storageKind == FieldStorageKind.THREAD_LOCAL }
|
||||
.forEach { initGlobalField(it) }
|
||||
context.llvm.initializersGenerationState.moduleGlobalInitializers.forEach {
|
||||
if (context.shouldContainLocationDebugInfo())
|
||||
debugLocation(it.startLocation!!, it.endLocation)
|
||||
@@ -511,11 +514,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
store(Int32(FILE_NOT_INITIALIZED).llvm, address)
|
||||
LLVMSetInitializer(address, Int32(FILE_NOT_INITIALIZED).llvm)
|
||||
}
|
||||
if (!context.useLazyFileInitializers()) {
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { it.storageKind == FieldStorageKind.THREAD_LOCAL }
|
||||
.forEach { initThreadLocalField(it) }
|
||||
}
|
||||
context.llvm.initializersGenerationState.topLevelFields
|
||||
.filter { !context.useLazyFileInitializers() || it.shouldBeInitializedEagerly }
|
||||
.filter { it.storageKind == FieldStorageKind.THREAD_LOCAL }
|
||||
.forEach { initThreadLocalField(it) }
|
||||
context.llvm.initializersGenerationState.moduleThreadLocalInitializers.forEach {
|
||||
if (context.shouldContainLocationDebugInfo())
|
||||
debugLocation(it.startLocation!!, it.endLocation)
|
||||
|
||||
+9
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.lower
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.storageKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.setDeclarationsParent
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -39,6 +41,12 @@ internal val IrFunction.isFileInitializer: Boolean
|
||||
internal fun IrBuilderWithScope.irCallFileInitializer(initializer: IrFunctionSymbol) =
|
||||
irCall(initializer).apply { putValueArgument(0, irFalse()) }
|
||||
|
||||
internal val IrField.shouldBeInitializedEagerly: Boolean
|
||||
get() {
|
||||
val annotations = correspondingPropertySymbol?.owner?.annotations ?: annotations
|
||||
return annotations.hasAnnotation(KonanFqNames.eagerInitialization)
|
||||
}
|
||||
|
||||
// TODO: ExplicitlyExported for IR proto are not longer needed.
|
||||
internal class FileInitializersLowering(val context: Context) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
@@ -47,7 +55,7 @@ internal class FileInitializersLowering(val context: Context) : FileLoweringPass
|
||||
var kPropertiesField: IrField? = null
|
||||
for (declaration in irFile.declarations) {
|
||||
val irField = (declaration as? IrField) ?: (declaration as? IrProperty)?.backingField
|
||||
if (irField == null || !irField.hasNonConstInitializer) continue
|
||||
if (irField == null || !irField.hasNonConstInitializer || irField.shouldBeInitializedEagerly) continue
|
||||
when {
|
||||
irField.origin == DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION -> {
|
||||
require(kPropertiesField == null) { "Expected at most one kProperties field" }
|
||||
|
||||
@@ -1485,6 +1485,26 @@ standaloneTest("initializers_globalInitedBeforeThreadLocal") {
|
||||
source = "codegen/initializers/globalInitedBeforeThreadLocal.kt"
|
||||
}
|
||||
|
||||
standaloneTest("initializers_eagerInitializationGlobal1") {
|
||||
enabled = project.globalTestArgs.contains('-Xir-property-lazy-initialization')
|
||||
source = "codegen/initializers/eagerInitializationGlobal1.kt"
|
||||
}
|
||||
|
||||
standaloneTest("initializers_eagerInitializationGlobal2") {
|
||||
enabled = project.globalTestArgs.contains('-Xir-property-lazy-initialization')
|
||||
source = "codegen/initializers/eagerInitializationGlobal2.kt"
|
||||
}
|
||||
|
||||
standaloneTest("initializers_eagerInitializationThreadLocal1") {
|
||||
enabled = project.globalTestArgs.contains('-Xir-property-lazy-initialization')
|
||||
source = "codegen/initializers/eagerInitializationThreadLocal1.kt"
|
||||
}
|
||||
|
||||
standaloneTest("initializers_eagerInitializationThreadLocal2") {
|
||||
enabled = project.globalTestArgs.contains('-Xir-property-lazy-initialization')
|
||||
source = "codegen/initializers/eagerInitializationThreadLocal2.kt"
|
||||
}
|
||||
|
||||
standaloneTest("initializers_workers1") {
|
||||
source = "codegen/initializers/workers1.kt"
|
||||
goldValue = "42\n3\n"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
import kotlin.test.*
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
val x = foo()
|
||||
|
||||
private fun foo(): Int {
|
||||
z1 = true
|
||||
return 42
|
||||
}
|
||||
|
||||
// Will be initialized since [x]'s initializer calls a function from the file.
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
assertTrue(z1)
|
||||
assertTrue(z2)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
import kotlin.test.*
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
val x = run { z1 = true; 42 }
|
||||
|
||||
// Won't be initialized (cause no function from the file will be called during [x] initialization).
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
assertTrue(z1)
|
||||
assertFalse(z2)
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
@ThreadLocal
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
@ThreadLocal
|
||||
var z3 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
import kotlin.test.*
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
@ThreadLocal
|
||||
val x = foo()
|
||||
|
||||
private fun foo(): Int {
|
||||
z1 = true
|
||||
return 42
|
||||
}
|
||||
|
||||
// Both will be initialized since [x]'s initializer calls a function from the file.
|
||||
val y1 = run { z2 = true; 117 }
|
||||
|
||||
@ThreadLocal
|
||||
val y2 = run { z3 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
assertTrue(z1)
|
||||
assertTrue(z2)
|
||||
assertTrue(z3)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
@ThreadLocal
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
@ThreadLocal
|
||||
var z3 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
import kotlin.test.*
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
@ThreadLocal
|
||||
val x = run { z1 = true; 42 }
|
||||
|
||||
// Won't be initialized (cause no function from the file will be called during [x] initialization).
|
||||
val y1 = run { z2 = true; 117 }
|
||||
|
||||
@ThreadLocal
|
||||
val y2 = run { z3 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
assertTrue(z1)
|
||||
assertFalse(z2)
|
||||
assertFalse(z3)
|
||||
}
|
||||
Reference in New Issue
Block a user