diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt index 5b52e3b3b97..e0d5b4b4c5f 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt @@ -173,7 +173,6 @@ object KotlinTypes { val set by CollectionClassifier val map by CollectionClassifier - val nativePtr by InteropType val cOpaque by InteropType @@ -198,7 +197,6 @@ object KotlinTypes { val cFunction by InteropClassifier val objCObjectVar by InteropClassifier - val objCStringVarOf by InteropClassifier val objCObjectBase by InteropClassifier val objCObjectBaseMeta by InteropClassifier @@ -220,6 +218,7 @@ object KotlinTypes { private object InteropClassifier : ClassifierAtPackage("kotlinx.cinterop") private object InteropType : TypeAtPackage("kotlinx.cinterop") + } abstract class KotlinFile( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index c03bf930539..3f72b515d52 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType @@ -367,6 +368,9 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val val listOfInternal = internalFunction("listOfInternal") + val threadLocal = + context.builtIns.builtInsModule.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("kotlin.native.ThreadLocal")))!! + private fun internalFunction(name: String): IrSimpleFunctionSymbol = symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single()) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index d4ebacb5dd2..a7ae658e83e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -307,7 +307,7 @@ internal val ClassDescriptor.objectInstanceShadowFieldSymbolName: String assert (this.isExported()) assert (this.kind.isSingleton) assert (!this.isUnit()) - assert (this.symbol.objectIsShared) + assert (this.objectIsShared) return "kshadowobjref:$fqNameSafe" } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 38414a68682..5ccccd10de6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -77,7 +77,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { fun getObjectInstanceShadowStorage(descriptor: ClassDescriptor): LLVMValueRef { assert (!descriptor.isUnit()) - assert (descriptor.symbol.objectIsShared) + assert (descriptor.objectIsShared) val llvmGlobal = if (!isExternal(descriptor)) { context.llvmDeclarations.forSingleton(descriptor).instanceShadowFieldRef!! } else { @@ -300,6 +300,11 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } } + fun freeze(value: LLVMValueRef, exceptionHandler: ExceptionHandler) { + if (isObjectRef(value)) + call(context.llvm.freezeSubgraph, listOf(value), Lifetime.IRRELEVANT, exceptionHandler) + } + private fun updateReturnRef(value: LLVMValueRef, address: LLVMValueRef) { call(context.llvm.updateReturnRefFunction, listOf(address, value)) } @@ -694,7 +699,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } } - val shared = descriptor.symbol.objectIsShared && context.config.threadsAreAllowed + val shared = descriptor.objectIsShared && context.config.threadsAreAllowed val objectPtr = codegen.getObjectInstanceStorage(descriptor, shared) val bbCurrent = currentBlock val bbInit= basicBlock("label_init", locationInfo) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 34d736cd98d..d2148a9b0f6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -418,6 +418,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val appendToInitalizersTail = importRtFunction("AppendToInitializersTail") val initRuntimeIfNeeded = importRtFunction("Kotlin_initRuntimeIfNeeded") val mutationCheck = importRtFunction("MutationCheck") + val freezeSubgraph = importRtFunction("FreezeSubgraph") val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") } val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index d11c476f4a9..c69198041bf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -50,9 +50,12 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe private val threadLocalAnnotationFqName = FqName("kotlin.native.ThreadLocal") -val IrClassSymbol.objectIsShared get() = +val IrClass.objectIsShared get() = !descriptor.annotations.hasAnnotation(threadLocalAnnotationFqName) +val IrField.isShared get() = + !descriptor.annotations.hasAnnotation(threadLocalAnnotationFqName) && !descriptor.isVar + internal fun emitLLVM(context: Context, phaser: PhaseManager) { val irModule = context.irModule!! @@ -373,14 +376,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map?) { - val initialization = evaluateExpression(it.initializer!!.expression) - val address = context.llvmDeclarations.forStaticField(it).storage - storeAny(initialization, address) + if (it.isShared) { + val initialization = evaluateExpression(it.initializer!!.expression) + val address = context.llvmDeclarations.forStaticField(it).storage + freeze(initialization, currentCodeContext.exceptionHandler) + storeAny(initialization, address) + } + } + } + ret(null) + } + + appendingTo(bbLocalInit) { + context.llvm.fileInitializers + .forEach { + if (it.initializer?.expression !is IrConst<*>?) { + if (!it.isShared) { + val initialization = evaluateExpression(it.initializer!!.expression) + val address = context.llvmDeclarations.forStaticField(it).storage + storeAny(initialization, address) + } } } ret(null) @@ -408,17 +431,25 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map && this.kind == IrC private var topLevelInitializersCounter = 0 -internal fun IrFile.addTopLevelInitializer(expression: IrExpression) { +internal fun IrFile.addTopLevelInitializer(expression: IrExpression, context: KonanBackendContext, threadLocal: Boolean) { val fieldDescriptor = PropertyDescriptorImpl.create( this.packageFragmentDescriptor, - Annotations.EMPTY, + if (threadLocal) + AnnotationsImpl(listOf(AnnotationDescriptorImpl(context.ir.symbols.threadLocal.defaultType, + emptyMap(), SourceElement.NO_SOURCE))) + else + Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE, false, @@ -76,7 +82,6 @@ internal fun IrFile.addTopLevelInitializer(expression: IrExpression) { false ) - val builtIns = fieldDescriptor.builtIns fieldDescriptor.setType(expression.type.toKotlinType(), emptyList(), null, null as KotlinType?) fieldDescriptor.initialize(null, null) @@ -123,8 +128,8 @@ private fun createFakeOverride( is PropertyDescriptor -> IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, descriptor).apply { // TODO: add field if getter is missing? - getter = descriptor.getter?.createFunction() as IrSimpleFunction? - setter = descriptor.setter?.createFunction() as IrSimpleFunction? + getter = descriptor.getter?.createFunction() + setter = descriptor.setter?.createFunction() } else -> TODO(descriptor.toString()) } @@ -238,7 +243,7 @@ fun IrSimpleFunction.setOverrides(symbolTable: ReferenceSymbolTable) { fun IrClass.simpleFunctions(): List = this.declarations.flatMap { when (it) { is IrSimpleFunction -> listOf(it) - is IrProperty -> listOfNotNull(it.getter as IrSimpleFunction?, it.setter as IrSimpleFunction?) + is IrProperty -> listOfNotNull(it.getter, it.setter) else -> emptyList() } } diff --git a/backend.native/tests/runtime/memory/escape2.kt b/backend.native/tests/runtime/memory/escape2.kt index a2d7ff66eff..09ef6b03d02 100644 --- a/backend.native/tests/runtime/memory/escape2.kt +++ b/backend.native/tests/runtime/memory/escape2.kt @@ -19,6 +19,7 @@ fun bar(b: B) { foo(c) } +@ThreadLocal val global = B() @Test fun runTest() { diff --git a/backend.native/tests/runtime/workers/freeze_stress.kt b/backend.native/tests/runtime/workers/freeze_stress.kt index a2e26347ab0..9932bf2de92 100644 --- a/backend.native/tests/runtime/workers/freeze_stress.kt +++ b/backend.native/tests/runtime/workers/freeze_stress.kt @@ -68,6 +68,7 @@ class Graph(val nodes: List, val roots: List) fun min(x: Int, y: Int) = if (x < y) x else y fun max(x: Int, y: Int) = if (x > y) x else y +@ThreadLocal val random = Random(42) fun generate(condensationSize: Int, branchingFactor: Int, swellingFactor: Int): Graph { diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 629a8edd302..7a832793466 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -1667,7 +1667,7 @@ void freezeCyclic(ContainerHeader* rootContainer, const KStdVectorcontainer(); diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index ac843bd242f..e8d3d8f167d 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -39,8 +39,9 @@ InitNode* initTailNode = nullptr; enum { INIT_GLOBALS = 0, - DEINIT_THREAD_LOCAL_GLOBALS = 1, - DEINIT_GLOBALS = 2 + INIT_THREAD_LOCAL_GLOBALS = 1, + DEINIT_THREAD_LOCAL_GLOBALS = 2, + DEINIT_GLOBALS = 3 }; enum { @@ -78,10 +79,13 @@ RuntimeState* initRuntime() { RuntimeState* result = konanConstructInstance(); if (!result) return nullptr; result->memoryState = InitMemory(); + bool firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1; // Keep global variables in state as well. - InitOrDeinitGlobalVariables(INIT_GLOBALS); - konan::consoleInit(); - atomicAdd(&aliveRuntimesCount, 1); + if (firstRuntime) { + konan::consoleInit(); + InitOrDeinitGlobalVariables(INIT_GLOBALS); + } + InitOrDeinitGlobalVariables(INIT_THREAD_LOCAL_GLOBALS); return result; } diff --git a/runtime/src/main/kotlin/kotlin/native/test/Launcher.kt b/runtime/src/main/kotlin/kotlin/native/test/Launcher.kt index 170714e58c2..5c5679b6ee9 100644 --- a/runtime/src/main/kotlin/kotlin/native/test/Launcher.kt +++ b/runtime/src/main/kotlin/kotlin/native/test/Launcher.kt @@ -18,6 +18,7 @@ package kotlin.native.test import kotlin.system.exitProcess +@ThreadLocal private val _generatedSuites = mutableListOf() internal fun registerSuite(suite: TestSuite): Unit { diff --git a/runtime/src/main/kotlin/kotlin/native/worker/Atomics.kt b/runtime/src/main/kotlin/kotlin/native/worker/Atomics.kt index 7b3d3c3c033..3041edc9b7d 100644 --- a/runtime/src/main/kotlin/kotlin/native/worker/Atomics.kt +++ b/runtime/src/main/kotlin/kotlin/native/worker/Atomics.kt @@ -173,9 +173,19 @@ class AtomicReference(private var value: T? = null) { external public fun get(): T? } -internal object UNINITIALIZED +internal object UNINITIALIZED { + // So that single-threaded configs can use those as well. + init { + freeze() + } +} -internal object INITIALIZING +internal object INITIALIZING { + // So that single-threaded configs can use those as well. + init { + freeze() + } +} @Frozen internal class AtomicLazyImpl(initializer: () -> T) : Lazy {