diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index 596077c5149..573d795b437 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.llvm.* +import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -257,7 +258,13 @@ private class ExportedElement(val kind: ElementKind, // Produce instance getter if needed. if (isSingletonObject) { generateFunction(owner.codegen, owner.kGetObjectFuncType, "${cname}_instance") { - val value = getObjectValue(irClass, ExceptionHandler.Caller, null) + val value = call( + owner.codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)), + emptyList(), + Lifetime.GLOBAL, + ExceptionHandler.Caller, + false, + returnSlot) ret(value) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 0f9c1e9c26c..0067fd38393 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.konan.library.KonanLibraryLayout import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.KonanTarget -import org.jetbrains.kotlin.konan.target.needSmallBinary import org.jetbrains.kotlin.library.SerializedIrModule import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.name.FqName @@ -50,15 +49,13 @@ internal class NativeMapping : DefaultMapping() { data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections) val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping() - val enumImplObjects = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val enumEntriesMaps = mutableMapOf>() val bridges = mutableMapOf() val notLoweredInlineFunctions = mutableMapOf() - val companionObjectCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val outerThisCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val lateinitPropertyCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() - val enumValuesCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + val objectInstanceGetter = DefaultDelegateFactory.newDeclarationToDeclarationMapping() } internal class Context(config: KonanConfig) : KonanBackendContext(config), ConfigChecks { @@ -95,8 +92,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config), Confi val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) } val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) } val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) } - val enumsSupport by lazy { EnumsSupport(mapping, ir.symbols, irBuiltIns, irFactory) } - val cachesAbiSupport by lazy { CachesAbiSupport(mapping, ir.symbols, irFactory) } + val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) } + val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) } open class LazyMember(val initializer: Context.() -> T) { operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index 25c7e3d47e6..b95ab5b9bc3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -250,6 +250,12 @@ internal val initializersPhase = makeKonanFileLoweringPhase( prerequisite = setOf(enumConstructorsPhase) ) +internal val objectClassesPhase = makeKonanFileLoweringPhase( + ::ObjectClassLowering, + name = "ObjectClasses", + description = "Enum constructors lowering" +) + internal val localFunctionsPhase = makeKonanFileOpPhase( op = { context, irFile -> LocalDelegatedPropertiesLowering().lower(irFile) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index e0c82a911ac..154826cf18f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -292,6 +292,7 @@ internal val allLoweringsPhase = SameTypeNamedCompilerPhase( coroutinesPhase, typeOperatorPhase, expressionBodyTransformPhase, + objectClassesPhase, constantInliningPhase, staticInitializersPhase, bridgesPhase, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt index 29f34f9b185..7e55df8e8f7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt @@ -1387,7 +1387,7 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing { } override fun CCallbackBuilder.receiveValue(): IrExpression { - return bridgeBuilder.kotlinIrBuilder.irGetObject(irBuiltIns.unitClass) + return bridgeBuilder.kotlinIrBuilder.irCall(symbols.theUnitInstance) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 20bfd347bf8..273b64d1d0a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -498,8 +498,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { } return declarations.mapNotNull { when (it) { - is IrField -> it.takeIf { it.isReal }?.toFieldInfo() - is IrProperty -> it.takeIf { it.isReal }?.backingField?.toFieldInfo() + is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo() + is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo() else -> null } } 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 d169f4b1bd6..f89e234c095 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 @@ -232,6 +232,8 @@ internal class KonanSymbols( val reinterpret = internalFunction("reinterpret") + val theUnitInstance = internalFunction("theUnitInstance") + val ieee754Equals = internalFunctions("ieee754Equals") val equals = irBuiltIns.findBuiltInClassMemberFunctions(any, Name.identifier("equals")).single() @@ -313,8 +315,6 @@ internal class KonanSymbols( val initInstance = internalFunction("initInstance") - val freeze = irBuiltIns.findFunctions(Name.identifier("freeze"), "kotlin", "native", "concurrent").single() - val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io") .single { it.descriptor.valueParameters.singleOrNull()?.type == (irBuiltIns as IrBuiltInsOverDescriptors).builtIns.stringType } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 0d4b1d0d28f..be09544fa72 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -23,28 +23,6 @@ import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.konan.ForeignExceptionMode -private fun IrConstructor.isAutogeneratedSimpleConstructor(context: Context): Boolean { - if (!this.isPrimary) return false - val statements = this.body?.statements ?: return false - if (statements.size < 2) return false - val lastStatement = statements.last() - if (lastStatement !is IrReturn || - (lastStatement.value as? IrGetObjectValue)?.symbol != context.irBuiltIns.unitClass) return false - val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false - val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false - if (!constructor.constructedClass.isAny()) return false - return statements.asSequence().take(statements.size - 1).drop(1).all { - it is IrBlock && it.origin == IrStatementOrigin.INITIALIZE_FIELD - } -} - -// TODO: shall we memoize that property? -internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean { - if (!context.shouldOptimize()) return false - if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true - val fields = context.getLayoutBuilder(this).fields - return fields.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor(context) } -} internal class CodeGenerator(override val context: Context) : ContextUtils { fun llvmFunction(function: IrFunction): LlvmCallable = @@ -1260,109 +1238,12 @@ internal abstract class FunctionGenerationContext( return null } + @Suppress("UNUSED_PARAMETER") fun getObjectValue(irClass: IrClass, exceptionHandler: ExceptionHandler, - startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null, - resultSlot: LLVMValueRef? = null + startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null, + resultSlot: LLVMValueRef? = null ): LLVMValueRef { - // TODO: could be processed the same way as other stateless objects. - if (irClass.isUnit()) { - return codegen.theUnitInstanceRef.llvm - } - - if (irClass.isCompanion) { - val parent = irClass.parent as IrClass - if (parent.isObjCClass()) { - // TODO: cache it too. - return call( - codegen.llvmFunction(context.ir.symbols.interopInterpretObjCPointer.owner), - listOf(getObjCClass(parent, exceptionHandler)), - Lifetime.GLOBAL, - exceptionHandler - ) - } - } - - val storageKind = irClass.storageKind(context) - - val objectPtr = if (isExternal(irClass)) { - when (storageKind) { - // If thread local object is imported - access it via getter function. - ObjectStorageKind.THREAD_LOCAL -> { - val valueGetterName = irClass.threadLocalObjectStorageGetterSymbolName - val valueGetterFunction = LLVMGetNamedFunction(llvm.module, valueGetterName) - ?: addLlvmFunctionWithDefaultAttributes( - context, - llvm.module, - valueGetterName, - functionType(kObjHeaderPtrPtr, false) - ) - call(valueGetterFunction, - listOf(), - resultLifetime = Lifetime.GLOBAL, - exceptionHandler = exceptionHandler) - } - - // If global object is imported - import it's storage directly. - ObjectStorageKind.PERMANENT, ObjectStorageKind.SHARED -> { - val llvmType = irClass.defaultType.toLLVMType(llvm) - importGlobal( - irClass.globalObjectStorageSymbolName, - llvmType, - origin = irClass.llvmSymbolOrigin - ) - } - } - } else { - // Local globals and thread locals storage info is stored in our map. - val singleton = llvmDeclarations.forSingleton(irClass) - val instanceAddress = singleton.instanceStorage - instanceAddress.getAddress(this) - } - - when (storageKind) { - ObjectStorageKind.SHARED -> - // If current file used a shared object, make file's (de)initializer function deinit it. - llvm.globalSharedObjects += objectPtr - ObjectStorageKind.THREAD_LOCAL -> - // If current file used locally defined TLS objects, make file's (de)initializer function - // init and deinit TLS. - // Note: for exported TLS objects a getter is generated in a file they're defined in. Which - // adds TLS init and deinit to that file's (de)initializer function. - if (!isExternal(irClass)) - llvm.fileUsesThreadLocalObjects = true - ObjectStorageKind.PERMANENT -> { /* Do nothing, no need to free such an instance. */ } - } - - if (storageKind == ObjectStorageKind.PERMANENT) { - return loadSlot(objectPtr, false) - } - val bbInit = basicBlock("label_init", startLocationInfo, endLocationInfo) - val bbExit = basicBlock("label_continue", startLocationInfo, endLocationInfo) - val objectVal = loadSlot(objectPtr, false, memoryOrder = LLVMAtomicOrdering.LLVMAtomicOrderingAcquire) - val objectInitialized = icmpUGt(ptrToInt(objectVal, codegen.intPtrType), codegen.immOneIntPtrType) - val bbCurrent = currentBlock - condBr(objectInitialized, bbExit, bbInit) - - positionAtEnd(bbInit) - val typeInfo = codegen.typeInfoForAllocation(irClass) - val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 } - val ctor = codegen.llvmFunction(defaultConstructor).llvmValue - val initFunction = - if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) { - llvm.initSingletonFunction - } else { - llvm.initThreadLocalSingleton - } - val args = listOf(objectPtr, typeInfo, ctor) - val newValue = call(initFunction, args, Lifetime.GLOBAL, exceptionHandler, resultSlot = resultSlot) - val bbInitResult = currentBlock - br(bbExit) - - positionAtEnd(bbExit) - val valuePhi = phi(irClass.defaultType.toLLVMType(llvm)) - addPhiIncoming(valuePhi, bbCurrent to objectVal, bbInitResult to newValue) - - return valuePhi + error("Should be lowered out: ${irClass.render()} while generating ${irFunction?.dump()}") } /** diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 7484365468d..9b6a4b0983e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -481,8 +481,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R val allocInstanceFunction = importRtFunction("AllocInstance") val allocArrayFunction = importRtFunction("AllocArrayInstance") - val initThreadLocalSingleton = importRtFunction("InitThreadLocalSingleton") - val initSingletonFunction = importRtFunction("InitSingleton") val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal") val updateHeapRefFunction = importRtFunction("UpdateHeapRef") val updateStackRefFunction = importRtFunction("UpdateStackRef") @@ -564,7 +562,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R val compilerUsedGlobals = mutableListOf() val irStaticInitializers = mutableListOf() val otherStaticInitializers = mutableListOf() - var fileUsesThreadLocalObjects = false val globalSharedObjects = mutableSetOf() val initializersGenerationState = InitializersGenerationState() val boxCacheGlobals = mutableMapOf() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index e83e6acbbdc..3cddc3bfcc2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -66,6 +66,7 @@ internal enum class IntrinsicType { IMMUTABLE_BLOB, INIT_INSTANCE, IS_EXPERIMENTAL_MM, + THE_UNIT_INSTANCE, // Enums ENUM_VALUES, ENUM_VALUE_OF, @@ -241,6 +242,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv IntrinsicType.IDENTITY -> emitIdentity(args) IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args) IntrinsicType.IS_EXPERIMENTAL_MM -> emitIsExperimentalMM() + IntrinsicType.THE_UNIT_INSTANCE -> theUnitInstanceRef.llvm IntrinsicType.GET_CONTINUATION, IntrinsicType.RETURN_IF_SUSPENDED, IntrinsicType.INTEROP_BITS_TO_FLOAT, 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 e09213922b2..182d3359b06 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 @@ -46,12 +46,6 @@ internal enum class FieldStorageKind { THREAD_LOCAL } -internal enum class ObjectStorageKind { - PERMANENT, - THREAD_LOCAL, - SHARED -} - // TODO: maybe unannotated singleton objects shall be accessed from main thread only as well? internal fun IrField.storageKind(context: Context): FieldStorageKind { // TODO: Is this correct? @@ -77,12 +71,6 @@ internal fun IrField.needsGCRegistration(context: Context) = (hasNonConstInitializer || // which are initialized from heap object !isFinal) // or are not final -internal fun IrClass.storageKind(context: Context): ObjectStorageKind = when { - this.annotations.hasAnnotation(KonanFqNames.threadLocal) && - context.config.threadsAreAllowed -> ObjectStorageKind.THREAD_LOCAL - this.hasConstStateAndNoSideEffects(context) -> ObjectStorageKind.PERMANENT - else -> ObjectStorageKind.SHARED -} internal fun IrField.isGlobalNonPrimitive(context: Context) = when { type.computePrimitiveBinaryTypeOrNull() != null -> false @@ -883,44 +871,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map { - val fields = context.getLayoutBuilder(declaration).fields - return Array(fields.size) { index -> - val initializer = fields[index].irField!!.initializer!!.expression as IrConst<*> - evaluateConst(initializer) - } } override fun visitProperty(declaration: IrProperty) { @@ -987,7 +937,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map return evaluateVararg (value) is IrBreak -> return evaluateBreak (value) is IrContinue -> return evaluateContinue (value) - is IrGetObjectValue -> return evaluateGetObjectValue (value, resultSlot) + is IrGetObjectValue -> return evaluateGetObjectValue (value) is IrFunctionReference -> return evaluateFunctionReference (value) is IrSuspendableExpression -> return evaluateSuspendableExpression (value, resultSlot) @@ -1012,14 +962,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map - val index = value.constructor.owner.valueParameters - .indexOfFirst { it.name.toString() == field.name } - .takeIf { it >= 0 } - ?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}") - evaluateConstantValue(value.valueArguments[index]) + val fields = context.getLayoutBuilder(constructedClass).fields + val valueParameters = value.constructor.owner.valueParameters.associateBy { it.name.toString() } + fields.map { field -> + if (field.isConst) { + val init = field.irField!!.initializer?.expression + require(field.name !in valueParameters) { + "Constant field ${field.name} of class ${constructedClass.name} shouldn't be a constructor parameter" + } + when (init) { + is IrConst<*> -> evaluateConst(init) + is IrConstantValue -> evaluateConstantValue(init) + null -> error("Constant field ${field.name} of class ${constructedClass.name} should have initializer") + else -> error("Unexpected constant initializer type: ${init::class}") + } + } else { + val index = valueParameters[field.name]?.index + ?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}") + evaluateConstantValue(value.valueArguments[index]) + } }.also { - require(it.size == value.valueArguments.size) { "Bad statically initialized object: too many fields" } + require(it.size == value.valueArguments.size + fields.count { it.isConst }) { + "Bad statically initialized object of class ${constructedClass.name}: too many fields" + } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 3b595c3a764..2a2a240b6f9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -52,9 +52,6 @@ internal class LlvmDeclarations(private val unique: Map - val associatedObjectGetter = generateFunction( - CodeGenerator(context), - functionType(kObjHeaderPtr, false, kObjHeaderPtrPtr), - "" - ) { - ret(getObjectValue(value, ExceptionHandler.Caller, startLocationInfo = null)) - }.also { - LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage) - } + val function = context.getObjectClassInstanceFunction(value) + val llvmFunction = context.generationState.llvmDeclarations.forFunction(function).llvmValue - Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(associatedObjectGetter)) + Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(llvmFunction)) } return staticData.placeGlobalConstArray( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 777083f147d..09defe0413b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator +import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract import org.jetbrains.kotlin.descriptors.ClassKind @@ -995,7 +996,8 @@ private fun ObjCExportCodeGenerator.generateObjCImp( target: IrFunction?, baseMethod: IrFunction, methodBridge: MethodBridge, - isVirtual: Boolean = false + isVirtual: Boolean = false, + customBridgeSuffix: String? = null, ) = if (target == null) { generateAbstractObjCImp(methodBridge, baseMethod) } else { @@ -1003,7 +1005,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp( methodBridge, isDirect = !isVirtual, baseMethod = baseMethod, - bridgeSuffix = (if (isVirtual) "virtual_" else "") + target.computeSymbolName() + bridgeSuffix = customBridgeSuffix ?: ((if (isVirtual) "virtual_" else "") + target.computeSymbolName()) ) { args, resultLifetime, exceptionHandler -> if (target is IrConstructor && target.constructedClass.isAbstract()) { callFromBridge( @@ -1913,12 +1915,20 @@ private fun ObjCExportCodeGenerator.createObjectInstanceAdapter( ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { assert(objectClass.kind == ClassKind.OBJECT) assert(!objectClass.isUnit()) - val bridgeSuffix = "${owner.computeTypeInfoSymbolName()}#$selector" - return generateObjCToKotlinSyntheticGetter(selector, bridgeSuffix) { - initRuntimeIfNeeded() // For instance methods it gets called when allocating. - val value = getObjectValue(objectClass, startLocationInfo = null, exceptionHandler = ExceptionHandler.Caller) - autoreleaseAndRet(kotlinReferenceToRetainedObjC(value)) - } + + val methodBridge = MethodBridge( + returnBridge = MethodBridge.ReturnValue.Mapped(ReferenceBridge), + receiver = MethodBridgeReceiver.Static, + valueParameters = emptyList() + ) + + val function = context.getObjectClassInstanceFunction(objectClass) + val imp = generateObjCImp( + function, function, methodBridge, + isVirtual = false, + customBridgeSuffix = "${owner.computeTypeInfoSymbolName()}#$selector") + + return objCToKotlinMethodAdapter(selector, methodBridge, imp) } private fun ObjCExportCodeGenerator.createEnumEntryAdapter( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index cfde0e13135..25c831fdc9b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -239,7 +239,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT val field = expression.symbol.owner val parentClass = field.parentClassOrNull - return if (parentClass == null || !parentClass.isInlined()) + return if (parentClass == null || !parentClass.isInlined() || field.isStatic) expression else { builder.at(expression) @@ -254,7 +254,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT override fun visitSetField(expression: IrSetField): IrExpression { super.visitSetField(expression) - return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true) { + return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true && !expression.symbol.owner.isStatic) { // Happens in one of the cases: // 1. In primary constructor of the inlined class. Makes no sense, "has no effect", can be removed. // The constructor will be lowered and used. @@ -442,7 +442,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT lateinit var thisVar: IrValueDeclaration fun IrBuilderWithScope.genReturnValue(): IrExpression = if (irConstructor.isPrimary) { - irGetObject(irBuiltIns.unitClass) + irCall(symbols.theUnitInstance) } else { irGet(thisVar) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt index 62d9f91ea72..a09e55d87fc 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CachesAbiLowering.kt @@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.NativeMapping import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName -import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin -import org.jetbrains.kotlin.backend.konan.isObjCClass import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.* @@ -22,8 +20,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetField -import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue -import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name @@ -39,26 +35,11 @@ internal object INTERNAL_ABI_ORIGIN : IrDeclarationOriginImpl("INTERNAL_ABI") * In case of compiler caches, this means that it is not accessible as Lazy IR * and we have to explicitly add an external declaration. */ -internal class CachesAbiSupport(mapping: NativeMapping, symbols: KonanSymbols, private val irFactory: IrFactory) { - private val companionObjectAccessors = mapping.companionObjectCacheAccessors +internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: IrFactory) { private val outerThisAccessors = mapping.outerThisCacheAccessors private val lateinitPropertyAccessors = mapping.lateinitPropertyCacheAccessors - private val enumValuesAccessors = mapping.enumValuesCacheAccessors private val lateInitFieldToNullableField = mapping.lateInitFieldToNullableField - private val array = symbols.array - fun getCompanionObjectAccessor(irClass: IrClass): IrSimpleFunction { - require(irClass.isCompanion) { "Expected a companion object but was: ${irClass.render()}" } - return companionObjectAccessors.getOrPut(irClass) { - irFactory.buildFun { - name = getMangledNameFor("globalAccessor", irClass) - origin = INTERNAL_ABI_ORIGIN - returnType = irClass.defaultType - }.apply { - parent = irClass.getPackageFragment() - } - } - } fun getOuterThisAccessor(irClass: IrClass): IrSimpleFunction { require(irClass.isInner) { "Expected an inner class but was: ${irClass.render()}" } @@ -103,19 +84,6 @@ internal class CachesAbiSupport(mapping: NativeMapping, symbols: KonanSymbols, p } } - fun getEnumValuesAccessor(irClass: IrClass): IrSimpleFunction { - require(irClass.isEnumClass) { "Expected a enum class but was: ${irClass.render()}" } - return enumValuesAccessors.getOrPut(irClass) { - irFactory.buildFun { - name = getMangledNameFor("getValues", irClass) - returnType = array.typeWith(irClass.defaultType) - origin = INTERNAL_ABI_ORIGIN - }.apply { - parent = irClass.getPackageFragment() - } - } - } - /** * Generate name for declaration that will be a part of internal ABI. */ @@ -143,15 +111,6 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass, if (declaration.isLocal) return - if (declaration.isCompanion) { - val function = cachesAbiSupport.getCompanionObjectAccessor(declaration) - context.createIrBuilder(function.symbol).apply { - function.body = irBlockBody { - +irReturn(irGetObjectValue(declaration.defaultType, declaration.symbol)) - } - } - data.add(function) - } if (declaration.isInner) { val function = cachesAbiSupport.getOuterThisAccessor(declaration) @@ -165,16 +124,6 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass, } data.add(function) } - - if (declaration.isEnumClass) { - val function = cachesAbiSupport.getEnumValuesAccessor(declaration) - context.createIrBuilder(function.symbol).run { - function.body = irBlockBody { - +irReturn(with(this@ExportCachesAbiVisitor.context.enumsSupport) { irGetValuesField(declaration) }) - } - } - data.add(function) - } } override fun visitProperty(declaration: IrProperty, data: MutableList) { @@ -198,29 +147,12 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass, internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPass, IrElementTransformerVoid() { private val cachesAbiSupport = context.cachesAbiSupport - private val enumsSupport = context.enumsSupport private val llvmImports = context.generationState.llvmImports override fun lower(irFile: IrFile) { irFile.transformChildrenVoid(this) } - override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { - expression.transformChildrenVoid(this) - - val irClass = expression.symbol.owner - if (!irClass.isCompanion || context.llvmModuleSpecification.containsDeclaration(irClass)) { - return expression - } - val parent = irClass.parentAsClass - if (parent.isObjCClass()) { - // Access to Obj-C metaclass is done via intrinsic. - return expression - } - val accessor = cachesAbiSupport.getCompanionObjectAccessor(irClass) - llvmImports.add(irClass.llvmSymbolOrigin) - return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()) - } override fun visitGetField(expression: IrGetField): IrExpression { expression.transformChildrenVoid(this) @@ -249,17 +181,6 @@ internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPa } } - field.origin == DECLARATION_ORIGIN_ENUM -> { - val enumClass = irClass?.parentClassOrNull - require(enumClass != null) { "Unexpected usage of enum VALUES field" } - require(enumClass.isEnumClass) { "Expected a enum class: ${enumClass.render()}" } - require(enumsSupport.getImplObject(enumClass) == irClass) { "Expected a enum's impl object: ${irClass.render()}" } - require(field == enumsSupport.getValuesField(irClass)) { "Expected VALUES field: ${field.render()}" } - val accessor = cachesAbiSupport.getEnumValuesAccessor(enumClass) - llvmImports.add(enumClass.llvmSymbolOrigin) - return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()) - } - else -> expression } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index d6f2bcdfc6a..cae0aa619da 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.konan.lower +import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.getOrPut import org.jetbrains.kotlin.backend.common.lower.EnumWhenLowering @@ -12,11 +13,10 @@ import org.jetbrains.kotlin.backend.common.lower.at import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.MemoryModel import org.jetbrains.kotlin.backend.konan.NativeMapping import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.ir.KonanNameConventions -import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols +import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType import org.jetbrains.kotlin.descriptors.ClassKind @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter -import org.jetbrains.kotlin.ir.builders.declarations.buildClass import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* @@ -44,17 +43,29 @@ internal data class LoweredEnumEntryDescription(val ordinal: Int, val getterId: internal class EnumsSupport( mapping: NativeMapping, - symbols: KonanSymbols, private val irBuiltIns: IrBuiltIns, private val irFactory: IrFactory, ) { - private val enumImplObjects = mapping.enumImplObjects private val enumValueGetters = mapping.enumValueGetters private val enumEntriesMaps = mapping.enumEntriesMaps - private val array = symbols.array - private val enumEntries = symbols.enumEntriesInterface - private val genericValueOfSymbol = symbols.valueOfForEnum - private val genericValuesSymbol = symbols.valuesForEnum + + private val valuesFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + private val valueOfFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + + fun getValuesFunction(enumClass: IrClass) = valuesFunctions.getOrPut(enumClass) { + enumClass.simpleFunctions().single { + it.name == valuesFunctionName && it.dispatchReceiverParameter == null + } + } + fun saveValuesFunction(enumClass: IrClass) { getValuesFunction(enumClass) } + + fun getValueOfFunction(enumClass: IrClass) = valueOfFunctions.getOrPut(enumClass) { + enumClass.simpleFunctions().single { + it.name == valueOfFunctionName && it.dispatchReceiverParameter == null + } + } + fun saveValueOfFunction(enumClass: IrClass) { getValueOfFunction(enumClass) } + fun enumEntriesMap(enumClass: IrClass): Map { require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } @@ -70,41 +81,6 @@ internal class EnumsSupport( } } - fun getImplObject(enumClass: IrClass): IrClass { - require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } - return enumImplObjects.getOrPut(enumClass) { - irFactory.buildClass { - startOffset = enumClass.startOffset - endOffset = enumClass.endOffset - origin = DECLARATION_ORIGIN_ENUM - name = implObjectName - kind = ClassKind.OBJECT - }.apply { - superTypes = listOf(irBuiltIns.anyType) - parent = enumClass - - addChild(irFactory.buildField { - startOffset = enumClass.startOffset - endOffset = enumClass.endOffset - origin = DECLARATION_ORIGIN_ENUM - name = valuesFieldName - type = array.typeWith(enumClass.defaultType) - visibility = DescriptorVisibilities.PRIVATE - isFinal = true - }) - addChild(irFactory.buildField { - startOffset = enumClass.startOffset - endOffset = enumClass.endOffset - origin = DECLARATION_ORIGIN_ENUM - name = entriesFieldName - type = enumEntries.typeWith(enumClass.defaultType) - visibility = DescriptorVisibilities.PRIVATE - isFinal = true - }) - } - } - } - fun getValueGetter(enumClass: IrClass): IrFunction { require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } return enumValueGetters.getOrPut(enumClass) { @@ -126,36 +102,9 @@ internal class EnumsSupport( } } - fun getValuesField(implObject: IrClass) = implObject.fields.single { it.name == valuesFieldName } - fun getEntriesField(implObject: IrClass) = implObject.fields.single { it.name == entriesFieldName } - - fun IrBuilderWithScope.irGetValuesField(enumClass: IrClass): IrExpression { - val implObject = getImplObject(enumClass) - val valuesField = getValuesField(implObject) - return irGetField(irGetObject(implObject.symbol), valuesField) - } - - fun IrBuilderWithScope.irEnumValues(enumClass: IrClass) : IrExpression = - irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply { - putValueArgument(0, irGetValuesField(enumClass)) - } - - fun IrBuilderWithScope.irEnumValueOf(enumClass: IrClass, value: IrExpression) : IrExpression = - irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply { - putValueArgument(0, value) - putValueArgument(1, irGetValuesField(enumClass)) - } - - fun IrBuilderWithScope.irEnumEntries(enumClass: IrClass) : IrExpression { - val implObject = getImplObject(enumClass) - val entriesField = getEntriesField(implObject) - return irGetField(irGetObject(implObject.symbol), entriesField) - } - companion object { - val valuesFieldName = "VALUES".synthesizedName - val entriesFieldName = "ENTRIES".synthesizedName - val implObjectName = "OBJECT".synthesizedName + val valuesFunctionName = Name.identifier("values") + val valueOfFunctionName = Name.identifier("valueOf") } } @@ -170,8 +119,6 @@ internal class NativeEnumWhenLowering constructor(context: Context) : EnumWhenLo internal class EnumUsageLowering(val context: Context) : IrElementTransformer, FileLoweringPass { private val enumsSupport = context.enumsSupport - private val symbols = context.ir.symbols - private val arrayGet = symbols.arrayGet[symbols.array]!! override fun lower(irFile: IrFile) { visitFile(irFile, data = null) @@ -206,19 +153,21 @@ internal class EnumUsageLowering(val context: Context) : IrElementTransformer data.irCall(enumsSupport.getValuesFunction(irClass)) + IntrinsicType.ENUM_VALUE_OF -> data.irCall(enumsSupport.getValueOfFunction(irClass)).apply { + putValueArgument(0, expression.getValueArgument(0)!!) + } + else -> TODO("Unsupported intrinsic type ${intrinsicType}") + } + } + + private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) = with (enumsSupport) { + irCall(getValueGetter(enumClass).symbol, enumClass.defaultType).apply { + putValueArgument(0, irInt(enumEntriesMap(enumClass).getValue(name).getterId)) } } - private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) = - irCall(arrayGet, enumClass.defaultType).apply { - dispatchReceiver = with(enumsSupport) { irGetValuesField(enumClass) } - putValueArgument(0, irInt(enumsSupport.enumEntriesMap(enumClass).getValue(name).getterId)) - } } internal class EnumClassLowering(val context: Context) : FileLoweringPass { @@ -228,7 +177,6 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { private val createEnumEntries = symbols.createEnumEntries private val initInstance = symbols.initInstance private val arrayGet = symbols.array.owner.functions.single { it.name == KonanNameConventions.getWithoutBoundCheck }.symbol - private val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first() override fun lower(irFile: IrFile) { irFile.transformChildrenVoid(object : IrElementTransformerVoid() { @@ -242,14 +190,37 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { } private inner class EnumClassTransformer(val irClass: IrClass) { - private val implObject = enumsSupport.getImplObject(irClass) - private val valuesField = enumsSupport.getValuesField(implObject) - private val entriesField = enumsSupport.getEntriesField(implObject) + private val valuesField = context.irFactory.buildField { + startOffset = irClass.startOffset + endOffset = irClass.endOffset + origin = DECLARATION_ORIGIN_ENUM + name = "VALUES".synthesizedName + type = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType) + visibility = DescriptorVisibilities.PRIVATE + isFinal = true + isStatic = true + } + private val entriesField = context.irFactory.buildField { + startOffset = irClass.startOffset + endOffset = irClass.endOffset + origin = DECLARATION_ORIGIN_ENUM + name = "ENTRIES".synthesizedName + type = symbols.enumEntriesInterface.typeWith(irClass.defaultType) + visibility = DescriptorVisibilities.PRIVATE + isFinal = true + isStatic = true + } + + // also saves this in enumSupport before removing them from list private val enumEntriesMap = enumsSupport.enumEntriesMap(irClass) + fun run() { val enumEntries = transformEnumBody() - defineImplObject(enumEntries) + // These fields are inserted into begining to be initialized before companion object + // The values field should be initialized first, so we need to insert entries field first + defineEntriesField() + defineValuesField(enumEntries) defineValueGetter() } @@ -263,23 +234,31 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { declaration.correspondingClass = null listOfNotNull(correspondingClass) } + else -> null } } + enumsSupport.saveValuesFunction(irClass) + enumsSupport.saveValueOfFunction(irClass) irClass.simpleFunctions().forEach { declaration -> val body = declaration.body if (body is IrSyntheticBody) { declaration.body = when (body.kind) { IrSyntheticBodyKind.ENUM_VALUEOF -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) { - +irReturn(with(enumsSupport) { irEnumValueOf(irClass, irGet(declaration.valueParameters[0])) }) + +irReturn(irCall(symbols.valueOfForEnum, listOf(irClass.defaultType)).apply { + putValueArgument(0, irGet(declaration.valueParameters[0])) + putValueArgument(1, irGetField(null, valuesField)) + }) } IrSyntheticBodyKind.ENUM_VALUES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) { - +irReturn(with(enumsSupport) { irEnumValues(irClass) }) + +irReturn(irCall(symbols.valuesForEnum, listOf(irClass.defaultType)).apply { + putValueArgument(0, irGetField(null, valuesField)) + }) } IrSyntheticBodyKind.ENUM_ENTRIES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) { - +irReturn(with(enumsSupport) { irEnumEntries(irClass) }) + +irReturn(irGetField(null, entriesField)) } } } @@ -292,7 +271,7 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { context.createIrBuilder(valueGetter.symbol).run { valueGetter.body = irBlockBody(valueGetter) { +irReturn(irCall(arrayGet, irClass.defaultType).apply { - dispatchReceiver = with(enumsSupport) { irGetValuesField(irClass) } + dispatchReceiver = irGetField(null, valuesField) putValueArgument(0, irGet(valueGetter.valueParameters[0])) }) } @@ -300,49 +279,18 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { irClass.declarations.add(valueGetter) } - private fun defineImplObject(enumEntries: List) { - implObject.createParameterDeclarations() - - implObject.addSimpleDelegatingConstructor(constructorOfAny, context.irBuiltIns, true /* TODO: why primary? */) - implObject.addFakeOverrides(context.typeSystem) - irClass.declarations.add(implObject) - - ImplConstructorBuilder(implObject.constructors.single()).build(enumEntries) - } - - private inner class ImplConstructorBuilder(val constructor: IrConstructor) { - private val irBuilder = context.createIrBuilder(constructor.symbol, constructor.startOffset, constructor.endOffset) - - fun build(enumEntries: List) { - val statements = (constructor.body as IrBlockBody).statements - - // The initialization order is the following: - // - first all enum entries in their declaration order - // - then companion object if it exists - statements += buildValuesFieldInitializer(enumEntries) - // Split allocation and constructors calling because enum entries can reference one another. - statements += callEnumEntriesConstructors(enumEntries) - statements += buildEntriesFieldInitializer() - - // Needed for legacy MM targets that do not support threads. - if (this@EnumClassLowering.context.memoryModel != MemoryModel.EXPERIMENTAL) { - statements += irBuilder.irBlock { - irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(valuesField.type)).apply { - extensionReceiver = irGet(implObject.thisReceiver!!) - } - } + private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall = + irCall(initInstance).apply { + putValueArgument(0, instance) + putValueArgument(1, constructor) } - irClass.companionObject()?.let { statements += irBuilder.irGetObject(it.symbol) } - } + private fun defineValuesField(enumEntries: List) { + irClass.declarations.add(0, valuesField) + valuesField.parent = irClass + val irBuilder = context.createIrBuilder(valuesField.symbol, irClass.startOffset, irClass.endOffset) - private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall = - irCall(initInstance).apply { - putValueArgument(0, instance) - putValueArgument(1, constructor) - } - - private fun buildValuesFieldInitializer(enumEntries: List) = irBuilder.run { + valuesField.initializer = irBuilder.irExprBody(irBuilder.irBlock { val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression( startOffset, endOffset, irClass.defaultType, @@ -363,11 +311,22 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { irCall(createUninitializedInstance, listOf(entryClass.defaultType)) } ) - irSetField(irGet(implObject.thisReceiver!!), valuesField, irValuesInitializer) + val instances = irTemporary(irValuesInitializer) + +irSetField(null, valuesField, irGet(instances), origin = ObjectClassLowering.IrStatementOriginFieldPreInit) + callEnumEntriesConstructors(instances, enumEntries) + +irGet(instances) + }).also { + it.setDeclarationsParent(valuesField) } + valuesField.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner) + } - private fun buildEntriesFieldInitializer() = irBuilder.irBlock { - val irValuesArray = irTemporary(irGetField(irGet(implObject.thisReceiver!!), valuesField)) + fun defineEntriesField() { + irClass.declarations.add(0, entriesField) + entriesField.parent = irClass + val irBuilder = context.createIrBuilder(entriesField.symbol, irClass.startOffset, irClass.endOffset) + entriesField.initializer = irBuilder.irExprBody(irBuilder.irBlock { + val irValuesArray = irTemporary(irGetField(null, valuesField)) val irEntriesArray = this@EnumClassLowering.context.createArrayOfExpression( startOffset, endOffset, irClass.defaultType, @@ -380,34 +339,30 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { } } ) - val irEntriesInitializer = irCall(createEnumEntries, listOf(irClass.defaultType)).apply { + +irCall(createEnumEntries, listOf(irClass.defaultType)).apply { putValueArgument(0, irEntriesArray) } - +irSetField(irGet(implObject.thisReceiver!!), entriesField, irEntriesInitializer) - } + }) + } - private fun callEnumEntriesConstructors(enumEntries: List) = irBuilder.irBlock { - val receiver = implObject.thisReceiver!! - val instances = irTemporary(irGetField(irGet(receiver), valuesField)) - enumEntries.forEach { - val instance = irCall(arrayGet).apply { - dispatchReceiver = irGet(instances) - putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId)) + private fun IrBlockBuilder.callEnumEntriesConstructors(instances: IrVariable, enumEntries: List) { + enumEntries.forEach { + val instance = irCall(arrayGet).apply { + dispatchReceiver = irGet(instances) + putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId)) + } + val initializer = it.initializerExpression!!.expression + when { + initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer) + + initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> { + val statements = initializer.statements + val constructorCall = statements.last() as IrConstructorCall + statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall) + +initializer } - val initializer = it.initializerExpression!!.expression - initializer.setDeclarationsParent(constructor) - when { - initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer) - initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> { - val statements = initializer.statements - val constructorCall = statements.last() as IrConstructorCall - statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall) - +initializer - } - - else -> error("Unexpected initializer: $initializer") - } + else -> error("Unexpected initializer: $initializer") } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt new file mode 100644 index 00000000000..7b70714cd5a --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ObjectClassLowering.kt @@ -0,0 +1,174 @@ +/* + * Copyright 2010-2022 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.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.getOrPut +import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer +import org.jetbrains.kotlin.backend.common.lower.at +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation +import org.jetbrains.kotlin.backend.konan.ir.isAny +import org.jetbrains.kotlin.backend.konan.ir.isUnit +import org.jetbrains.kotlin.backend.konan.isObjCClass +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.addField +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.irCall +import org.jetbrains.kotlin.name.Name + +internal fun Context.getObjectClassInstanceFunction(clazz: IrClass) = mapping.objectInstanceGetter.getOrPut(clazz) { + when { + clazz.isUnit() -> ir.symbols.theUnitInstance.owner + clazz.isCompanion -> { + require((clazz.parent as? IrClass)?.isExternalObjCClass() != true) { "External objc Classes can't be used this way"} + irFactory.buildFun { + name = "companion".synthesizedName + returnType = clazz.defaultType + }.apply { + parent = clazz.parent + } + } + else -> { + irFactory.buildFun { + name = "instance".synthesizedName + returnType = clazz.defaultType + }.apply { + parent = clazz + } + } + } +} + +internal class ObjectClassLowering(val context: Context) : FileLoweringPass { + val symbols = context.ir.symbols + + override fun lower(irFile: IrFile) { + irFile.transform(object: IrBuildingTransformer(context) { + override fun visitClass(declaration: IrClass) : IrDeclaration { + super.visitClass(declaration) + if (declaration.isObject && !declaration.isCompanion && !declaration.isUnit()) { + processObjectClass( + declaration, + declaration, + instanceFieldName + ) + } + declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let { + processObjectClass( + it as IrClass, + declaration, + companionFieldName + ) + } + return declaration + } + + override fun visitGetObjectValue(expression: IrGetObjectValue) : IrExpression { + builder.at(expression) + val singleton = expression.symbol.owner + return if (singleton.isCompanion && (singleton.parent as IrClass).isExternalObjCClass()) + builder.irGetObjCClassCompanion(singleton) + else + builder.irCall(context.getObjectClassInstanceFunction(singleton)) + } + }, null) + } + + fun IrBuilderWithScope.irGetObjCClassCompanion(declaration: IrClass): IrExpression { + require(declaration.isCompanion && (declaration.parent as IrClass).isObjCClass()) + return irCall(symbols.interopInterpretObjCPointer, listOf(declaration.defaultType)).apply { + putValueArgument(0, irCall(symbols.interopGetObjCClass, listOf((declaration.parent as IrClass).defaultType))) + } + } + + fun processObjectClass( + declaration: IrClass, + classToAdd: IrClass, + fieldName: Name + ) { + val function = context.getObjectClassInstanceFunction(declaration) + classToAdd.declarations.add(function) + + val primaryConstructor = declaration.constructors.single { it.isPrimary } + require(primaryConstructor.valueParameters.isEmpty()) + val instanceField = classToAdd.addField { + name = fieldName + isFinal = true + isStatic = true + type = declaration.defaultType + visibility = DescriptorVisibilities.PRIVATE + }.also { field -> + val builder = context.createIrBuilder(field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) + val initializer = if (declaration.isCompanion && classToAdd.isObjCClass()) { + builder.irGetObjCClassCompanion(declaration) + } else { + if (declaration.hasConstStateAndNoSideEffects() && !declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) { + builder.irConstantObject( + declaration, + emptyList() + ) + } else { + builder.irBlock { + // we need to make object available for rereading from the same thread while initializing + +irSetField(null, field, irCall(symbols.createUninitializedInstance, listOf(declaration.defaultType)), origin = IrStatementOriginFieldPreInit) + +irCall(symbols.initInstance).apply { + putValueArgument(0, irGetField(null, field)) + putValueArgument(1, irCallConstructor(primaryConstructor.symbol, emptyList())) + } + +irGetField(null, field) + } + } + } + field.initializer = builder.irExprBody(initializer) + if (declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) { + field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.threadLocal.owner) + } else if (declaration.annotations.hasAnnotation(KonanFqNames.sharedImmutable) || context.memoryModel != MemoryModel.EXPERIMENTAL){ + field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner) + } + } + function.body = context.createIrBuilder(function.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + val value = irGetField(null, instanceField) + +irReturn(value) + } + } + + private fun IrConstructor.isAutogeneratedSimpleConstructor(): Boolean { + if (!this.isPrimary) return false + if (valueParameters.isNotEmpty()) return false + val statements = this.body?.statements ?: return false + if (statements.isEmpty()) return false + val constructorCall = statements[0] as? IrDelegatingConstructorCall ?: return false + val constructor = constructorCall.symbol.owner as? IrConstructor ?: return false + if (!constructor.constructedClass.isAny()) return false + return statements.asSequence().drop(1).all { + it is IrBlock && it.origin == IrStatementOrigin.INITIALIZE_FIELD + } + } + + private fun IrClass.hasConstStateAndNoSideEffects(): Boolean { + if (this.hasAnnotation(KonanFqNames.canBePrecreated)) return true + val fields = context.getLayoutBuilder(this).fields + return fields.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor() } + } + + companion object { + val companionFieldName = "companionField".synthesizedName + val instanceFieldName = "instanceField".synthesizedName + + } + + // This is a hack to avoid early freezing. Should be removed when freezing is removed + object IrStatementOriginFieldPreInit : IrStatementOriginImpl("FIELD_PRE_INIT") + +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReturnsInsertionLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReturnsInsertionLowering.kt index 5d4cff3fef8..02f5a369372 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReturnsInsertionLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReturnsInsertionLowering.kt @@ -9,13 +9,11 @@ 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.ir.IrElement -import org.jetbrains.kotlin.ir.builders.irGetObject -import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.isNullableNothing import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -38,7 +36,7 @@ internal class ReturnsInsertionLowering(val context: Context) : FileLoweringPass body as IrBlockBody context.createIrBuilder(declaration.symbol, declaration.endOffset, declaration.endOffset).run { if (declaration is IrConstructor || declaration.returnType == context.irBuiltIns.unitType) { - body.statements += irReturn(irGetObject(symbols.unit)) + body.statements += irReturn(irCall(symbols.theUnitInstance, context.irBuiltIns.unitType)) } else if (declaration.returnType.isNullable()) { // this is a workaround for KT-42832 val typeOperatorCall = body.statements.lastOrNull() as? IrTypeOperatorCall @@ -56,7 +54,7 @@ internal class ReturnsInsertionLowering(val context: Context) : FileLoweringPass if (expression.inlineFunctionSymbol?.owner?.returnType == context.irBuiltIns.unitType) { val offset = (expression.statements.lastOrNull() ?: expression).endOffset context.createIrBuilder(expression.symbol, offset, offset).run { - expression.statements += irReturn(irGetObject(symbols.unit)) + expression.statements += irReturn(irCall(symbols.theUnitInstance, context.irBuiltIns.unitType)) } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt index 29c28929c8b..0a3ab71bd65 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StaticInitializersLowering.kt @@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.konan.ConfigChecks import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT import org.jetbrains.kotlin.backend.konan.KonanFqNames -import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind -import org.jetbrains.kotlin.backend.konan.llvm.needsGCRegistration -import org.jetbrains.kotlin.backend.konan.llvm.storageKind +import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.* diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index 441e99e2928..e02e4ce1f08 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall import org.jetbrains.kotlin.backend.konan.util.IntArrayList import org.jetbrains.kotlin.backend.konan.util.LongArrayList +import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.* @@ -106,7 +107,7 @@ internal object DevirtualizationAnalysis { context.getLayoutBuilder(declaration).associatedObjects.values.forEach { assert(it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" } - associatedObjectConstructors += moduleDFG.symbolTable.mapFunction(it.constructors.single()) + associatedObjectConstructors += moduleDFG.symbolTable.mapFunction(context.getObjectClassInstanceFunction(it)) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt index 027f1c56df8..b80ee6d267e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.konan.optimizations -import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.konan.Context @@ -21,7 +20,6 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irBlock import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol import org.jetbrains.kotlin.ir.util.* @@ -427,15 +425,7 @@ internal object StaticInitializersOptimization { } override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet { - val objectClass = expression.symbol.owner - val constructor = objectClass.constructors.toList().atMostOne() - if (constructor != null) { - updateResultForFunction(constructor, data) - } else { - require(objectClass.isExternal || objectClass is IrLazyClass) { "No constructor for ${objectClass.render()}" } - } - // Don't update [data] as the constructor might not be called here (the object might be initialized already). - return data + error("IrGetObjectValue should be lowered away at this point") } private fun processCall(expression: IrFunctionAccessExpression, actualCallee: IrFunction, data: BitSet): BitSet { diff --git a/kotlin-native/backend.native/tests/framework/stacktraceBridges/stacktraceBridges.swift b/kotlin-native/backend.native/tests/framework/stacktraceBridges/stacktraceBridges.swift index 85ff9f7efc7..19124148846 100644 --- a/kotlin-native/backend.native/tests/framework/stacktraceBridges/stacktraceBridges.swift +++ b/kotlin-native/backend.native/tests/framework/stacktraceBridges/stacktraceBridges.swift @@ -24,12 +24,12 @@ func testKotlin2Objc() throws { func testCompanionObject() throws { let trace = Foo.companion.trace - try assertTrue(trace[6].contains("objc2kotlin_kclass:Foo#companion")) + try assertTrue(trace[8].contains("objc2kotlin_kclass:Foo#companion")) } func testStandaloneObject() throws { let trace = Object.shared.trace - try assertTrue(trace[6].contains("objc2kotlin_kclass:Object#shared")) + try assertTrue(trace[8].contains("objc2kotlin_kclass:Object#shared")) } func testEnumEntry() throws { diff --git a/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp b/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp index 0da6af51b3f..4903dbc953a 100644 --- a/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp +++ b/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp @@ -31,8 +31,6 @@ touchType(KRefSharedHolder) touchFunction(AllocInstance) touchFunction(AllocArrayInstance) -touchFunction(InitThreadLocalSingleton) -touchFunction(InitSingleton) touchFunction(InitAndRegisterGlobal) touchFunction(UpdateHeapRef) touchFunction(UpdateStackRef) diff --git a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp index 0fcfd03fe55..7c2cae88a4c 100644 --- a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp @@ -2373,104 +2373,6 @@ OBJ_GETTER(allocArrayInstance, const TypeInfo* type_info, int32_t elements) { RETURN_OBJ(container.GetPlace()->obj()); } -template -OBJ_GETTER(initThreadLocalSingleton, - ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - ObjHeader* value = *location; - if (value != nullptr) { - // OK'ish, inited by someone else. - RETURN_OBJ(value); - } - ObjHeader* object = allocInstance(typeInfo, OBJ_RESULT); - updateHeapRef(location, object); -#if KONAN_NO_EXCEPTIONS - ctor(object); - return object; -#else - try { - ctor(object); - return object; - } catch (...) { - UpdateReturnRef(OBJ_RESULT, nullptr); - ZeroHeapRef(location); - throw; - } -#endif -} - -template -OBJ_GETTER(initSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { -#if KONAN_NO_THREADS - ObjHeader* value = *location; - if (value != nullptr) { - // OK'ish, inited by someone else. - RETURN_OBJ(value); - } - ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT); - UpdateHeapRef(location, object); -#if KONAN_NO_EXCEPTIONS - ctor(object); - FreezeSubgraph(object); - return object; -#else - try { - ctor(object); - if (Strict) - FreezeSubgraph(object); - return object; - } catch (...) { - UpdateReturnRef(OBJ_RESULT, nullptr); - ZeroHeapRef(location); - throw; - } -#endif // KONAN_NO_EXCEPTIONS -#else // KONAN_NO_THREADS - // Search from the top of the stack. - for (auto it = memoryState->initializingSingletons.rbegin(); it != memoryState->initializingSingletons.rend(); ++it) { - if (it->first == location) { - RETURN_OBJ(it->second); - } - } - - ObjHeader* initializing = kInitializingSingleton; - - // Spin lock. - ObjHeader* value = nullptr; - while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing); - if (value != nullptr) { - // OK'ish, inited by someone else. - RETURN_OBJ(value); - } - ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT); - memoryState->initializingSingletons.push_back(std::make_pair(location, object)); -#if KONAN_NO_EXCEPTIONS - ctor(object); - if (Strict) - FreezeSubgraph(object); - UpdateHeapRef(location, object); - synchronize(); - memoryState->initializingSingletons.pop_back(); - return object; -#else // KONAN_NO_EXCEPTIONS - try { - ctor(object); - if (Strict) - FreezeSubgraph(object); - UpdateHeapRef(location, object); - synchronize(); - memoryState->initializingSingletons.pop_back(); - return object; - } catch (...) { - UpdateReturnRef(OBJ_RESULT, nullptr); - zeroHeapRef(location); - memoryState->initializingSingletons.pop_back(); - synchronize(); - throw; - } -#endif // KONAN_NO_EXCEPTIONS -#endif // KONAN_NO_THREADS -} - /** * We keep thread affinity and reference value based cookie in the atomic references, so that * repeating read operation of the same value do not lead to the repeating rememberNewContainer() operation. @@ -3398,20 +3300,6 @@ OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* typeInfo, int32_t elements RETURN_RESULT_OF(allocArrayInstance, typeInfo, elements); } -OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initThreadLocalSingleton, location, typeInfo, ctor); -} -OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initThreadLocalSingleton, location, typeInfo, ctor); -} - -OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initSingleton, location, typeInfo, ctor); -} -OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initSingleton, location, typeInfo, ctor); -} - void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) { RuntimeCheck(false, "Global registration is impossible in legacy MM"); } diff --git a/kotlin-native/runtime/src/legacymm/cpp/MemoryPrivate.hpp b/kotlin-native/runtime/src/legacymm/cpp/MemoryPrivate.hpp index 8994afca150..9a0aa4b13ff 100644 --- a/kotlin-native/runtime/src/legacymm/cpp/MemoryPrivate.hpp +++ b/kotlin-native/runtime/src/legacymm/cpp/MemoryPrivate.hpp @@ -309,11 +309,6 @@ OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW; OBJ_GETTER(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements); OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* type_info, int32_t elements); -OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); -OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); - -OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); -OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object); MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object); diff --git a/kotlin-native/runtime/src/main/cpp/CompilerExport.cpp b/kotlin-native/runtime/src/main/cpp/CompilerExport.cpp index fc359cc3dc9..d84b9928ed7 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerExport.cpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerExport.cpp @@ -25,8 +25,6 @@ void ensureUsed(Ret (*f)(Args...)) { void EnsureDeclarationsEmitted() { ensureUsed(AllocInstance); ensureUsed(AllocArrayInstance); - ensureUsed(InitThreadLocalSingleton); - ensureUsed(InitSingleton); ensureUsed(InitAndRegisterGlobal); ensureUsed(UpdateHeapRef); ensureUsed(UpdateStackRef); diff --git a/kotlin-native/runtime/src/main/cpp/Logging.hpp b/kotlin-native/runtime/src/main/cpp/Logging.hpp index efcbf85845c..df0183eb90d 100644 --- a/kotlin-native/runtime/src/main/cpp/Logging.hpp +++ b/kotlin-native/runtime/src/main/cpp/Logging.hpp @@ -83,6 +83,7 @@ void VLog(Level level, std::initializer_list tags, const char* form inline constexpr const char* kTagGC = "gc"; inline constexpr const char* kTagMM = "mm"; +inline constexpr const char* kTagTLS = "tls"; } // namespace kotlin diff --git a/kotlin-native/runtime/src/main/cpp/Memory.h b/kotlin-native/runtime/src/main/cpp/Memory.h index c656695016b..72501d1a53c 100644 --- a/kotlin-native/runtime/src/main/cpp/Memory.h +++ b/kotlin-native/runtime/src/main/cpp/Memory.h @@ -195,9 +195,6 @@ OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW; OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements); -OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); - -OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); // `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already // set by static initialization. diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt index 182d0a8cf99..ad5913ad8e5 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt @@ -53,6 +53,7 @@ class IntrinsicType { const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB" const val INIT_INSTANCE = "INIT_INSTANCE" const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM" + const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE" // Enums const val ENUM_VALUES = "ENUM_VALUES" diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Intrinsics.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Intrinsics.kt index 1120a5cd60a..946662ba452 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Intrinsics.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Intrinsics.kt @@ -30,3 +30,6 @@ import kotlin.native.internal.IntrinsicType // Reinterprets this value from T to R having the same binary representation (e.g. to unwrap inline class). @TypedIntrinsic(IntrinsicType.IDENTITY) @PublishedApi external internal fun T.reinterpret(): R + + +@TypedIntrinsic(IntrinsicType.THE_UNIT_INSTANCE) @ExportForCompiler external internal fun theUnitInstance(): Unit \ No newline at end of file diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp b/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp deleted file mode 100644 index 004bf2d2307..00000000000 --- a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -#include "InitializationScheme.hpp" - -#include "Common.h" -#include "ObjectOps.hpp" -#include "ThreadData.hpp" -#include "ThreadState.hpp" - -using namespace kotlin; - -OBJ_GETTER(mm::InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - AssertThreadState(threadData, ThreadState::kRunnable); - if (auto* value = *location) { - // Initialized by someone else. - RETURN_OBJ(value); - } - auto* value = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT); - mm::SetHeapRef(location, value); -#if KONAN_NO_EXCEPTIONS - ctor(value); -#else - try { - ctor(value); - } catch (...) { - mm::SetStackRef(OBJ_RESULT, nullptr); - mm::SetHeapRef(location, nullptr); - throw; - } -#endif - return value; -} - -OBJ_GETTER(mm::InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - AssertThreadState(threadData, ThreadState::kRunnable); - auto& initializingSingletons = threadData->initializingSingletons(); - - // Search from the top of the stack. - for (auto it = initializingSingletons.rbegin(); it != initializingSingletons.rend(); ++it) { - if (it->first == location) { - RETURN_OBJ(it->second); - } - } - - ObjHeader* initializing = kInitializingSingleton; - - // Spin lock. - ObjHeader* value = nullptr; - { - ThreadStateGuard guard(ThreadState::kNative); - while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing) { - } - } - if (value != nullptr) { - // Initialized by someone else. - RETURN_OBJ(value); - } - auto* object = mm::AllocateObject(threadData, typeInfo, OBJ_RESULT); - initializingSingletons.push_back(std::make_pair(location, object)); - -#if KONAN_NO_EXCEPTIONS - ctor(object); -#else - try { - ctor(object); - } catch (...) { - mm::SetStackRef(OBJ_RESULT, nullptr); - mm::SetHeapRefAtomic(location, nullptr); - initializingSingletons.pop_back(); - throw; - } -#endif - mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location); - mm::SetHeapRefAtomic(location, object); - initializingSingletons.pop_back(); - return object; -} diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.hpp b/kotlin-native/runtime/src/mm/cpp/InitializationScheme.hpp deleted file mode 100644 index 49420b1df61..00000000000 --- a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_MM_INITIALIZATION_SCHEME_H -#define RUNTIME_MM_INITIALIZATION_SCHEME_H - -#include "Memory.h" - -namespace kotlin { -namespace mm { - -class ThreadData; - -OBJ_GETTER(InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); -OBJ_GETTER(InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); - -} // namespace mm -} // namespace kotlin - -#endif // RUNTIME_MM_INITIALIZATION_SCHEME_H diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp b/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp deleted file mode 100644 index 4b08d6e85a9..00000000000 --- a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -#include "InitializationScheme.hpp" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ObjectTestSupport.hpp" -#include "ScopedThread.hpp" -#include "TestSupport.hpp" -#include "ThreadData.hpp" -#include "Types.h" -#include "std_support/Vector.hpp" - -using namespace kotlin; - -using testing::_; - -namespace { - -struct EmptyPayload { - using Field = ObjHeader* EmptyPayload::*; - static constexpr std::array kFields{}; -}; - -class InitSingletonTest : public testing::Test { -public: - InitSingletonTest() { - globalConstructor_ = &constructor_; - } - - ~InitSingletonTest() { - globalConstructor_ = nullptr; - mm::GlobalData::Instance().gc().ClearForTests(); - mm::GlobalData::Instance().globalsRegistry().ClearForTests(); - } - - testing::MockFunction& constructor() { return constructor_; } - - OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, mm::ThreadData& threadData) { - RETURN_RESULT_OF(mm::InitThreadLocalSingleton, &threadData, location, type_.typeInfo(), constructorImpl); - } - - OBJ_GETTER(InitSingleton, ObjHeader** location, mm::ThreadData& threadData) { - RETURN_RESULT_OF(mm::InitSingleton, &threadData, location, type_.typeInfo(), constructorImpl); - } - -private: - testing::StrictMock> constructor_; - test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder()}; - - static testing::MockFunction* globalConstructor_; - - static void constructorImpl(ObjHeader* object) { globalConstructor_->Call(object); } -}; - -// static -testing::MockFunction* InitSingletonTest::globalConstructor_ = nullptr; - -} // namespace - -TEST_F(InitSingletonTest, InitThreadLocalSingleton) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - - ObjHeader* valueAtConstructor = nullptr; - EXPECT_CALL(constructor(), Call(_)).WillOnce( - [&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - valueAtConstructor = value; - }); - ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(valueAtConstructor, location); - }); -} - -TEST_F(InitSingletonTest, InitThreadLocalSingletonTwice) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader previousValue; - ObjHeader* location = &previousValue; - ObjHeader* stackLocation = nullptr; - - EXPECT_CALL(constructor(), Call(_)).Times(0); - ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(value, &previousValue); - }); -} - -TEST_F(InitSingletonTest, InitThreadLocalSingletonFail) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - constexpr int kException = 42; - - EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); - try { - InitThreadLocalSingleton(&location, threadData, &stackLocation); - ASSERT_TRUE(false); // Cannot be reached. - } catch (int exception) { - EXPECT_THAT(exception, kException); - } - EXPECT_THAT(stackLocation, nullptr); - EXPECT_THAT(location, nullptr); - }); -} - -TEST_F(InitSingletonTest, InitSingleton) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - - ObjHeader* valueAtConstructor = nullptr; - EXPECT_CALL(constructor(), Call(_)).WillOnce( - [&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(location, kInitializingSingleton); - valueAtConstructor = value; - }); - ObjHeader* value = InitSingleton(&location, threadData, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(valueAtConstructor, location); - }); -} - - -TEST_F(InitSingletonTest, InitSingletonTwice) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader previousValue; - ObjHeader* location = &previousValue; - ObjHeader* stackLocation = nullptr; - - EXPECT_CALL(constructor(), Call(_)).Times(0); - ObjHeader* value = InitSingleton(&location, threadData, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(value, &previousValue); - }); -} - -TEST_F(InitSingletonTest, InitSingletonFail) { - RunInNewThread([this](mm::ThreadData& threadData) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - constexpr int kException = 42; - - EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); - try { - InitSingleton(&location, threadData, &stackLocation); - ASSERT_TRUE(false); // Cannot be reached. - } catch (int exception) { - EXPECT_THAT(exception, kException); - } - EXPECT_THAT(stackLocation, nullptr); - EXPECT_THAT(location, nullptr); - }); -} - -TEST_F(InitSingletonTest, InitSingletonRecursive) { - RunInNewThread([this](mm::ThreadData& threadData) { - // The first singleton. Its constructor depends on the second singleton. - ObjHeader* location1 = nullptr; - ObjHeader* stackLocation1 = nullptr; - // The second singleton. Its constructor depends on the first singleton. - ObjHeader* location2 = nullptr; - ObjHeader* stackLocation2 = nullptr; - - EXPECT_CALL(constructor(), Call(_)) - .Times(2) // called only once for each singleton. - .WillRepeatedly([this, &location1, &stackLocation1, &location2, &stackLocation2, &threadData](ObjHeader* value) { - if (value == stackLocation1) { - ObjHeader* result = InitSingleton(&location2, threadData, &stackLocation2); - EXPECT_THAT(result, stackLocation2); - EXPECT_THAT(result, location2); - EXPECT_THAT(result, testing::Not(testing::Truly(isNullOrMarker))); - } else { - ObjHeader* result = InitSingleton(&location1, threadData, &stackLocation1); - EXPECT_THAT(result, stackLocation1); - EXPECT_THAT(result, testing::Ne(location1)); - EXPECT_THAT(location1, kInitializingSingleton); - } - }); - ObjHeader* value = InitSingleton(&location1, threadData, &stackLocation1); - EXPECT_THAT(value, stackLocation1); - EXPECT_THAT(value, location1); - }); -} - -TEST_F(InitSingletonTest, InitSingletonConcurrent) { - constexpr size_t kThreadCount = kDefaultThreadCount; - std::atomic canStart(false); - std::atomic readyCount(0); - std_support::vector threads; - ObjHeader* location = nullptr; - std_support::vector stackLocations(kThreadCount, nullptr); - std_support::vector actual(kThreadCount, nullptr); - - for (size_t i = 0; i < kThreadCount; ++i) { - threads.emplace_back([this, i, &location, &stackLocations, &actual, &readyCount, &canStart]() { - ScopedMemoryInit init; - auto* threadData = init.memoryState()->GetThreadData(); - ++readyCount; - while (!canStart) { - } - actual[i] = InitSingleton(&location, *threadData, &stackLocations[i]); - threadData->Publish(); - }); - } - - while (readyCount < kThreadCount) { - } - // Constructor is called exactly once. - EXPECT_CALL(constructor(), Call(_)); - canStart = true; - threads.clear(); - testing::Mock::VerifyAndClearExpectations(&constructor()); - - EXPECT_THAT(location, testing::Not(testing::Truly(isNullOrMarker))); - EXPECT_THAT(stackLocations, testing::Each(location)); - EXPECT_THAT(actual, testing::Each(location)); -} - -TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) { - constexpr size_t kThreadCount = kDefaultThreadCount; - std::atomic canStart(false); - std::atomic readyCount(0); - std_support::vector threads; - constexpr int kException = 42; - ObjHeader* location = nullptr; - std_support::vector stackLocations(kThreadCount, nullptr); - - for (size_t i = 0; i < kThreadCount; ++i) { - threads.emplace_back([this, i, &location, &stackLocations, &readyCount, &canStart]() { - ScopedMemoryInit init; - auto* threadData = init.memoryState()->GetThreadData(); - ++readyCount; - while (!canStart) { - } - try { - InitSingleton(&location, *threadData, &stackLocations[i]); - ASSERT_TRUE(false); // Cannot be reached. - } catch (int exception) { - EXPECT_THAT(exception, kException); - } - threadData->Publish(); - }); - } - - while (readyCount < kThreadCount) { - } - // Constructor is called exactly `kThreadCount` times. - EXPECT_CALL(constructor(), Call(_)).Times(kThreadCount).WillRepeatedly([]() { throw kException; }); - canStart = true; - threads.clear(); - testing::Mock::VerifyAndClearExpectations(&constructor()); - - EXPECT_THAT(location, nullptr); - EXPECT_THAT(stackLocations, testing::Each(nullptr)); -} diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index b5af13dbe83..91096c37db5 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -11,7 +11,6 @@ #include "Freezing.hpp" #include "GC.hpp" #include "GlobalsRegistry.hpp" -#include "InitializationScheme.hpp" #include "KAssert.h" #include "Natives.h" #include "ObjectOps.hpp" @@ -146,18 +145,6 @@ extern "C" OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elem RETURN_RESULT_OF(mm::AllocateArray, threadData, typeInfo, static_cast(elements)); } -extern "C" ALWAYS_INLINE OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); - - RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadData, location, typeInfo, ctor); -} - -extern "C" ALWAYS_INLINE OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); - - RETURN_RESULT_OF(mm::InitSingleton, threadData, location, typeInfo, ctor); -} - extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) { auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); AssertThreadState(threadData, ThreadState::kRunnable); diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadLocalStorage.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadLocalStorage.cpp index 2dacb15ca03..4ad1c745e51 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadLocalStorage.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadLocalStorage.cpp @@ -4,12 +4,14 @@ */ #include "ThreadLocalStorage.hpp" +#include "Logging.hpp" using namespace kotlin; void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept { RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state"); RuntimeAssert(size >= 0, "Size cannot be negative"); + RuntimeLogDebug({kTagTLS}, "Add record key = %p, size = %d\n", key, size); auto it = map_.find(key); if (it != map_.end()) { RuntimeAssert(it->second.size == size, "Attempt to add TLS record with the same key, but different size"); @@ -20,12 +22,14 @@ void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept { } void mm::ThreadLocalStorage::Commit() noexcept { + RuntimeLogDebug({kTagTLS}, "Committed"); RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state"); storage_.resize(size_); state_ = State::kCommitted; } void mm::ThreadLocalStorage::Clear() noexcept { + RuntimeLogDebug({kTagTLS}, "Cleared"); RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state"); // Just free the storage. storage_.clear(); @@ -33,6 +37,7 @@ void mm::ThreadLocalStorage::Clear() noexcept { } ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept { + RuntimeLogDebug({kTagTLS}, "Lookup key = %p, index = %d", key, index); RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state"); if (lastKeyAndEntry_.first == key) { return Lookup(lastKeyAndEntry_.second, index); @@ -44,6 +49,7 @@ ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) noexcept { } ObjHeader** mm::ThreadLocalStorage::Lookup(Entry entry, int index) noexcept { + RuntimeLogDebug({kTagTLS}, "Lookup entry = {%d, %d}, index = %d", entry.offset, entry.size, index); RuntimeAssert(index < entry.size, "Out of bounds TLS access"); return &storage_[entry.offset + index]; } diff --git a/kotlin-native/runtime/src/relaxed/cpp/MemoryImpl.cpp b/kotlin-native/runtime/src/relaxed/cpp/MemoryImpl.cpp index bc93e2a8abb..ef837e119f2 100644 --- a/kotlin-native/runtime/src/relaxed/cpp/MemoryImpl.cpp +++ b/kotlin-native/runtime/src/relaxed/cpp/MemoryImpl.cpp @@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) { RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements); } -OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitThreadLocalSingletonRelaxed, location, typeInfo, ctor); -} - -OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitSingletonRelaxed, location, typeInfo, ctor); -} - RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) { ReleaseHeapRefRelaxed(object); } diff --git a/kotlin-native/runtime/src/strict/cpp/MemoryImpl.cpp b/kotlin-native/runtime/src/strict/cpp/MemoryImpl.cpp index 6610fd1dfe7..08f4600d5aa 100644 --- a/kotlin-native/runtime/src/strict/cpp/MemoryImpl.cpp +++ b/kotlin-native/runtime/src/strict/cpp/MemoryImpl.cpp @@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) { RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements); } -OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitThreadLocalSingletonStrict, location, typeInfo, ctor); -} - -OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitSingletonStrict, location, typeInfo, ctor); -} - RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) { ReleaseHeapRefStrict(object); }