[K/N] Rework object and enum classes initialization
Now it works with common logic for all static scope not with custom one.
This commit is contained in:
committed by
Space Team
parent
7006eb938d
commit
d3adfec2fb
+8
-1
@@ -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.pop
|
||||||
import org.jetbrains.kotlin.backend.common.push
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
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.builtins.UnsignedType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
@@ -257,7 +258,13 @@ private class ExportedElement(val kind: ElementKind,
|
|||||||
// Produce instance getter if needed.
|
// Produce instance getter if needed.
|
||||||
if (isSingletonObject) {
|
if (isSingletonObject) {
|
||||||
generateFunction(owner.codegen, owner.kGetObjectFuncType, "${cname}_instance") {
|
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)
|
ret(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
|||||||
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
||||||
import org.jetbrains.kotlin.konan.target.Architecture
|
import org.jetbrains.kotlin.konan.target.Architecture
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
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.SerializedIrModule
|
||||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -50,15 +49,13 @@ internal class NativeMapping : DefaultMapping() {
|
|||||||
data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
|
data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
|
||||||
|
|
||||||
val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
||||||
val enumImplObjects = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrClass>()
|
|
||||||
val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||||
val enumEntriesMaps = mutableMapOf<IrClass, Map<Name, LoweredEnumEntryDescription>>()
|
val enumEntriesMaps = mutableMapOf<IrClass, Map<Name, LoweredEnumEntryDescription>>()
|
||||||
val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
|
val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
|
||||||
val notLoweredInlineFunctions = mutableMapOf<IrFunctionSymbol, IrFunction>()
|
val notLoweredInlineFunctions = mutableMapOf<IrFunctionSymbol, IrFunction>()
|
||||||
val companionObjectCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
|
||||||
val outerThisCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
val outerThisCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||||
val lateinitPropertyCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrProperty, IrSimpleFunction>()
|
val lateinitPropertyCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrProperty, IrSimpleFunction>()
|
||||||
val enumValuesCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
val objectInstanceGetter = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Context(config: KonanConfig) : KonanBackendContext(config), ConfigChecks {
|
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 innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) }
|
||||||
val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) }
|
val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) }
|
||||||
val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) }
|
val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) }
|
||||||
val enumsSupport by lazy { EnumsSupport(mapping, ir.symbols, irBuiltIns, irFactory) }
|
val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) }
|
||||||
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, ir.symbols, irFactory) }
|
val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) }
|
||||||
|
|
||||||
open class LazyMember<T>(val initializer: Context.() -> T) {
|
open class LazyMember<T>(val initializer: Context.() -> T) {
|
||||||
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
|
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
|
||||||
|
|||||||
+6
@@ -250,6 +250,12 @@ internal val initializersPhase = makeKonanFileLoweringPhase(
|
|||||||
prerequisite = setOf(enumConstructorsPhase)
|
prerequisite = setOf(enumConstructorsPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val objectClassesPhase = makeKonanFileLoweringPhase(
|
||||||
|
::ObjectClassLowering,
|
||||||
|
name = "ObjectClasses",
|
||||||
|
description = "Enum constructors lowering"
|
||||||
|
)
|
||||||
|
|
||||||
internal val localFunctionsPhase = makeKonanFileOpPhase(
|
internal val localFunctionsPhase = makeKonanFileOpPhase(
|
||||||
op = { context, irFile ->
|
op = { context, irFile ->
|
||||||
LocalDelegatedPropertiesLowering().lower(irFile)
|
LocalDelegatedPropertiesLowering().lower(irFile)
|
||||||
|
|||||||
+1
@@ -292,6 +292,7 @@ internal val allLoweringsPhase = SameTypeNamedCompilerPhase(
|
|||||||
coroutinesPhase,
|
coroutinesPhase,
|
||||||
typeOperatorPhase,
|
typeOperatorPhase,
|
||||||
expressionBodyTransformPhase,
|
expressionBodyTransformPhase,
|
||||||
|
objectClassesPhase,
|
||||||
constantInliningPhase,
|
constantInliningPhase,
|
||||||
staticInitializersPhase,
|
staticInitializersPhase,
|
||||||
bridgesPhase,
|
bridgesPhase,
|
||||||
|
|||||||
+1
-1
@@ -1387,7 +1387,7 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun CCallbackBuilder.receiveValue(): IrExpression {
|
override fun CCallbackBuilder.receiveValue(): IrExpression {
|
||||||
return bridgeBuilder.kotlinIrBuilder.irGetObject(irBuiltIns.unitClass)
|
return bridgeBuilder.kotlinIrBuilder.irCall(symbols.theUnitInstance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -498,8 +498,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
|
|||||||
}
|
}
|
||||||
return declarations.mapNotNull {
|
return declarations.mapNotNull {
|
||||||
when (it) {
|
when (it) {
|
||||||
is IrField -> it.takeIf { it.isReal }?.toFieldInfo()
|
is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo()
|
||||||
is IrProperty -> it.takeIf { it.isReal }?.backingField?.toFieldInfo()
|
is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -232,6 +232,8 @@ internal class KonanSymbols(
|
|||||||
|
|
||||||
val reinterpret = internalFunction("reinterpret")
|
val reinterpret = internalFunction("reinterpret")
|
||||||
|
|
||||||
|
val theUnitInstance = internalFunction("theUnitInstance")
|
||||||
|
|
||||||
val ieee754Equals = internalFunctions("ieee754Equals")
|
val ieee754Equals = internalFunctions("ieee754Equals")
|
||||||
|
|
||||||
val equals = irBuiltIns.findBuiltInClassMemberFunctions(any, Name.identifier("equals")).single()
|
val equals = irBuiltIns.findBuiltInClassMemberFunctions(any, Name.identifier("equals")).single()
|
||||||
@@ -313,8 +315,6 @@ internal class KonanSymbols(
|
|||||||
|
|
||||||
val initInstance = internalFunction("initInstance")
|
val initInstance = internalFunction("initInstance")
|
||||||
|
|
||||||
val freeze = irBuiltIns.findFunctions(Name.identifier("freeze"), "kotlin", "native", "concurrent").single()
|
|
||||||
|
|
||||||
val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io")
|
val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io")
|
||||||
.single { it.descriptor.valueParameters.singleOrNull()?.type == (irBuiltIns as IrBuiltInsOverDescriptors).builtIns.stringType }
|
.single { it.descriptor.valueParameters.singleOrNull()?.type == (irBuiltIns as IrBuiltInsOverDescriptors).builtIns.stringType }
|
||||||
|
|
||||||
|
|||||||
+4
-123
@@ -23,28 +23,6 @@ import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
|||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
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 {
|
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||||
fun llvmFunction(function: IrFunction): LlvmCallable =
|
fun llvmFunction(function: IrFunction): LlvmCallable =
|
||||||
@@ -1260,109 +1238,12 @@ internal abstract class FunctionGenerationContext(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun getObjectValue(irClass: IrClass, exceptionHandler: ExceptionHandler,
|
fun getObjectValue(irClass: IrClass, exceptionHandler: ExceptionHandler,
|
||||||
startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null,
|
startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null,
|
||||||
resultSlot: LLVMValueRef? = null
|
resultSlot: LLVMValueRef? = null
|
||||||
): LLVMValueRef {
|
): LLVMValueRef {
|
||||||
// TODO: could be processed the same way as other stateless objects.
|
error("Should be lowered out: ${irClass.render()} while generating ${irFunction?.dump()}")
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
-3
@@ -481,8 +481,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R
|
|||||||
|
|
||||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||||
val initThreadLocalSingleton = importRtFunction("InitThreadLocalSingleton")
|
|
||||||
val initSingletonFunction = importRtFunction("InitSingleton")
|
|
||||||
val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal")
|
val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal")
|
||||||
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
|
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
|
||||||
val updateStackRefFunction = importRtFunction("UpdateStackRef")
|
val updateStackRefFunction = importRtFunction("UpdateStackRef")
|
||||||
@@ -564,7 +562,6 @@ internal class Llvm(private val context: Context, val module: LLVMModuleRef) : R
|
|||||||
val compilerUsedGlobals = mutableListOf<LLVMValueRef>()
|
val compilerUsedGlobals = mutableListOf<LLVMValueRef>()
|
||||||
val irStaticInitializers = mutableListOf<IrStaticInitializer>()
|
val irStaticInitializers = mutableListOf<IrStaticInitializer>()
|
||||||
val otherStaticInitializers = mutableListOf<LLVMValueRef>()
|
val otherStaticInitializers = mutableListOf<LLVMValueRef>()
|
||||||
var fileUsesThreadLocalObjects = false
|
|
||||||
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
|
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
|
||||||
val initializersGenerationState = InitializersGenerationState()
|
val initializersGenerationState = InitializersGenerationState()
|
||||||
val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>()
|
val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>()
|
||||||
|
|||||||
+2
@@ -66,6 +66,7 @@ internal enum class IntrinsicType {
|
|||||||
IMMUTABLE_BLOB,
|
IMMUTABLE_BLOB,
|
||||||
INIT_INSTANCE,
|
INIT_INSTANCE,
|
||||||
IS_EXPERIMENTAL_MM,
|
IS_EXPERIMENTAL_MM,
|
||||||
|
THE_UNIT_INSTANCE,
|
||||||
// Enums
|
// Enums
|
||||||
ENUM_VALUES,
|
ENUM_VALUES,
|
||||||
ENUM_VALUE_OF,
|
ENUM_VALUE_OF,
|
||||||
@@ -241,6 +242,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
|||||||
IntrinsicType.IDENTITY -> emitIdentity(args)
|
IntrinsicType.IDENTITY -> emitIdentity(args)
|
||||||
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
|
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
|
||||||
IntrinsicType.IS_EXPERIMENTAL_MM -> emitIsExperimentalMM()
|
IntrinsicType.IS_EXPERIMENTAL_MM -> emitIsExperimentalMM()
|
||||||
|
IntrinsicType.THE_UNIT_INSTANCE -> theUnitInstanceRef.llvm
|
||||||
IntrinsicType.GET_CONTINUATION,
|
IntrinsicType.GET_CONTINUATION,
|
||||||
IntrinsicType.RETURN_IF_SUSPENDED,
|
IntrinsicType.RETURN_IF_SUSPENDED,
|
||||||
IntrinsicType.INTEROP_BITS_TO_FLOAT,
|
IntrinsicType.INTEROP_BITS_TO_FLOAT,
|
||||||
|
|||||||
+27
-67
@@ -46,12 +46,6 @@ internal enum class FieldStorageKind {
|
|||||||
THREAD_LOCAL
|
THREAD_LOCAL
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class ObjectStorageKind {
|
|
||||||
PERMANENT,
|
|
||||||
THREAD_LOCAL,
|
|
||||||
SHARED
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: maybe unannotated singleton objects shall be accessed from main thread only as well?
|
// TODO: maybe unannotated singleton objects shall be accessed from main thread only as well?
|
||||||
internal fun IrField.storageKind(context: Context): FieldStorageKind {
|
internal fun IrField.storageKind(context: Context): FieldStorageKind {
|
||||||
// TODO: Is this correct?
|
// TODO: Is this correct?
|
||||||
@@ -77,12 +71,6 @@ internal fun IrField.needsGCRegistration(context: Context) =
|
|||||||
(hasNonConstInitializer || // which are initialized from heap object
|
(hasNonConstInitializer || // which are initialized from heap object
|
||||||
!isFinal) // or are not final
|
!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 {
|
internal fun IrField.isGlobalNonPrimitive(context: Context) = when {
|
||||||
type.computePrimitiveBinaryTypeOrNull() != null -> false
|
type.computePrimitiveBinaryTypeOrNull() != null -> false
|
||||||
@@ -883,44 +871,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (declaration.kind.isSingleton && !declaration.isUnit()) {
|
|
||||||
val singleton = context.generationState.llvmDeclarations.forSingleton(declaration)
|
|
||||||
val access = singleton.instanceStorage
|
|
||||||
if (access is GlobalAddressAccess) {
|
|
||||||
// Global objects are kept in a data segment and can be accessed by any module (if exported) and also
|
|
||||||
// they need to be initialized statically.
|
|
||||||
LLVMSetInitializer(access.getAddress(null), if (declaration.storageKind(context) == ObjectStorageKind.PERMANENT)
|
|
||||||
llvm.staticData.createConstKotlinObject(declaration,
|
|
||||||
*computeFields(declaration)).llvm else codegen.kNullObjHeaderPtr)
|
|
||||||
} else {
|
|
||||||
// Thread local objects are kept in a special map, so they need a getter function to be accessible
|
|
||||||
// by other modules.
|
|
||||||
val isObjCCompanion = declaration.isCompanion && declaration.parentAsClass.isObjCClass()
|
|
||||||
// If can be exported and can be instantiated.
|
|
||||||
if (declaration.isExported() && !isObjCCompanion &&
|
|
||||||
declaration.constructors.singleOrNull() { it.valueParameters.size == 0 } != null) {
|
|
||||||
val valueGetterName = declaration.threadLocalObjectStorageGetterSymbolName
|
|
||||||
generateFunction(codegen,
|
|
||||||
functionType(codegen.kObjHeaderPtrPtr, false),
|
|
||||||
valueGetterName) {
|
|
||||||
val value = access.getAddress(this)
|
|
||||||
ret(value)
|
|
||||||
}
|
|
||||||
// Getter uses TLS object, so need to ensure that this file's (de)initializer function
|
|
||||||
// inits and deinits TLS.
|
|
||||||
llvm.fileUsesThreadLocalObjects = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeFields(declaration: IrClass): Array<ConstValue> {
|
|
||||||
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) {
|
override fun visitProperty(declaration: IrProperty) {
|
||||||
@@ -987,7 +937,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
is IrVararg -> return evaluateVararg (value)
|
is IrVararg -> return evaluateVararg (value)
|
||||||
is IrBreak -> return evaluateBreak (value)
|
is IrBreak -> return evaluateBreak (value)
|
||||||
is IrContinue -> return evaluateContinue (value)
|
is IrContinue -> return evaluateContinue (value)
|
||||||
is IrGetObjectValue -> return evaluateGetObjectValue (value, resultSlot)
|
is IrGetObjectValue -> return evaluateGetObjectValue (value)
|
||||||
is IrFunctionReference -> return evaluateFunctionReference (value)
|
is IrFunctionReference -> return evaluateFunctionReference (value)
|
||||||
is IrSuspendableExpression ->
|
is IrSuspendableExpression ->
|
||||||
return evaluateSuspendableExpression (value, resultSlot)
|
return evaluateSuspendableExpression (value, resultSlot)
|
||||||
@@ -1012,14 +962,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun evaluateGetObjectValue(value: IrGetObjectValue, resultSlot: LLVMValueRef?): LLVMValueRef =
|
private fun evaluateGetObjectValue(value: IrGetObjectValue): LLVMValueRef {
|
||||||
functionGenerationContext.getObjectValue(
|
error("Should be lowered out: ${value.symbol.owner.render()}")
|
||||||
value.symbol.owner,
|
}
|
||||||
currentCodeContext.exceptionHandler,
|
|
||||||
value.startLocation,
|
|
||||||
value.endLocation,
|
|
||||||
resultSlot
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
@@ -1798,7 +1743,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
)
|
)
|
||||||
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL)
|
if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL)
|
||||||
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
functionGenerationContext.checkGlobalsAccessible(currentCodeContext.exceptionHandler)
|
||||||
if (value.symbol.owner.shouldBeFrozen(context))
|
if (value.symbol.owner.shouldBeFrozen(context) && value.origin != ObjectClassLowering.IrStatementOriginFieldPreInit)
|
||||||
functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler)
|
functionGenerationContext.freeze(valueToAssign, currentCodeContext.exceptionHandler)
|
||||||
functionGenerationContext.storeAny(valueToAssign, globalAddress, false)
|
functionGenerationContext.storeAny(valueToAssign, globalAddress, false)
|
||||||
}
|
}
|
||||||
@@ -1909,14 +1854,29 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
val fields = if (value.constructor.owner.isConstantConstructorIntrinsic) {
|
val fields = if (value.constructor.owner.isConstantConstructorIntrinsic) {
|
||||||
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
||||||
} else {
|
} else {
|
||||||
context.getLayoutBuilder(constructedClass).fields.map { field ->
|
val fields = context.getLayoutBuilder(constructedClass).fields
|
||||||
val index = value.constructor.owner.valueParameters
|
val valueParameters = value.constructor.owner.valueParameters.associateBy { it.name.toString() }
|
||||||
.indexOfFirst { it.name.toString() == field.name }
|
fields.map { field ->
|
||||||
.takeIf { it >= 0 }
|
if (field.isConst) {
|
||||||
?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
val init = field.irField!!.initializer?.expression
|
||||||
evaluateConstantValue(value.valueArguments[index])
|
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 {
|
}.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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-37
@@ -52,9 +52,6 @@ internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDe
|
|||||||
fun forStaticField(field: IrField) = (field.metadata as? CodegenStaticFieldMetadata)?.llvm ?:
|
fun forStaticField(field: IrField) = (field.metadata as? CodegenStaticFieldMetadata)?.llvm ?:
|
||||||
error(field.descriptor.toString())
|
error(field.descriptor.toString())
|
||||||
|
|
||||||
fun forSingleton(irClass: IrClass) = forClass(irClass).singletonDeclarations ?:
|
|
||||||
error(irClass.descriptor.toString())
|
|
||||||
|
|
||||||
fun forUnique(kind: UniqueKind) = unique[kind] ?: error("No unique $kind")
|
fun forUnique(kind: UniqueKind) = unique[kind] ?: error("No unique $kind")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -64,11 +61,8 @@ internal class ClassLlvmDeclarations(
|
|||||||
val typeInfoGlobal: StaticData.Global,
|
val typeInfoGlobal: StaticData.Global,
|
||||||
val writableTypeInfoGlobal: StaticData.Global?,
|
val writableTypeInfoGlobal: StaticData.Global?,
|
||||||
val typeInfo: ConstPointer,
|
val typeInfo: ConstPointer,
|
||||||
val singletonDeclarations: SingletonLlvmDeclarations?,
|
|
||||||
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
|
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
|
||||||
|
|
||||||
internal class SingletonLlvmDeclarations(val instanceStorage: AddressAccess)
|
|
||||||
|
|
||||||
internal class KotlinObjCClassLlvmDeclarations(
|
internal class KotlinObjCClassLlvmDeclarations(
|
||||||
val classInfoGlobal: StaticData.Global,
|
val classInfoGlobal: StaticData.Global,
|
||||||
val bodyOffsetGlobal: StaticData.Global
|
val bodyOffsetGlobal: StaticData.Global
|
||||||
@@ -219,12 +213,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
if (declaration.isUnit() || declaration.isKotlinArray())
|
if (declaration.isUnit() || declaration.isKotlinArray())
|
||||||
createUniqueDeclarations(declaration, typeInfoPtr, bodyType)
|
createUniqueDeclarations(declaration, typeInfoPtr, bodyType)
|
||||||
|
|
||||||
val singletonDeclarations = if (declaration.kind.isSingleton) {
|
|
||||||
createSingletonDeclarations(declaration)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val objCDeclarations = if (declaration.isKotlinObjCClass()) {
|
val objCDeclarations = if (declaration.isKotlinObjCClass()) {
|
||||||
createKotlinObjCClassDeclarations(declaration)
|
createKotlinObjCClassDeclarations(declaration)
|
||||||
} else {
|
} else {
|
||||||
@@ -245,8 +233,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
it.setZeroInitializer()
|
it.setZeroInitializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
|
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr, objCDeclarations)
|
||||||
singletonDeclarations, objCDeclarations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUniqueDeclarations(
|
private fun createUniqueDeclarations(
|
||||||
@@ -264,28 +251,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSingletonDeclarations(irClass: IrClass): SingletonLlvmDeclarations? {
|
|
||||||
if (irClass.isUnit()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val storageKind = irClass.storageKind(context)
|
|
||||||
val threadLocal = storageKind == ObjectStorageKind.THREAD_LOCAL
|
|
||||||
val isExported = irClass.isExported()
|
|
||||||
val symbolName = if (isExported) {
|
|
||||||
irClass.globalObjectStorageSymbolName
|
|
||||||
} else {
|
|
||||||
"kobjref:" + qualifyInternalName(irClass)
|
|
||||||
}
|
|
||||||
val instanceAddress = if (threadLocal) {
|
|
||||||
addKotlinThreadLocal(symbolName, irClass.defaultType.toLLVMType(llvm))
|
|
||||||
} else {
|
|
||||||
addKotlinGlobal(symbolName, irClass.defaultType.toLLVMType(llvm), isExported)
|
|
||||||
}
|
|
||||||
|
|
||||||
return SingletonLlvmDeclarations(instanceAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createKotlinObjCClassDeclarations(irClass: IrClass): KotlinObjCClassLlvmDeclarations {
|
private fun createKotlinObjCClassDeclarations(irClass: IrClass): KotlinObjCClassLlvmDeclarations {
|
||||||
val internalName = qualifyInternalName(irClass)
|
val internalName = qualifyInternalName(irClass)
|
||||||
|
|
||||||
@@ -312,7 +277,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
super.visitField(declaration)
|
super.visitField(declaration)
|
||||||
|
|
||||||
val containingClass = declaration.parent as? IrClass
|
val containingClass = declaration.parent as? IrClass
|
||||||
if (containingClass != null) {
|
if (containingClass != null && !declaration.isStatic) {
|
||||||
if (!containingClass.requiresRtti()) return
|
if (!containingClass.requiresRtti()) return
|
||||||
val classDeclarations = (containingClass.metadata as? CodegenClassMetadata)?.llvm
|
val classDeclarations = (containingClass.metadata as? CodegenClassMetadata)?.llvm
|
||||||
?: error(containingClass.descriptor.toString())
|
?: error(containingClass.descriptor.toString())
|
||||||
|
|||||||
+4
-10
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
|
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isAny
|
import org.jetbrains.kotlin.backend.konan.ir.isAny
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering.Companion.isLoweredFunctionReference
|
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering.Companion.isLoweredFunctionReference
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
@@ -463,17 +464,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val associatedObjectTableRecords = associatedObjects.map { (key, value) ->
|
val associatedObjectTableRecords = associatedObjects.map { (key, value) ->
|
||||||
val associatedObjectGetter = generateFunction(
|
val function = context.getObjectClassInstanceFunction(value)
|
||||||
CodeGenerator(context),
|
val llvmFunction = context.generationState.llvmDeclarations.forFunction(function).llvmValue
|
||||||
functionType(kObjHeaderPtr, false, kObjHeaderPtrPtr),
|
|
||||||
""
|
|
||||||
) {
|
|
||||||
ret(getObjectValue(value, ExceptionHandler.Caller, startLocationInfo = null))
|
|
||||||
}.also {
|
|
||||||
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
|
|
||||||
}
|
|
||||||
|
|
||||||
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(associatedObjectGetter))
|
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(llvmFunction))
|
||||||
}
|
}
|
||||||
|
|
||||||
return staticData.placeGlobalConstArray(
|
return staticData.placeGlobalConstArray(
|
||||||
|
|||||||
+18
-8
@@ -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.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
|
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
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.objcexport.*
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
@@ -995,7 +996,8 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
|||||||
target: IrFunction?,
|
target: IrFunction?,
|
||||||
baseMethod: IrFunction,
|
baseMethod: IrFunction,
|
||||||
methodBridge: MethodBridge,
|
methodBridge: MethodBridge,
|
||||||
isVirtual: Boolean = false
|
isVirtual: Boolean = false,
|
||||||
|
customBridgeSuffix: String? = null,
|
||||||
) = if (target == null) {
|
) = if (target == null) {
|
||||||
generateAbstractObjCImp(methodBridge, baseMethod)
|
generateAbstractObjCImp(methodBridge, baseMethod)
|
||||||
} else {
|
} else {
|
||||||
@@ -1003,7 +1005,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
|||||||
methodBridge,
|
methodBridge,
|
||||||
isDirect = !isVirtual,
|
isDirect = !isVirtual,
|
||||||
baseMethod = baseMethod,
|
baseMethod = baseMethod,
|
||||||
bridgeSuffix = (if (isVirtual) "virtual_" else "") + target.computeSymbolName()
|
bridgeSuffix = customBridgeSuffix ?: ((if (isVirtual) "virtual_" else "") + target.computeSymbolName())
|
||||||
) { args, resultLifetime, exceptionHandler ->
|
) { args, resultLifetime, exceptionHandler ->
|
||||||
if (target is IrConstructor && target.constructedClass.isAbstract()) {
|
if (target is IrConstructor && target.constructedClass.isAbstract()) {
|
||||||
callFromBridge(
|
callFromBridge(
|
||||||
@@ -1913,12 +1915,20 @@ private fun ObjCExportCodeGenerator.createObjectInstanceAdapter(
|
|||||||
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
|
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
|
||||||
assert(objectClass.kind == ClassKind.OBJECT)
|
assert(objectClass.kind == ClassKind.OBJECT)
|
||||||
assert(!objectClass.isUnit())
|
assert(!objectClass.isUnit())
|
||||||
val bridgeSuffix = "${owner.computeTypeInfoSymbolName()}#$selector"
|
|
||||||
return generateObjCToKotlinSyntheticGetter(selector, bridgeSuffix) {
|
val methodBridge = MethodBridge(
|
||||||
initRuntimeIfNeeded() // For instance methods it gets called when allocating.
|
returnBridge = MethodBridge.ReturnValue.Mapped(ReferenceBridge),
|
||||||
val value = getObjectValue(objectClass, startLocationInfo = null, exceptionHandler = ExceptionHandler.Caller)
|
receiver = MethodBridgeReceiver.Static,
|
||||||
autoreleaseAndRet(kotlinReferenceToRetainedObjC(value))
|
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(
|
private fun ObjCExportCodeGenerator.createEnumEntryAdapter(
|
||||||
|
|||||||
+3
-3
@@ -239,7 +239,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
|||||||
|
|
||||||
val field = expression.symbol.owner
|
val field = expression.symbol.owner
|
||||||
val parentClass = field.parentClassOrNull
|
val parentClass = field.parentClassOrNull
|
||||||
return if (parentClass == null || !parentClass.isInlined())
|
return if (parentClass == null || !parentClass.isInlined() || field.isStatic)
|
||||||
expression
|
expression
|
||||||
else {
|
else {
|
||||||
builder.at(expression)
|
builder.at(expression)
|
||||||
@@ -254,7 +254,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
|||||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||||
super.visitSetField(expression)
|
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:
|
// Happens in one of the cases:
|
||||||
// 1. In primary constructor of the inlined class. Makes no sense, "has no effect", can be removed.
|
// 1. In primary constructor of the inlined class. Makes no sense, "has no effect", can be removed.
|
||||||
// The constructor will be lowered and used.
|
// The constructor will be lowered and used.
|
||||||
@@ -442,7 +442,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
|||||||
lateinit var thisVar: IrValueDeclaration
|
lateinit var thisVar: IrValueDeclaration
|
||||||
|
|
||||||
fun IrBuilderWithScope.genReturnValue(): IrExpression = if (irConstructor.isPrimary) {
|
fun IrBuilderWithScope.genReturnValue(): IrExpression = if (irConstructor.isPrimary) {
|
||||||
irGetObject(irBuiltIns.unitClass)
|
irCall(symbols.theUnitInstance)
|
||||||
} else {
|
} else {
|
||||||
irGet(thisVar)
|
irGet(thisVar)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-80
@@ -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.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.NativeMapping
|
import org.jetbrains.kotlin.backend.konan.NativeMapping
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
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.ir.llvmSymbolOrigin
|
||||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetField
|
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.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.*
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
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
|
* In case of compiler caches, this means that it is not accessible as Lazy IR
|
||||||
* and we have to explicitly add an external declaration.
|
* and we have to explicitly add an external declaration.
|
||||||
*/
|
*/
|
||||||
internal class CachesAbiSupport(mapping: NativeMapping, symbols: KonanSymbols, private val irFactory: IrFactory) {
|
internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: IrFactory) {
|
||||||
private val companionObjectAccessors = mapping.companionObjectCacheAccessors
|
|
||||||
private val outerThisAccessors = mapping.outerThisCacheAccessors
|
private val outerThisAccessors = mapping.outerThisCacheAccessors
|
||||||
private val lateinitPropertyAccessors = mapping.lateinitPropertyCacheAccessors
|
private val lateinitPropertyAccessors = mapping.lateinitPropertyCacheAccessors
|
||||||
private val enumValuesAccessors = mapping.enumValuesCacheAccessors
|
|
||||||
private val lateInitFieldToNullableField = mapping.lateInitFieldToNullableField
|
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 {
|
fun getOuterThisAccessor(irClass: IrClass): IrSimpleFunction {
|
||||||
require(irClass.isInner) { "Expected an inner class but was: ${irClass.render()}" }
|
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.
|
* 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.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) {
|
if (declaration.isInner) {
|
||||||
val function = cachesAbiSupport.getOuterThisAccessor(declaration)
|
val function = cachesAbiSupport.getOuterThisAccessor(declaration)
|
||||||
@@ -165,16 +124,6 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass,
|
|||||||
}
|
}
|
||||||
data.add(function)
|
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<IrFunction>) {
|
override fun visitProperty(declaration: IrProperty, data: MutableList<IrFunction>) {
|
||||||
@@ -198,29 +147,12 @@ internal class ExportCachesAbiVisitor(val context: Context) : FileLoweringPass,
|
|||||||
|
|
||||||
internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPass, IrElementTransformerVoid() {
|
internal class ImportCachesAbiTransformer(val context: Context) : FileLoweringPass, IrElementTransformerVoid() {
|
||||||
private val cachesAbiSupport = context.cachesAbiSupport
|
private val cachesAbiSupport = context.cachesAbiSupport
|
||||||
private val enumsSupport = context.enumsSupport
|
|
||||||
private val llvmImports = context.generationState.llvmImports
|
private val llvmImports = context.generationState.llvmImports
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
irFile.transformChildrenVoid(this)
|
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 {
|
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
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
|
else -> expression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+115
-160
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.lower
|
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.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.getOrPut
|
import org.jetbrains.kotlin.backend.common.getOrPut
|
||||||
import org.jetbrains.kotlin.backend.common.lower.EnumWhenLowering
|
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.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.NativeMapping
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.KonanNameConventions
|
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.IntrinsicType
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
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.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
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.buildField
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
@@ -44,17 +43,29 @@ internal data class LoweredEnumEntryDescription(val ordinal: Int, val getterId:
|
|||||||
|
|
||||||
internal class EnumsSupport(
|
internal class EnumsSupport(
|
||||||
mapping: NativeMapping,
|
mapping: NativeMapping,
|
||||||
symbols: KonanSymbols,
|
|
||||||
private val irBuiltIns: IrBuiltIns,
|
private val irBuiltIns: IrBuiltIns,
|
||||||
private val irFactory: IrFactory,
|
private val irFactory: IrFactory,
|
||||||
) {
|
) {
|
||||||
private val enumImplObjects = mapping.enumImplObjects
|
|
||||||
private val enumValueGetters = mapping.enumValueGetters
|
private val enumValueGetters = mapping.enumValueGetters
|
||||||
private val enumEntriesMaps = mapping.enumEntriesMaps
|
private val enumEntriesMaps = mapping.enumEntriesMaps
|
||||||
private val array = symbols.array
|
|
||||||
private val enumEntries = symbols.enumEntriesInterface
|
private val valuesFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||||
private val genericValueOfSymbol = symbols.valueOfForEnum
|
private val valueOfFunctions = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
|
||||||
private val genericValuesSymbol = symbols.valuesForEnum
|
|
||||||
|
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<Name, LoweredEnumEntryDescription> {
|
fun enumEntriesMap(enumClass: IrClass): Map<Name, LoweredEnumEntryDescription> {
|
||||||
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
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 {
|
fun getValueGetter(enumClass: IrClass): IrFunction {
|
||||||
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
|
||||||
return enumValueGetters.getOrPut(enumClass) {
|
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 {
|
companion object {
|
||||||
val valuesFieldName = "VALUES".synthesizedName
|
val valuesFunctionName = Name.identifier("values")
|
||||||
val entriesFieldName = "ENTRIES".synthesizedName
|
val valueOfFunctionName = Name.identifier("valueOf")
|
||||||
val implObjectName = "OBJECT".synthesizedName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,8 +119,6 @@ internal class NativeEnumWhenLowering constructor(context: Context) : EnumWhenLo
|
|||||||
|
|
||||||
internal class EnumUsageLowering(val context: Context) : IrElementTransformer<IrBuilderWithScope?>, FileLoweringPass {
|
internal class EnumUsageLowering(val context: Context) : IrElementTransformer<IrBuilderWithScope?>, FileLoweringPass {
|
||||||
private val enumsSupport = context.enumsSupport
|
private val enumsSupport = context.enumsSupport
|
||||||
private val symbols = context.ir.symbols
|
|
||||||
private val arrayGet = symbols.arrayGet[symbols.array]!!
|
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
visitFile(irFile, data = null)
|
visitFile(irFile, data = null)
|
||||||
@@ -206,19 +153,21 @@ internal class EnumUsageLowering(val context: Context) : IrElementTransformer<Ir
|
|||||||
|
|
||||||
require(irClass.kind == ClassKind.ENUM_CLASS)
|
require(irClass.kind == ClassKind.ENUM_CLASS)
|
||||||
|
|
||||||
return with(enumsSupport) {
|
return when (intrinsicType) {
|
||||||
if (intrinsicType == IntrinsicType.ENUM_VALUES)
|
IntrinsicType.ENUM_VALUES -> data.irCall(enumsSupport.getValuesFunction(irClass))
|
||||||
data.irEnumValues(irClass)
|
IntrinsicType.ENUM_VALUE_OF -> data.irCall(enumsSupport.getValueOfFunction(irClass)).apply {
|
||||||
else
|
putValueArgument(0, expression.getValueArgument(0)!!)
|
||||||
data.irEnumValueOf(irClass, 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 {
|
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 createEnumEntries = symbols.createEnumEntries
|
||||||
private val initInstance = symbols.initInstance
|
private val initInstance = symbols.initInstance
|
||||||
private val arrayGet = symbols.array.owner.functions.single { it.name == KonanNameConventions.getWithoutBoundCheck }.symbol
|
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) {
|
override fun lower(irFile: IrFile) {
|
||||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
@@ -242,14 +190,37 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class EnumClassTransformer(val irClass: IrClass) {
|
private inner class EnumClassTransformer(val irClass: IrClass) {
|
||||||
private val implObject = enumsSupport.getImplObject(irClass)
|
private val valuesField = context.irFactory.buildField {
|
||||||
private val valuesField = enumsSupport.getValuesField(implObject)
|
startOffset = irClass.startOffset
|
||||||
private val entriesField = enumsSupport.getEntriesField(implObject)
|
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)
|
private val enumEntriesMap = enumsSupport.enumEntriesMap(irClass)
|
||||||
|
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
val enumEntries = transformEnumBody()
|
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()
|
defineValueGetter()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,23 +234,31 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
|||||||
declaration.correspondingClass = null
|
declaration.correspondingClass = null
|
||||||
listOfNotNull(correspondingClass)
|
listOfNotNull(correspondingClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enumsSupport.saveValuesFunction(irClass)
|
||||||
|
enumsSupport.saveValueOfFunction(irClass)
|
||||||
irClass.simpleFunctions().forEach { declaration ->
|
irClass.simpleFunctions().forEach { declaration ->
|
||||||
val body = declaration.body
|
val body = declaration.body
|
||||||
if (body is IrSyntheticBody) {
|
if (body is IrSyntheticBody) {
|
||||||
declaration.body = when (body.kind) {
|
declaration.body = when (body.kind) {
|
||||||
IrSyntheticBodyKind.ENUM_VALUEOF -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
|
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) {
|
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) {
|
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 {
|
context.createIrBuilder(valueGetter.symbol).run {
|
||||||
valueGetter.body = irBlockBody(valueGetter) {
|
valueGetter.body = irBlockBody(valueGetter) {
|
||||||
+irReturn(irCall(arrayGet, irClass.defaultType).apply {
|
+irReturn(irCall(arrayGet, irClass.defaultType).apply {
|
||||||
dispatchReceiver = with(enumsSupport) { irGetValuesField(irClass) }
|
dispatchReceiver = irGetField(null, valuesField)
|
||||||
putValueArgument(0, irGet(valueGetter.valueParameters[0]))
|
putValueArgument(0, irGet(valueGetter.valueParameters[0]))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -300,49 +279,18 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
|||||||
irClass.declarations.add(valueGetter)
|
irClass.declarations.add(valueGetter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun defineImplObject(enumEntries: List<IrEnumEntry>) {
|
private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall =
|
||||||
implObject.createParameterDeclarations()
|
irCall(initInstance).apply {
|
||||||
|
putValueArgument(0, instance)
|
||||||
implObject.addSimpleDelegatingConstructor(constructorOfAny, context.irBuiltIns, true /* TODO: why primary? */)
|
putValueArgument(1, constructor)
|
||||||
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<IrEnumEntry>) {
|
|
||||||
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!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irClass.companionObject()?.let { statements += irBuilder.irGetObject(it.symbol) }
|
private fun defineValuesField(enumEntries: List<IrEnumEntry>) {
|
||||||
}
|
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 =
|
valuesField.initializer = irBuilder.irExprBody(irBuilder.irBlock {
|
||||||
irCall(initInstance).apply {
|
|
||||||
putValueArgument(0, instance)
|
|
||||||
putValueArgument(1, constructor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildValuesFieldInitializer(enumEntries: List<IrEnumEntry>) = irBuilder.run {
|
|
||||||
val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression(
|
val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression(
|
||||||
startOffset, endOffset,
|
startOffset, endOffset,
|
||||||
irClass.defaultType,
|
irClass.defaultType,
|
||||||
@@ -363,11 +311,22 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
|
|||||||
irCall(createUninitializedInstance, listOf(entryClass.defaultType))
|
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 {
|
fun defineEntriesField() {
|
||||||
val irValuesArray = irTemporary(irGetField(irGet(implObject.thisReceiver!!), valuesField))
|
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(
|
val irEntriesArray = this@EnumClassLowering.context.createArrayOfExpression(
|
||||||
startOffset, endOffset,
|
startOffset, endOffset,
|
||||||
irClass.defaultType,
|
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)
|
putValueArgument(0, irEntriesArray)
|
||||||
}
|
}
|
||||||
+irSetField(irGet(implObject.thisReceiver!!), entriesField, irEntriesInitializer)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun callEnumEntriesConstructors(enumEntries: List<IrEnumEntry>) = irBuilder.irBlock {
|
private fun IrBlockBuilder.callEnumEntriesConstructors(instances: IrVariable, enumEntries: List<IrEnumEntry>) {
|
||||||
val receiver = implObject.thisReceiver!!
|
enumEntries.forEach {
|
||||||
val instances = irTemporary(irGetField(irGet(receiver), valuesField))
|
val instance = irCall(arrayGet).apply {
|
||||||
enumEntries.forEach {
|
dispatchReceiver = irGet(instances)
|
||||||
val instance = irCall(arrayGet).apply {
|
putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId))
|
||||||
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 -> {
|
else -> error("Unexpected initializer: $initializer")
|
||||||
val statements = initializer.statements
|
|
||||||
val constructorCall = statements.last() as IrConstructorCall
|
|
||||||
statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall)
|
|
||||||
+initializer
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> error("Unexpected initializer: $initializer")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+174
@@ -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")
|
||||||
|
|
||||||
|
}
|
||||||
+3
-5
@@ -9,13 +9,11 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.builders.irGetObject
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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.isNullable
|
||||||
import org.jetbrains.kotlin.ir.types.isNullableNothing
|
import org.jetbrains.kotlin.ir.types.isNullableNothing
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
@@ -38,7 +36,7 @@ internal class ReturnsInsertionLowering(val context: Context) : FileLoweringPass
|
|||||||
body as IrBlockBody
|
body as IrBlockBody
|
||||||
context.createIrBuilder(declaration.symbol, declaration.endOffset, declaration.endOffset).run {
|
context.createIrBuilder(declaration.symbol, declaration.endOffset, declaration.endOffset).run {
|
||||||
if (declaration is IrConstructor || declaration.returnType == context.irBuiltIns.unitType) {
|
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()) {
|
} else if (declaration.returnType.isNullable()) {
|
||||||
// this is a workaround for KT-42832
|
// this is a workaround for KT-42832
|
||||||
val typeOperatorCall = body.statements.lastOrNull() as? IrTypeOperatorCall
|
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) {
|
if (expression.inlineFunctionSymbol?.owner?.returnType == context.irBuiltIns.unitType) {
|
||||||
val offset = (expression.statements.lastOrNull() ?: expression).endOffset
|
val offset = (expression.statements.lastOrNull() ?: expression).endOffset
|
||||||
context.createIrBuilder(expression.symbol, offset, offset).run {
|
context.createIrBuilder(expression.symbol, offset, offset).run {
|
||||||
expression.statements += irReturn(irGetObject(symbols.unit))
|
expression.statements += irReturn(irCall(symbols.theUnitInstance, context.irBuiltIns.unitType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.konan.ConfigChecks
|
|||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT
|
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind
|
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.needsGCRegistration
|
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.storageKind
|
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
|||||||
+2
-1
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.konan.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall
|
import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall
|
||||||
import org.jetbrains.kotlin.backend.konan.util.IntArrayList
|
import org.jetbrains.kotlin.backend.konan.util.IntArrayList
|
||||||
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
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.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
@@ -106,7 +107,7 @@ internal object DevirtualizationAnalysis {
|
|||||||
|
|
||||||
context.getLayoutBuilder(declaration).associatedObjects.values.forEach {
|
context.getLayoutBuilder(declaration).associatedObjects.values.forEach {
|
||||||
assert(it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" }
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-11
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.optimizations
|
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.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.IrBuilderWithScope
|
||||||
import org.jetbrains.kotlin.ir.builders.irBlock
|
import org.jetbrains.kotlin.ir.builders.irBlock
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
@@ -427,15 +425,7 @@ internal object StaticInitializersOptimization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
|
override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
|
||||||
val objectClass = expression.symbol.owner
|
error("IrGetObjectValue should be lowered away at this point")
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processCall(expression: IrFunctionAccessExpression, actualCallee: IrFunction, data: BitSet): BitSet {
|
private fun processCall(expression: IrFunctionAccessExpression, actualCallee: IrFunction, data: BitSet): BitSet {
|
||||||
|
|||||||
+2
-2
@@ -24,12 +24,12 @@ func testKotlin2Objc() throws {
|
|||||||
|
|
||||||
func testCompanionObject() throws {
|
func testCompanionObject() throws {
|
||||||
let trace = Foo.companion.trace
|
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 {
|
func testStandaloneObject() throws {
|
||||||
let trace = Object.shared.trace
|
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 {
|
func testEnumEntry() throws {
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ touchType(KRefSharedHolder)
|
|||||||
|
|
||||||
touchFunction(AllocInstance)
|
touchFunction(AllocInstance)
|
||||||
touchFunction(AllocArrayInstance)
|
touchFunction(AllocArrayInstance)
|
||||||
touchFunction(InitThreadLocalSingleton)
|
|
||||||
touchFunction(InitSingleton)
|
|
||||||
touchFunction(InitAndRegisterGlobal)
|
touchFunction(InitAndRegisterGlobal)
|
||||||
touchFunction(UpdateHeapRef)
|
touchFunction(UpdateHeapRef)
|
||||||
touchFunction(UpdateStackRef)
|
touchFunction(UpdateStackRef)
|
||||||
|
|||||||
@@ -2373,104 +2373,6 @@ OBJ_GETTER(allocArrayInstance, const TypeInfo* type_info, int32_t elements) {
|
|||||||
RETURN_OBJ(container.GetPlace()->obj());
|
RETURN_OBJ(container.GetPlace()->obj());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Strict>
|
|
||||||
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<Strict>(typeInfo, OBJ_RESULT);
|
|
||||||
updateHeapRef<Strict>(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 <bool Strict>
|
|
||||||
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
|
* 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.
|
* 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<false>, typeInfo, elements);
|
RETURN_RESULT_OF(allocArrayInstance<false>, typeInfo, elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
|
||||||
RETURN_RESULT_OF(initThreadLocalSingleton<true>, location, typeInfo, ctor);
|
|
||||||
}
|
|
||||||
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
|
||||||
RETURN_RESULT_OF(initThreadLocalSingleton<false>, location, typeInfo, ctor);
|
|
||||||
}
|
|
||||||
|
|
||||||
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
|
||||||
RETURN_RESULT_OF(initSingleton<true>, location, typeInfo, ctor);
|
|
||||||
}
|
|
||||||
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
|
||||||
RETURN_RESULT_OF(initSingleton<false>, location, typeInfo, ctor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||||
RuntimeCheck(false, "Global registration is impossible in legacy MM");
|
RuntimeCheck(false, "Global registration is impossible in legacy MM");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements);
|
||||||
OBJ_GETTER(AllocArrayInstanceRelaxed, 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, SetStackRef, ObjHeader** location, const ObjHeader* object);
|
||||||
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ void ensureUsed(Ret (*f)(Args...)) {
|
|||||||
void EnsureDeclarationsEmitted() {
|
void EnsureDeclarationsEmitted() {
|
||||||
ensureUsed(AllocInstance);
|
ensureUsed(AllocInstance);
|
||||||
ensureUsed(AllocArrayInstance);
|
ensureUsed(AllocArrayInstance);
|
||||||
ensureUsed(InitThreadLocalSingleton);
|
|
||||||
ensureUsed(InitSingleton);
|
|
||||||
ensureUsed(InitAndRegisterGlobal);
|
ensureUsed(InitAndRegisterGlobal);
|
||||||
ensureUsed(UpdateHeapRef);
|
ensureUsed(UpdateHeapRef);
|
||||||
ensureUsed(UpdateStackRef);
|
ensureUsed(UpdateStackRef);
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ void VLog(Level level, std::initializer_list<const char*> tags, const char* form
|
|||||||
|
|
||||||
inline constexpr const char* kTagGC = "gc";
|
inline constexpr const char* kTagGC = "gc";
|
||||||
inline constexpr const char* kTagMM = "mm";
|
inline constexpr const char* kTagMM = "mm";
|
||||||
|
inline constexpr const char* kTagTLS = "tls";
|
||||||
|
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|
||||||
|
|||||||
@@ -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(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
|
// `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already
|
||||||
// set by static initialization.
|
// set by static initialization.
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class IntrinsicType {
|
|||||||
const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB"
|
const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB"
|
||||||
const val INIT_INSTANCE = "INIT_INSTANCE"
|
const val INIT_INSTANCE = "INIT_INSTANCE"
|
||||||
const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM"
|
const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM"
|
||||||
|
const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE"
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
const val ENUM_VALUES = "ENUM_VALUES"
|
const val ENUM_VALUES = "ENUM_VALUES"
|
||||||
|
|||||||
@@ -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).
|
// 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, R> T.reinterpret(): R
|
@TypedIntrinsic(IntrinsicType.IDENTITY) @PublishedApi external internal fun <T, R> T.reinterpret(): R
|
||||||
|
|
||||||
|
|
||||||
|
@TypedIntrinsic(IntrinsicType.THE_UNIT_INSTANCE) @ExportForCompiler external internal fun theUnitInstance(): Unit
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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 <atomic>
|
|
||||||
|
|
||||||
#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<Field, 0> 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<void(ObjHeader*)>& 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<testing::MockFunction<void(ObjHeader*)>> constructor_;
|
|
||||||
test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
|
|
||||||
|
|
||||||
static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
|
|
||||||
|
|
||||||
static void constructorImpl(ObjHeader* object) { globalConstructor_->Call(object); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// static
|
|
||||||
testing::MockFunction<void(ObjHeader*)>* 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<bool> canStart(false);
|
|
||||||
std::atomic<size_t> readyCount(0);
|
|
||||||
std_support::vector<ScopedThread> threads;
|
|
||||||
ObjHeader* location = nullptr;
|
|
||||||
std_support::vector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
|
||||||
std_support::vector<ObjHeader*> 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<bool> canStart(false);
|
|
||||||
std::atomic<size_t> readyCount(0);
|
|
||||||
std_support::vector<ScopedThread> threads;
|
|
||||||
constexpr int kException = 42;
|
|
||||||
ObjHeader* location = nullptr;
|
|
||||||
std_support::vector<ObjHeader*> 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));
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "Freezing.hpp"
|
#include "Freezing.hpp"
|
||||||
#include "GC.hpp"
|
#include "GC.hpp"
|
||||||
#include "GlobalsRegistry.hpp"
|
#include "GlobalsRegistry.hpp"
|
||||||
#include "InitializationScheme.hpp"
|
|
||||||
#include "KAssert.h"
|
#include "KAssert.h"
|
||||||
#include "Natives.h"
|
#include "Natives.h"
|
||||||
#include "ObjectOps.hpp"
|
#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<uint32_t>(elements));
|
RETURN_RESULT_OF(mm::AllocateArray, threadData, typeInfo, static_cast<uint32_t>(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) {
|
extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
|
||||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||||
AssertThreadState(threadData, ThreadState::kRunnable);
|
AssertThreadState(threadData, ThreadState::kRunnable);
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ThreadLocalStorage.hpp"
|
#include "ThreadLocalStorage.hpp"
|
||||||
|
#include "Logging.hpp"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
void mm::ThreadLocalStorage::AddRecord(Key key, int size) noexcept {
|
||||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||||
RuntimeAssert(size >= 0, "Size cannot be negative");
|
RuntimeAssert(size >= 0, "Size cannot be negative");
|
||||||
|
RuntimeLogDebug({kTagTLS}, "Add record key = %p, size = %d\n", key, size);
|
||||||
auto it = map_.find(key);
|
auto it = map_.find(key);
|
||||||
if (it != map_.end()) {
|
if (it != map_.end()) {
|
||||||
RuntimeAssert(it->second.size == size, "Attempt to add TLS record with the same key, but different size");
|
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 {
|
void mm::ThreadLocalStorage::Commit() noexcept {
|
||||||
|
RuntimeLogDebug({kTagTLS}, "Committed");
|
||||||
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
RuntimeAssert(state_ == State::kBuilding, "Storage must be in the building state");
|
||||||
storage_.resize(size_);
|
storage_.resize(size_);
|
||||||
state_ = State::kCommitted;
|
state_ = State::kCommitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mm::ThreadLocalStorage::Clear() noexcept {
|
void mm::ThreadLocalStorage::Clear() noexcept {
|
||||||
|
RuntimeLogDebug({kTagTLS}, "Cleared");
|
||||||
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||||
// Just free the storage.
|
// Just free the storage.
|
||||||
storage_.clear();
|
storage_.clear();
|
||||||
@@ -33,6 +37,7 @@ void mm::ThreadLocalStorage::Clear() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ObjHeader** mm::ThreadLocalStorage::Lookup(Key key, int index) 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");
|
RuntimeAssert(state_ == State::kCommitted, "Storage must be in the committed state");
|
||||||
if (lastKeyAndEntry_.first == key) {
|
if (lastKeyAndEntry_.first == key) {
|
||||||
return Lookup(lastKeyAndEntry_.second, index);
|
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 {
|
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");
|
RuntimeAssert(index < entry.size, "Out of bounds TLS access");
|
||||||
return &storage_[entry.offset + index];
|
return &storage_[entry.offset + index];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
|||||||
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, 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) {
|
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||||
ReleaseHeapRefRelaxed(object);
|
ReleaseHeapRefRelaxed(object);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
|||||||
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, 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) {
|
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
|
||||||
ReleaseHeapRefStrict(object);
|
ReleaseHeapRefStrict(object);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user