[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.
This commit is contained in:
+2
@@ -15,6 +15,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
||||
val runtimeAssertionsMode by option<RuntimeAssertsMode>()
|
||||
|
||||
val memoryModel by option<MemoryModel>()
|
||||
|
||||
val freezing by option<Freezing>()
|
||||
}
|
||||
|
||||
open class BinaryOption<T : Any>(
|
||||
|
||||
+35
@@ -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
|
||||
}
|
||||
}
|
||||
+13
@@ -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
|
||||
|
||||
+8
-2
@@ -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<IrE
|
||||
val address = context.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress(this)
|
||||
val initialValue = if (irField.initializer?.expression !is IrConst<*>?) {
|
||||
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<IrE
|
||||
)
|
||||
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind == FieldStorageKind.GLOBAL)
|
||||
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
||||
if (value.symbol.owner.storageKind == FieldStorageKind.SHARED_FROZEN)
|
||||
if (value.symbol.owner.shouldBeFrozen(context))
|
||||
functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler)
|
||||
functionGenerationContext.storeAny(valueToAssign, globalAddress, false)
|
||||
}
|
||||
@@ -2629,6 +2634,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
overrideRuntimeGlobal("Kotlin_destroyRuntimeMode", Int32(context.config.destroyRuntimeMode.value))
|
||||
overrideRuntimeGlobal("Kotlin_gcAggressive", Int32(if (context.config.gcAggressive) 1 else 0))
|
||||
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
||||
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+1
-2
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.symbols.isPublicApi
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -54,7 +53,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
private fun flagsFromClass(irClass: IrClass): Int {
|
||||
var result = 0
|
||||
if (irClass.isFrozen)
|
||||
if (irClass.isFrozen && context.config.freezing.freezeImplicit)
|
||||
result = result or TF_IMMUTABLE
|
||||
// TODO: maybe perform deeper analysis to find surely acyclic types.
|
||||
if (!irClass.isInterface && !irClass.isAbstract() && !irClass.isAnnotationClass) {
|
||||
|
||||
@@ -176,6 +176,9 @@ KNativePtr Kotlin_AtomicNativePtr_get(KRef thiz) {
|
||||
}
|
||||
|
||||
void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
|
||||
if (!kotlin::compiler::freezingEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (value != nullptr && !isPermanentOrFrozen(value)) {
|
||||
ThrowInvalidMutabilityException(value);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using namespace kotlin;
|
||||
RUNTIME_WEAK int32_t Kotlin_destroyRuntimeMode = 1;
|
||||
RUNTIME_WEAK int32_t Kotiln_gcAggressive = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||
|
||||
ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept {
|
||||
return static_cast<compiler::DestroyRuntimeMode>(Kotlin_destroyRuntimeMode);
|
||||
@@ -25,3 +26,7 @@ ALWAYS_INLINE bool compiler::gcAggressive() noexcept {
|
||||
ALWAYS_INLINE compiler::WorkerExceptionHandling compiler::workerExceptionHandling() noexcept {
|
||||
return static_cast<compiler::WorkerExceptionHandling>(Kotlin_workerExceptionHandling);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool compiler::freezingEnabled() noexcept {
|
||||
return Kotlin_freezingEnabled != 0;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user