Do not generate getters for global singletons (#4166)

This commit is contained in:
Alexander Shabalin
2020-05-19 17:47:35 +03:00
committed by GitHub
parent 07e6590ad3
commit bd42c443f9
6 changed files with 112 additions and 72 deletions
@@ -85,7 +85,16 @@ internal val IrClass.writableTypeInfoSymbolName: String
return "ktypew:" + this.fqNameForIrSerialization.toString()
}
internal val IrClass.objectInstanceGetterSymbolName: String
internal val IrClass.globalObjectStorageSymbolName: String
get() {
assert (this.isExported())
assert (this.kind.isSingleton)
assert (!this.isUnit())
return "kobjref:$fqNameForIrSerialization"
}
internal val IrClass.threadLocalObjectStorageGetterSymbolName: String
get() {
assert (this.isExported())
assert (this.kind.isSingleton)
@@ -901,31 +901,56 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
}
}
// If object is imported - access it via getter function.
if (isExternal(irClass)) {
val valueGetterName = irClass.objectInstanceGetterSymbolName
val valueGetterFunction = LLVMGetNamedFunction(context.llvmModule, valueGetterName) ?:
LLVMAddFunction(context.llvmModule, valueGetterName,
functionType(kObjHeaderPtr, false, kObjHeaderPtrPtr))
return call(valueGetterFunction!!,
listOf(),
resultLifetime = Lifetime.GLOBAL,
exceptionHandler = 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(context.llvmModule, valueGetterName)
?: LLVMAddFunction(context.llvmModule, 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 = getLLVMType(irClass.defaultType)
importGlobal(
irClass.globalObjectStorageSymbolName,
llvmType,
origin = irClass.llvmSymbolOrigin
)
}
}
} else {
// Local globals and thread locals storage info is stored in our map.
val singleton = context.llvmDeclarations.forSingleton(irClass)
val instanceAddress = singleton.instanceStorage
instanceAddress.getAddress(this)
}
val storageKind = irClass.storageKind(context)
when (storageKind) {
ObjectStorageKind.SHARED -> context.llvm.sharedObjects += irClass
ObjectStorageKind.THREAD_LOCAL -> context.llvm.objects += irClass
ObjectStorageKind.SHARED ->
// If current file used a shared object, make file's (de)initializer function deinit it.
context.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))
context.llvm.fileUsesThreadLocalObjects = true
ObjectStorageKind.PERMANENT -> { /* Do nothing, no need to free such an instance. */ }
}
val singleton = context.llvmDeclarations.forSingleton(irClass)
val instanceAddress = singleton.instanceStorage
if (storageKind == ObjectStorageKind.PERMANENT) {
return loadSlot(instanceAddress.getAddress(this), false)
return loadSlot(objectPtr, false)
}
val objectPtr = instanceAddress.getAddress(this)
val bbInit = basicBlock("label_init", startLocationInfo, endLocationInfo)
val bbExit = basicBlock("label_continue", startLocationInfo, endLocationInfo)
val objectVal = loadSlot(objectPtr, false)
@@ -557,8 +557,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val irStaticInitializers = mutableListOf<IrStaticInitializer>()
val otherStaticInitializers = mutableListOf<LLVMValueRef>()
val fileInitializers = mutableListOf<IrField>()
val objects = mutableSetOf<IrClass>()
val sharedObjects = mutableSetOf<IrClass>()
var fileUsesThreadLocalObjects = false
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
private object lazyRtFunction {
operator fun provideDelegate(
@@ -455,10 +455,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
storeHeapRef(codegen.kNullObjHeaderPtr, address)
}
}
context.llvm.sharedObjects.forEach { irClass ->
val address = context.llvmDeclarations.forSingleton(irClass).instanceStorage.getAddress(
functionGenerationContext
)
context.llvm.globalSharedObjects.forEach { address ->
storeHeapRef(codegen.kNullObjHeaderPtr, address)
}
ret(null)
@@ -498,14 +495,14 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
override fun visitFile(declaration: IrFile) {
// TODO: collect those two in one place.
context.llvm.fileInitializers.clear()
context.llvm.objects.clear()
context.llvm.sharedObjects.clear()
context.llvm.fileUsesThreadLocalObjects = false
context.llvm.globalSharedObjects.clear()
@Suppress("UNCHECKED_CAST")
using(FileScope(declaration)) {
declaration.acceptChildrenVoid(this)
if (context.llvm.fileInitializers.isEmpty() && context.llvm.objects.isEmpty() && context.llvm.sharedObjects.isEmpty())
if (context.llvm.fileInitializers.isEmpty() && !context.llvm.fileUsesThreadLocalObjects && context.llvm.globalSharedObjects.isEmpty())
return
// Create global initialization records.
@@ -776,20 +773,28 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val singleton = context.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)
context.llvm.staticData.createConstKotlinObject(declaration,
*computeFields(declaration)).llvm else codegen.kNullObjHeaderPtr)
}
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.objectInstanceGetterSymbolName
generateFunction(codegen,
LLVMFunctionType(codegen.kObjHeaderPtr, cValuesOf(codegen.kObjHeaderPtrPtr), 1, 0)!!,
valueGetterName) {
val value = getObjectValue(declaration, ExceptionHandler.Caller, null, null)
ret(value)
} 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.
context.llvm.fileUsesThreadLocalObjects = true
}
}
}
@@ -266,17 +266,19 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
return null
}
val isExported = irClass.isExported()
val valueGetterName = if (isExported) {
irClass.objectInstanceGetterSymbolName
} else {
null
}
val storageKind = irClass.storageKind(context)
val threadLocal = storageKind == ObjectStorageKind.THREAD_LOCAL
val symbolName = "kobjref:" + qualifyInternalName(irClass)
val instanceAddress = addKotlinGlobal(symbolName, getLLVMType(irClass.defaultType), threadLocal = threadLocal)
val isExported = irClass.isExported()
val symbolName = if (isExported) {
irClass.globalObjectStorageSymbolName
} else {
"kobjref:" + qualifyInternalName(irClass)
}
val instanceAddress = if (threadLocal) {
addKotlinThreadLocal(symbolName, getLLVMType(irClass.defaultType))
} else {
addKotlinGlobal(symbolName, getLLVMType(irClass.defaultType), isExported)
}
return SingletonLlvmDeclarations(instanceAddress)
}
@@ -319,8 +321,11 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
} else {
// Fields are module-private, so we use internal name:
val name = "kvar:" + qualifyInternalName(declaration)
val storage = addKotlinGlobal(
name, getLLVMType(declaration.type), threadLocal = declaration.storageKind == FieldStorageKind.THREAD_LOCAL)
val storage = if (declaration.storageKind == FieldStorageKind.THREAD_LOCAL) {
addKotlinThreadLocal(name, getLLVMType(declaration.type))
} else {
addKotlinGlobal(name, getLLVMType(declaration.type), isExported = false)
}
this.staticFields[declaration] = StaticFieldLlvmDeclarations(storage)
}
@@ -238,18 +238,13 @@ internal fun getGlobalType(ptrToGlobal: LLVMValueRef): LLVMTypeRef {
return LLVMGetElementType(ptrToGlobal.type)!!
}
internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef, isExported: Boolean,
threadLocal: Boolean = false): LLVMValueRef {
internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef, isExported: Boolean): LLVMValueRef {
if (isExported)
assert(LLVMGetNamedGlobal(context.llvmModule, name) == null)
val result = LLVMAddGlobal(context.llvmModule, type, name)!!
if (threadLocal)
LLVMSetThreadLocalMode(result, context.llvm.tlsMode)
return result
return LLVMAddGlobal(context.llvmModule, type, name)!!
}
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin,
threadLocal: Boolean = false): LLVMValueRef {
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef {
context.llvm.imports.add(origin)
@@ -257,11 +252,9 @@ internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin:
return if (found != null) {
assert (getGlobalType(found) == type)
assert (LLVMGetInitializer(found) == null) { "$name is already declared in the current module" }
if (threadLocal)
assert(LLVMGetThreadLocalMode(found) == context.llvm.tlsMode)
found
} else {
addGlobal(name, type, false, threadLocal)
addGlobal(name, type, isExported = false)
}
}
@@ -282,20 +275,23 @@ internal class TLSAddressAccess(
}
}
internal fun ContextUtils.addKotlinGlobal(name: String, type: LLVMTypeRef, threadLocal: Boolean): AddressAccess {
if (threadLocal) {
return if (isObjectType(type)) {
val index = context.llvm.tlsCount++
TLSAddressAccess(context, index)
} else {
GlobalAddressAccess(LLVMAddGlobal(context.llvmModule, type, name)!!.also {
LLVMSetThreadLocalMode(it, context.llvm.tlsMode)
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
})
}
internal fun ContextUtils.addKotlinThreadLocal(name: String, type: LLVMTypeRef): AddressAccess {
return if (isObjectType(type)) {
val index = context.llvm.tlsCount++
TLSAddressAccess(context, index)
} else {
// TODO: This will break if Workers get decoupled from host threads.
GlobalAddressAccess(LLVMAddGlobal(context.llvmModule, type, name)!!.also {
LLVMSetThreadLocalMode(it, context.llvm.tlsMode)
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
})
}
}
internal fun ContextUtils.addKotlinGlobal(name: String, type: LLVMTypeRef, isExported: Boolean): AddressAccess {
return GlobalAddressAccess(LLVMAddGlobal(context.llvmModule, type, name)!!.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
if (!isExported)
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
})
}