From 4b5a7c6646f3fc3e682464ff3ad6bf747d74665b Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Sat, 28 Aug 2021 11:10:49 +0700 Subject: [PATCH] [K/N] Add binary option to disable freezing We don't want to deprecate freezing at all, but it is possible that freezing, new memory model and lazy global initialization combination might not work in some cases. It might be a problem when such case appears in 3rd-party library that user can't fix. To mitigate this problem this commit introduces `freezing` binary option. It has three variants: * Full - ol' good behavior. * Disabled - well, no freezing at all. * ExplicitOnly - a compromise when user want to freeze something themselves, but something is messed up during globals initialization. --- .../kotlin/backend/konan/BinaryOptions.kt | 2 ++ .../kotlin/backend/konan/Freezing.kt | 35 +++++++++++++++++++ .../kotlin/backend/konan/KonanConfig.kt | 13 +++++++ .../kotlin/backend/konan/llvm/IrToBitcode.kt | 10 ++++-- .../backend/konan/llvm/RTTIGenerator.kt | 3 +- kotlin-native/runtime/src/main/cpp/Atomic.cpp | 3 ++ .../src/main/cpp/CompilerConstants.cpp | 5 +++ .../src/main/cpp/CompilerConstants.hpp | 2 ++ kotlin-native/runtime/src/main/cpp/Worker.cpp | 2 +- 9 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Freezing.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt index e88d3f0dbe8..3741765908c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt @@ -15,6 +15,8 @@ object BinaryOptions : BinaryOptionRegistry() { val runtimeAssertionsMode by option() val memoryModel by option() + + val freezing by option() } open class BinaryOption( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Freezing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Freezing.kt new file mode 100644 index 00000000000..fecf196c7bb --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Freezing.kt @@ -0,0 +1,35 @@ +/* + * 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 org.jetbrains.kotlin.backend.konan + +/** + * Gradually control what parts of freezing are enabled. + * Not intended to be used with legacy MM where freezing is a must. + * + * If [enableFreezeAtRuntime] is false then `Any.freeze()` and `checkIfFrozen(ref: Any?)` are no-op. + * [freezeImplicit] enabled freezing for @Frozen types and @SharedImmutable globals (i.e. implicit calls to `Any.freeze()`). + */ +enum class Freezing(val enableFreezeAtRuntime: Boolean, val freezeImplicit: Boolean) { + /** + * Enable freezing in `Any.freeze()` as well as for @Frozen types and @SharedImmutable globals. + */ + Full(true, true), + + /** + * Enable freezing only in explicit calls to `Any.freeze()`. + */ + ExplicitOnly(true, false), + + /** + * No freezing at all. + */ + Disabled(false, false); + + companion object { + // Users might depend on runtime guarantees of freezing, so it should be enabled by default. + val Default = Full + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 625f7b24ace..c2b649f2280 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -81,6 +81,19 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration val runtimeAssertsMode: RuntimeAssertsMode get() = configuration.get(BinaryOptions.runtimeAssertionsMode) ?: RuntimeAssertsMode.IGNORE val workerExceptionHandling: WorkerExceptionHandling get() = configuration.get(KonanConfigKeys.WORKER_EXCEPTION_HANDLING)!! val runtimeLogs: String? get() = configuration.get(KonanConfigKeys.RUNTIME_LOGS) + val freezing: Freezing by lazy { + val freezingMode = configuration.get(BinaryOptions.freezing) + when { + freezingMode == null -> Freezing.Default + memoryModel != MemoryModel.EXPERIMENTAL && freezingMode != Freezing.Default -> { + configuration.report( + CompilerMessageSeverity.ERROR, + "`freezing` can only be adjusted with experimental MM. Falling back to default behavior.") + Freezing.Default + } + else -> freezingMode + } + } val needVerifyIr: Boolean get() = configuration.get(KonanConfigKeys.VERIFY_IR) == true diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 9c76fefc44d..fb670e07ea1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -88,6 +88,11 @@ val IrField.isGlobalNonPrimitive get() = when { else -> storageKind == FieldStorageKind.GLOBAL } + +internal fun IrField.shouldBeFrozen(context: Context): Boolean = + this.storageKind == FieldStorageKind.SHARED_FROZEN && + (context.memoryModel != MemoryModel.EXPERIMENTAL || context.config.freezing.freezeImplicit) + internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid { val generator = RTTIGenerator(context) @@ -341,7 +346,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map?) { val initialization = evaluateExpression(irField.initializer!!.expression) - if (irField.storageKind == FieldStorageKind.SHARED_FROZEN) + if (irField.shouldBeFrozen(context)) freeze(initialization, currentCodeContext.exceptionHandler) initialization } else { @@ -1754,7 +1759,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map(Kotlin_destroyRuntimeMode); @@ -25,3 +26,7 @@ ALWAYS_INLINE bool compiler::gcAggressive() noexcept { ALWAYS_INLINE compiler::WorkerExceptionHandling compiler::workerExceptionHandling() noexcept { return static_cast(Kotlin_workerExceptionHandling); } + +ALWAYS_INLINE bool compiler::freezingEnabled() noexcept { + return Kotlin_freezingEnabled != 0; +} diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp index 38034eced3e..b21310be196 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp @@ -69,6 +69,8 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept { return Kotlin_runtimeLogs == nullptr ? std::string_view() : std::string_view(Kotlin_runtimeLogs); } +bool freezingEnabled() noexcept; + } // namespace compiler } // namespace kotlin diff --git a/kotlin-native/runtime/src/main/cpp/Worker.cpp b/kotlin-native/runtime/src/main/cpp/Worker.cpp index f885f09ee0b..a54c879f32c 100644 --- a/kotlin-native/runtime/src/main/cpp/Worker.cpp +++ b/kotlin-native/runtime/src/main/cpp/Worker.cpp @@ -1167,7 +1167,7 @@ KNativePtr Kotlin_Worker_detachObjectGraphInternal(KInt transferMode, KRef produ } void Kotlin_Worker_freezeInternal(KRef object) { - if (object != nullptr) + if (object != nullptr && compiler::freezingEnabled()) FreezeSubgraph(object); }