From a97b3935e2711fb17c10f6df3f1e5fb8a577bf5f Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 25 Jan 2017 17:47:17 +0700 Subject: [PATCH] backend: rework descriptor usage * handle differently internal and exported declarations * bind LLVM declarations to descriptor instances used as keys * fix some related issues --- .../jetbrains/kotlin/backend/konan/Context.kt | 7 +- .../konan/descriptors/DescriptorUtils.kt | 21 +- .../kotlin/backend/konan/ir/ModuleIndex.kt | 12 +- .../backend/konan/llvm/BinaryInterface.kt | 154 ++++++++ .../backend/konan/llvm/CodeGenerator.kt | 17 +- .../kotlin/backend/konan/llvm/ContextUtils.kt | 104 ++---- .../kotlin/backend/konan/llvm/DataLayout.kt | 4 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 65 ++-- .../backend/konan/llvm/LlvmDeclarations.kt | 335 ++++++++++++++++++ .../kotlin/backend/konan/llvm/LlvmUtils.kt | 45 ++- .../kotlin/backend/konan/llvm/NameUtils.kt | 101 ------ .../backend/konan/llvm/RTTIGenerator.kt | 70 ++-- .../kotlin/backend/konan/llvm/Runtime.kt | 4 + .../kotlin/backend/konan/llvm/StaticData.kt | 38 +- .../backend/konan/llvm/StaticObjects.kt | 21 +- .../kotlin/konan/internal/RuntimeUtils.kt | 10 +- 16 files changed, 658 insertions(+), 350 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt delete mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index ce3152e07b7..41b4ed80cf5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -3,10 +3,6 @@ package org.jetbrains.kotlin.backend.konan import llvm.* import org.jetbrains.kotlin.backend.konan.ir.Ir import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex -import org.jetbrains.kotlin.backend.konan.llvm.Runtime -import org.jetbrains.kotlin.backend.konan.llvm.getFunctionType -import org.jetbrains.kotlin.backend.konan.llvm.StaticData -import org.jetbrains.kotlin.backend.konan.llvm.Llvm import org.jetbrains.kotlin.backend.konan.KonanBackendContext import org.jetbrains.kotlin.backend.konan.KonanPhase import org.jetbrains.kotlin.backend.konan.KonanConfig @@ -15,7 +11,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint -import org.jetbrains.kotlin.backend.konan.llvm.verifyModule +import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor import java.lang.System.out @@ -50,6 +46,7 @@ internal final class Context(val config: KonanConfig) : KonanBackendContext() { } lateinit var llvm: Llvm + lateinit var llvmDeclarations: LlvmDeclarations var phase: KonanPhase? = null var depth: Int = 0 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 26ad0898868..15d46ded1d8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -35,16 +35,16 @@ internal val ClassDescriptor.implementedInterfaces: List * * TODO: this method is actually a part of resolve and probably duplicates another one */ -internal val FunctionDescriptor.implementation: FunctionDescriptor - get() { - if (this.kind.isReal) { - return this - } else { - val overridden = OverridingUtil.getOverriddenDeclarations(this) - val filtered = OverridingUtil.filterOutOverridden(overridden) - return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor - } +internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor { + if (this.kind.isReal) { + return this + } else { + val overridden = OverridingUtil.getOverriddenDeclarations(this) + val filtered = OverridingUtil.filterOutOverridden(overridden) + // TODO: is it correct to take first? + return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor } +} private val intrinsicAnnotation = FqName("konan.internal.Intrinsic") @@ -193,9 +193,6 @@ val ClassDescriptor.vtableEntries: List return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable } } -val ClassDescriptor.vtableSize: Int - get() = if (this.isAbstract()) 0 else this.vtableEntries.size - fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int { this.vtableEntries.forEachIndexed { index, functionDescriptor -> if (functionDescriptor == function.original) return index diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt index 21fb20af825..2550d71dd9c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/ModuleIndex.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlin.backend.konan.ir +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrClass @@ -8,15 +9,13 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.resolve.descriptorUtil.classId class ModuleIndex(val module: IrModuleFragment) { /** * Contains all classes declared in [module] */ - val classes: Map + val classes: Map /** * Contains all functions declared in [module] @@ -24,7 +23,7 @@ class ModuleIndex(val module: IrModuleFragment) { val functions = mutableMapOf() init { - val map = mutableMapOf() + val map = mutableMapOf() module.acceptVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { @@ -34,10 +33,7 @@ class ModuleIndex(val module: IrModuleFragment) { override fun visitClass(declaration: IrClass) { super.visitClass(declaration) - val classId = declaration.descriptor.classId - if (classId != null) { - map[classId] = declaration - } + map[declaration.descriptor] = declaration } override fun visitFunction(declaration: IrFunction) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt new file mode 100644 index 00000000000..c3f93302941 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -0,0 +1,154 @@ +package org.jetbrains.kotlin.backend.konan.llvm + +import llvm.LLVMTypeRef +import org.jetbrains.kotlin.backend.konan.descriptors.allValueParameters +import org.jetbrains.kotlin.backend.konan.descriptors.isUnit +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty + +// This file describes the ABI for Kotlin descriptors of exported declarations. +// TODO: revise the naming scheme to ensure it produces unique names. +// TODO: do not serialize descriptors of non-exported declarations. + +/** + * Defines whether the declaration is exported, i.e. visible from other modules. + * + * Exported declarations must have predictable and stable ABI + * that doesn't depend on any internal transformations (e.g. IR lowering), + * and so should be computable from the descriptor itself without checking a backend state. + */ +internal tailrec fun DeclarationDescriptor.isExported(): Boolean { + if (this.annotations.findAnnotation(symbolNameAnnotation) != null) { + // Treat any `@SymbolName` declaration as exported. + return true + } + if (this.annotations.findAnnotation(exportForCppRuntimeAnnotation) != null) { + // Treat any `@ExportForCppRuntime` declaration as exported. + return true + } + + if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) { + // Currently code generator can access the constructor of the singleton, + // so ignore visibility of the constructor itself. + return constructedClass.isExported() + } + + if (this is DeclarationDescriptorWithVisibility && !this.visibility.isPublicAPI) { + // If the declaration is explicitly marked as non-public, + // then it must not be accessible from other modules. + return false + } + + if (this is DeclarationDescriptorNonRoot) { + // If the declaration is not root, then check its container too. + return containingDeclaration.isExported() + } + + return true +} + +private val symbolNameAnnotation = FqName("konan.SymbolName") + +private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppRuntime") + +private fun typeToHashString(type: KotlinType): String { + if (TypeUtils.isTypeParameter(type)) return "GENERIC" + + var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString() + if (!type.arguments.isEmpty()) { + hashString += "<${type.arguments.map { + typeToHashString(it.type) + }.joinToString(",")}>" + } + + if (type.isMarkedNullable) hashString += "?" + return hashString +} + +private val FunctionDescriptor.signature: String + get() { + val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: "" + val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this + + val argsPart = actualDescriptor.valueParameters.map { + typeToHashString(it.type) + }.joinToString(";") + + // TODO: add return type + // (it is not simple because return type can be changed when overriding) + return "$extensionReceiverPart($argsPart)" + } + +// TODO: rename to indicate that it has signature included +internal val FunctionDescriptor.functionName: String + get() = with(this.original) { // basic support for generics + "$name$signature" + } + +internal val FunctionDescriptor.symbolName: String + get() { + if (!this.isExported()) { + throw AssertionError(this.toString()) + } + + this.annotations.findAnnotation(symbolNameAnnotation)?.let { + if (this.isExternal) { + return getStringValue(it)!! + } else { + // ignore; TODO: report compile error + } + } + + this.annotations.findAnnotation(exportForCppRuntimeAnnotation)?.let { + val name = getStringValue(it) ?: this.name.asString() + return name // no wrapping currently required + } + + val containingDeclarationPart = containingDeclaration.fqNameSafe.let { + if (it.isRoot) "" else "$it." + } + return "kfun:$containingDeclarationPart$functionName" + } + +private fun getStringValue(annotation: AnnotationDescriptor): String? { + annotation.allValueArguments.values.ifNotEmpty { + val stringValue = this.single() as StringValue + return stringValue.value + } + + return null +} + +// TODO: bring here dependencies of this method? +internal fun RuntimeAware.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef { + val original = function.original + val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!) + val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) }) + if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) + + return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray()) +} + +internal val ClassDescriptor.typeInfoSymbolName: String + get() { + assert (this.isExported()) + return "ktype:" + this.fqNameSafe.toString() + } + +internal val theUnitInstanceName = "kobj:kotlin.Unit" + +internal val ClassDescriptor.objectInstanceFieldSymbolName: String + get() { + assert (this.isExported()) + assert (this.kind.isSingleton) + assert (!this.isUnit()) + + return "kobjref:$fqNameSafe" + } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index a4e0402556e..3092f1bc544 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -23,8 +23,16 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { var localAllocs = 0 fun prologue(descriptor: FunctionDescriptor) { - prologue(llvmFunction(descriptor), + val llvmFunction = llvmFunction(descriptor) + + prologue(llvmFunction, LLVMGetReturnType(getLlvmFunctionType(descriptor))!!) + + if (!descriptor.isExported()) { + LLVMSetLinkage(llvmFunction, LLVMLinkage.LLVMPrivateLinkage) + // (Cannot do this before the function body is created). + } + if (descriptor is ConstructorDescriptor) { constructedClass = descriptor.constructedClass } @@ -300,7 +308,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { //-------------------------------------------------------------------------// /* to class descriptor */ - fun classType(descriptor: ClassDescriptor): LLVMTypeRef = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!! fun typeInfoValue(descriptor: ClassDescriptor): LLVMValueRef = descriptor.llvmTypeInfoPtr /** @@ -314,12 +321,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { } fun countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction) - fun indexInClass(p:PropertyDescriptor):Int { - val index = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p) - assert(index >= 0) { "Unable to find property $p" } - return index - } - fun basicBlock(name: String = "label_"): LLVMBasicBlockRef { val currentBlock = this.currentBlock val result = LLVMInsertBasicBlock(currentBlock, name)!! diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 43e6191393e..d2ac04ff9a9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -3,24 +3,15 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.Distribution import org.jetbrains.kotlin.backend.konan.hash.GlobalHash -import org.jetbrains.kotlin.backend.konan.KonanConfigKeys -import org.jetbrains.kotlin.backend.konan.descriptors.backingField -import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.descriptorUtil.classId -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -33,10 +24,10 @@ const val SCOPE_PERMANENT = 3 /** * Provides utility methods to the implementer. */ -internal interface ContextUtils { +internal interface ContextUtils : RuntimeAware { val context: Context - val runtime: Runtime + override val runtime: Runtime get() = context.llvm.runtime /** @@ -50,57 +41,7 @@ internal interface ContextUtils { val staticData: StaticData get() = context.llvm.staticData - /** - * All fields of the class instance. - * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. - */ - val ClassDescriptor.fields: List - get() { - val superClass = this.getSuperClassNotAny() // TODO: what if Any has fields? - val superFields = if (superClass != null) superClass.fields else emptyList() - - return superFields + this.declaredFields - } - - /** - * Fields declared in the class. - */ - val ClassDescriptor.declaredFields: List - get() { - // TODO: Here's what is going on here: - // The existence of a backing field for a property is only described in the IR, - // but not in the property descriptor. - // That works, while we process the IR, but not for deserialized descriptors. - // - // So to have something in deserialized descriptors, - // while we still see the IR, we mark the property with an annotation. - // - // We could apply the annotation during IR rewite, but we still are not - // that far in the rewriting infrastructure. So we postpone - // the annotation until the serializer. - // - // In this function we check the presence of the backing filed - // two ways: first we check IR, then we check the annotation. - - val irClass = context.ir.moduleIndexForCodegen.classes[this.classId] - if (irClass != null) { - val declarations = irClass.declarations - - return declarations.mapNotNull { - when (it) { - is IrProperty -> it.backingField?.descriptor - is IrField -> it.descriptor - else -> null - } - } - } else { - val properties = this.unsubstitutedMemberScope. - getContributedDescriptors(). - filterIsInstance() - - return properties.mapNotNull{ it.backingField } - } - } + fun isExternal(descriptor: DeclarationDescriptor) = descriptor.module != context.ir.irModule.descriptor /** * LLVM function generated from the Kotlin function. @@ -112,13 +53,12 @@ internal interface ContextUtils { if (this is TypeAliasConstructorDescriptor) { return this.underlyingConstructorDescriptor.llvmFunction } - val globalName = this.symbolName - val module = context.llvmModule - val functionType = getLlvmFunctionType(this) - - return LLVMGetNamedFunction(module, globalName) ?: - LLVMAddFunction(module, globalName, functionType)!! + return if (isExternal(this)) { + context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this)) + } else { + context.llvmDeclarations.forFunction(this).llvmFunction + } } /** @@ -130,17 +70,14 @@ internal interface ContextUtils { return constValue(result) } - /** - * Pointer to struct { TypeInfo, vtable }. - */ - val ClassDescriptor.typeInfoWithVtable: ConstPointer - get() { - val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!) - return constPointer(externalGlobal(this.typeInfoSymbolName, type)) - } - val ClassDescriptor.typeInfoPtr: ConstPointer - get() = typeInfoWithVtable.getElementPtr(0) + get() { + return if (isExternal(this)) { + constPointer(externalGlobal(this.typeInfoSymbolName, runtime.typeInfoType)) + } else { + context.llvmDeclarations.forClass(this).typeInfo + } + } /** * Pointer to type info for given class. @@ -238,13 +175,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { } } - private fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef { - val found = LLVMGetNamedFunction(context.llvmModule, name) + internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef { + val found = LLVMGetNamedFunction(llvmModule, name) if (found != null) { assert (getFunctionType(found) == type) + assert (LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage) return found } else { - return LLVMAddFunction(context.llvmModule, name, type)!! + return LLVMAddFunction(llvmModule, name, type)!! } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt index 5812584b505..4f03338f621 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt @@ -6,7 +6,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isUnit -internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef { +internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef { return when { // Nullable types must be represented as objects for boxing. type.isMarkedNullable -> this.kObjHeaderPtr @@ -23,7 +23,7 @@ internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef { }!! } -internal fun ContextUtils.getLLVMReturnType(type: KotlinType): LLVMTypeRef { +internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef { return when { type.isUnit() -> LLVMVoidType()!! // TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains(). diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 783d8af129b..7bd120c96f3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.KotlinType @@ -41,6 +40,8 @@ internal fun emitLLVM(context: Context) { val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose context.llvmModule = llvmModule + context.llvmDeclarations = createLlvmDeclarations(context) + val phaser = PhaseManager(context) phaser.phase(KonanPhase.RTTI) { @@ -83,11 +84,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid { override fun visitClass(declaration: IrClass) { super.visitClass(declaration) - if (declaration.descriptor.kind == ClassKind.ANNOTATION_CLASS) { - // do not generate any RTTI for annotation classes as a workaround for link errors - return - } - if (declaration.descriptor.isIntrinsic) { // do not generate any code for intrinsic classes as they require special handling return @@ -254,7 +250,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val kNodeInitType = LLVMGetTypeByName(context.llvmModule, "struct.InitNode")!! //-------------------------------------------------------------------------// - fun createInitBody(initName: String) { + fun createInitBody(initName: String): LLVMValueRef { val initFunction = LLVMAddFunction(context.llvmModule, initName, kVoidFuncType)!! // create LLVM function codegen.prologue(initFunction, voidType) using(FunctionScope(initFunction)) { @@ -262,33 +258,33 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val irField = it as IrField val descriptor = irField.descriptor val initialization = evaluateExpression(irField.initializer!!.expression) - val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName) - codegen.storeAnyGlobal(initialization, globalPtr!!) + val globalPtr = context.llvmDeclarations.forStaticField(descriptor).storage + codegen.storeAnyGlobal(initialization, globalPtr) } codegen.ret(null) } codegen.epilogue() + + return initFunction } //-------------------------------------------------------------------------// // Creates static struct InitNode $nodeName = {$initName, NULL}; - fun createInitNode(initName: String, nodeName: String) { + fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef { memScoped { - val initFunction = LLVMGetNamedFunction(context.llvmModule, initName)!! // Get initialization function. val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL. val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args. val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode. - context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName". + return context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName". } } //-------------------------------------------------------------------------// - fun createInitCtor(ctorName: String, nodeName: String) { + fun createInitCtor(ctorName: String, initNodePtr: LLVMValueRef) { val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function. codegen.prologue(ctorFunction, voidType) - val initNodePtr = LLVMGetNamedGlobal(context.llvmModule, nodeName)!! // Get LLVM function initializing globals of current file. codegen.call(context.llvm.appendToInitalizersTail, initNodePtr.singletonList()) // Add node to the tail of initializers list. codegen.ret(null) codegen.epilogue() @@ -313,9 +309,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}" val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}" - createInitBody(initName) - createInitNode(initName, nodeName) - createInitCtor(ctorName, nodeName) + val initFunction = createInitBody(initName) + val initNode = createInitNode(initFunction, nodeName) + createInitCtor(ctorName, initNode) } //-------------------------------------------------------------------------// @@ -388,15 +384,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid if (constructorDescriptor.isPrimary) { if (DescriptorUtils.isObject(classDescriptor)) { - if (classDescriptor.isUnit()) { - context.llvm.staticData.createUnitInstance(classDescriptor) - } else { + if (!classDescriptor.isUnit()) { val objectPtr = objectPtrByName(classDescriptor) LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr) } } - val irOfCurrentClass = context.ir.moduleIndexForCodegen.classes[classDescriptor.classId] + val irOfCurrentClass = context.ir.moduleIndexForCodegen.classes[classDescriptor] irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) @@ -612,13 +606,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val descriptor = expression.descriptor if (descriptor.containingDeclaration is PackageFragmentDescriptor) { val type = codegen.getLLVMType(descriptor.type) - val globalProperty = LLVMAddGlobal(context.llvmModule, type, descriptor.symbolName) + val globalProperty = context.llvmDeclarations.forStaticField(descriptor).storage if (expression.initializer!!.expression is IrConst<*>) { LLVMSetInitializer(globalProperty, evaluateExpression(expression.initializer!!.expression)) } else { LLVMSetInitializer(globalProperty, LLVMConstNull(type)) context.llvm.fileInitializers.add(expression) } + + LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMPrivateLinkage) + // (Cannot do this before the global is initialized). + return } } @@ -1363,7 +1361,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } else { assert (value.receiver == null) - val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!! + val ptr = context.llvmDeclarations.forStaticField(value.descriptor).storage return codegen.loadSlot(ptr, value.descriptor.isVar()) } } @@ -1372,13 +1370,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun objectPtrByName(descriptor: ClassDescriptor): LLVMValueRef { assert (!descriptor.isUnit()) - val objName = descriptor.fqNameSafe.asString() - var objectPtr = LLVMGetNamedGlobal(context.llvmModule, objName) - if (objectPtr == null) { + return if (codegen.isExternal(descriptor)) { val llvmType = codegen.getLLVMType(descriptor.defaultType) - objectPtr = LLVMAddGlobal(context.llvmModule, llvmType, objName) + codegen.externalGlobal(descriptor.objectInstanceFieldSymbolName, llvmType) + } else { + context.llvmDeclarations.forSingleton(descriptor).instanceFieldRef } - return objectPtr!! } //-------------------------------------------------------------------------// @@ -1392,8 +1389,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } else { assert (value.receiver == null) - val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName) - codegen.storeAnyGlobal(valueToAssign, globalValue!!) + val globalValue = context.llvmDeclarations.forStaticField(value.descriptor).storage + codegen.storeAnyGlobal(valueToAssign, globalValue) } assert (value.type.isUnit()) @@ -1435,12 +1432,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid */ private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef { - val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor)) + val fieldInfo = context.llvmDeclarations.forField(value) + + val typePtr = pointerType(fieldInfo.classBodyType) memScoped { val args = allocArrayOf(kImmOne) val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "") val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!) - val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, codegen.indexInClass(value), "") + val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "") return fieldPtr!! } } @@ -1853,7 +1852,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// fun callDirect(descriptor: FunctionDescriptor, args: List): LLVMValueRef { - val realDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor) + val realDescriptor = descriptor.resolveFakeOverride().original val llvmFunction = codegen.functionLlvmValue(realDescriptor) return call(descriptor, llvmFunction, args) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt new file mode 100644 index 00000000000..7681cbef81e --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -0,0 +1,335 @@ +package org.jetbrains.kotlin.backend.konan.llvm + +import kotlinx.cinterop.* +import llvm.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny + +internal fun createLlvmDeclarations(context: Context): LlvmDeclarations { + val generator = DeclarationsGeneratorVisitor(context) + context.ir.irModule.acceptChildrenVoid(generator) + return with(generator) { + LlvmDeclarations( + functions, classes, fields, staticFields, theUnitInstanceRef + ) + } + +} + +internal class LlvmDeclarations( + private val functions: Map, + private val classes: Map, + private val fields: Map, + private val staticFields: Map, + private val theUnitInstanceRef: ConstPointer? +) { + fun forFunction(descriptor: FunctionDescriptor) = functions[descriptor] ?: + error(descriptor.toString()) + + fun forClass(descriptor: ClassDescriptor) = classes[descriptor] ?: + error(descriptor.toString()) + + fun forField(descriptor: PropertyDescriptor) = fields[descriptor] ?: + error(descriptor.toString()) + + fun forStaticField(descriptor: PropertyDescriptor) = staticFields[descriptor] ?: + error(descriptor.toString()) + + fun forSingleton(descriptor: ClassDescriptor) = forClass(descriptor).singletonDeclarations ?: + error(descriptor.toString()) + + fun getUnitInstanceRef() = theUnitInstanceRef ?: error("") + +} + +internal class ClassLlvmDeclarations( + val bodyType: LLVMTypeRef, + val fields: List, // TODO: it is not an LLVM declaration. + val typeInfoGlobal: StaticData.Global, + val typeInfo: ConstPointer, + val singletonDeclarations: SingletonLlvmDeclarations?) + +internal class SingletonLlvmDeclarations(val instanceFieldRef: LLVMValueRef) + +internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef) + +internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef) + +internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef) + +// TODO: rework getFields and getDeclaredFields. + +/** + * All fields of the class instance. + * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. + */ +private fun getFields(context: Context, classDescriptor: ClassDescriptor): List { + val superClass = classDescriptor.getSuperClassNotAny() // TODO: what if Any has fields? + val superFields = if (superClass != null) getFields(context, superClass) else emptyList() + + return superFields + getDeclaredFields(context, classDescriptor) +} + +/** + * Fields declared in the class. + */ +private fun getDeclaredFields(context: Context, classDescriptor: ClassDescriptor): List { + // TODO: Here's what is going on here: + // The existence of a backing field for a property is only described in the IR, + // but not in the property descriptor. + // That works, while we process the IR, but not for deserialized descriptors. + // + // So to have something in deserialized descriptors, + // while we still see the IR, we mark the property with an annotation. + // + // We could apply the annotation during IR rewite, but we still are not + // that far in the rewriting infrastructure. So we postpone + // the annotation until the serializer. + // + // In this function we check the presence of the backing filed + // two ways: first we check IR, then we check the annotation. + + val irClass = context.ir.moduleIndexForCodegen.classes[classDescriptor] + if (irClass != null) { + val declarations = irClass.declarations + + return declarations.mapNotNull { + when (it) { + is IrProperty -> it.backingField?.descriptor + is IrField -> it.descriptor + else -> null + } + } + } else { + val properties = classDescriptor.unsubstitutedMemberScope. + getContributedDescriptors(). + filterIsInstance() + + return properties.mapNotNull { it.backingField } + } +} + +private fun ContextUtils.createClassBodyType(name: String, fields: List): LLVMTypeRef { + val fieldTypes = fields.map { getLLVMType(it.type) }.toTypedArray() + + val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!! + + memScoped { + val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr + LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0) + } + return classType +} + +private class DeclarationsGeneratorVisitor(override val context: Context) : + IrElementVisitorVoid, ContextUtils { + + val functions = mutableMapOf() + val classes = mutableMapOf() + val fields = mutableMapOf() + val staticFields = mutableMapOf() + var theUnitInstanceRef: ConstPointer? = null + + private class Namer(val prefix: String) { + private val names = mutableMapOf() + private val counts = mutableMapOf() + + fun getName(parent: FqName, descriptor: DeclarationDescriptor): Name { + return names.getOrPut(descriptor) { + val count = counts.getOrDefault(parent, 0) + 1 + counts[parent] = count + Name.identifier(prefix + count) + } + } + } + + val objectNamer = Namer("object-") + + private fun getLocalName(parent: FqName, descriptor: DeclarationDescriptor): Name { + if (DescriptorUtils.isAnonymousObject(descriptor)) { + return objectNamer.getName(parent, descriptor) + } + + return descriptor.name + } + + private fun getFqName(descriptor: DeclarationDescriptor): FqName { + if (descriptor is PackageFragmentDescriptor) { + return descriptor.fqName + } + + + val containingDeclaration = descriptor.containingDeclaration + val parent = if (containingDeclaration != null) { + getFqName(containingDeclaration) + } else { + FqName.ROOT + } + + val localName = getLocalName(parent, descriptor) + return parent.child(localName) + } + + /** + * Produces the name to be used for non-exported LLVM declarations corresponding to [descriptor]. + * + * Note: since these declarations are going to be private, the name is only required not to clash with any + * exported declarations. + */ + private fun qualifyInternalName(descriptor: DeclarationDescriptor): String { + return getFqName(descriptor).asString() + "#internal" + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + + if (declaration.descriptor.isIntrinsic) { + // do not generate any declarations for intrinsic classes as they require special handling + } else { + this.classes[declaration.descriptor] = createClassDeclarations(declaration) + } + + super.visitClass(declaration) + } + + private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations { + val descriptor = declaration.descriptor + + val internalName = qualifyInternalName(descriptor) + + val fields = getFields(context, descriptor) + val bodyType = createClassBodyType("kclassbody:$internalName", fields) + + val typeInfoPtr: ConstPointer + val typeInfoGlobal: StaticData.Global + + val typeInfoSymbolName = if (descriptor.isExported()) { + descriptor.typeInfoSymbolName + } else { + "ktype:$internalName" + } + + if (!descriptor.isAbstract()) { + // Create the special global consisting of TypeInfo and vtable. + + val typeInfoGlobalName = "ktypeglobal:$internalName" + + val typeInfoWithVtableType = structType( + runtime.typeInfoType, + LLVMArrayType(int8TypePtr, descriptor.vtableEntries.size)!! + ) + + typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false) + + val llvmTypeInfoPtr = LLVMAddAlias(context.llvmModule, + kTypeInfoPtr, + typeInfoGlobal.pointer.getElementPtr(0).llvm, + typeInfoSymbolName)!! + + if (!descriptor.isExported()) { + LLVMSetLinkage(llvmTypeInfoPtr, LLVMLinkage.LLVMPrivateLinkage) + } + + typeInfoPtr = constPointer(llvmTypeInfoPtr) + + } else { + typeInfoGlobal = staticData.createGlobal(runtime.typeInfoType, + typeInfoSymbolName, + isExported = descriptor.isExported()) + + typeInfoPtr = typeInfoGlobal.pointer + } + + val singletonDeclarations = if (descriptor.kind.isSingleton) { + createSingletonDeclarations(descriptor, typeInfoPtr, bodyType) + } else { + null + } + + return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr, singletonDeclarations) + } + + private fun createSingletonDeclarations( + descriptor: ClassDescriptor, + typeInfoPtr: ConstPointer, + bodyType: LLVMTypeRef + ): SingletonLlvmDeclarations? { + + if (descriptor.isUnit()) { + this.theUnitInstanceRef = staticData.createUnitInstance(descriptor, bodyType, typeInfoPtr) + return null + } + + val symbolName = if (descriptor.isExported()) { + descriptor.objectInstanceFieldSymbolName + } else { + "kobjref:" + qualifyInternalName(descriptor) + } + val instanceFieldRef = LLVMAddGlobal( + context.llvmModule, + getLLVMType(descriptor.defaultType), + symbolName + )!! + + return SingletonLlvmDeclarations(instanceFieldRef) + } + + override fun visitField(declaration: IrField) { + super.visitField(declaration) + + val descriptor = declaration.descriptor + + val dispatchReceiverParameter = descriptor.dispatchReceiverParameter + if (dispatchReceiverParameter != null) { + val containingClass = dispatchReceiverParameter.containingDeclaration + val classDeclarations = this.classes[containingClass] ?: error(containingClass.toString()) + + val allFields = classDeclarations.fields + + this.fields[descriptor] = FieldLlvmDeclarations( + allFields.indexOf(descriptor), + classDeclarations.bodyType + ) + } else { + + // Fields are module-private, so we use internal name: + val name = "kvar:" + qualifyInternalName(descriptor) + + val storage = LLVMAddGlobal(context.llvmModule, getLLVMType(descriptor.type), name)!! + + this.staticFields[descriptor] = StaticFieldLlvmDeclarations(storage) + } + } + + override fun visitFunction(declaration: IrFunction) { + super.visitFunction(declaration) + + val descriptor = declaration.descriptor + val llvmFunctionType = getLlvmFunctionType(descriptor) + + val llvmFunction = if (descriptor.isExternal) { + context.llvm.externalFunction(descriptor.symbolName, llvmFunctionType) + } else { + val symbolName = if (descriptor.isExported()) { + descriptor.symbolName + } else { + "kfun:" + qualifyInternalName(descriptor) + } + LLVMAddFunction(context.llvmModule, symbolName, llvmFunctionType)!! + } + + this.functions[descriptor] = FunctionLlvmDeclarations(llvmFunction) + } +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index ae5155e8330..ad3270372e7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -111,21 +111,21 @@ internal val ContextUtils.kTheAnyTypeInfo: LLVMValueRef get() = KonanPlatform.builtIns.any.llvmTypeInfoPtr internal val ContextUtils.kTheArrayTypeInfo: LLVMValueRef get() = KonanPlatform.builtIns.array.llvmTypeInfoPtr -internal val ContextUtils.kTypeInfo: LLVMTypeRef - get() = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!! -internal val ContextUtils.kObjHeader: LLVMTypeRef - get() = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!! -internal val ContextUtils.kContainerHeader: LLVMTypeRef - get() = LLVMGetTypeByName(context.llvmModule, "struct.ContainerHeader")!! -internal val ContextUtils.kObjHeaderPtr: LLVMTypeRef +internal val RuntimeAware.kTypeInfo: LLVMTypeRef + get() = runtime.typeInfoType +internal val RuntimeAware.kObjHeader: LLVMTypeRef + get() = runtime.objHeaderType +internal val RuntimeAware.kContainerHeader: LLVMTypeRef + get() = runtime.containerHeaderType +internal val RuntimeAware.kObjHeaderPtr: LLVMTypeRef get() = pointerType(kObjHeader) -internal val ContextUtils.kObjHeaderPtrPtr: LLVMTypeRef +internal val RuntimeAware.kObjHeaderPtrPtr: LLVMTypeRef get() = pointerType(kObjHeaderPtr) -internal val ContextUtils.kArrayHeader: LLVMTypeRef - get() = LLVMGetTypeByName(context.llvmModule, "struct.ArrayHeader")!! -internal val ContextUtils.kArrayHeaderPtr: LLVMTypeRef +internal val RuntimeAware.kArrayHeader: LLVMTypeRef + get() = runtime.arrayHeaderType +internal val RuntimeAware.kArrayHeaderPtr: LLVMTypeRef get() = pointerType(kArrayHeader) -internal val ContextUtils.kTypeInfoPtr: LLVMTypeRef +internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef get() = pointerType(kTypeInfo) internal val kInt1 = LLVMInt1Type()!! internal val kBoolean = kInt1 @@ -151,18 +151,6 @@ internal fun structType(types: List): LLVMTypeRef = memScoped { LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!! } -internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef { - val original = function.original - val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!) - val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) }) - if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) - - memScoped { - val paramTypesPtr = allocArrayOf(paramTypes)[0].ptr - return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0)!! - } -} - internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int { // Note that type is usually function pointer, so we have to dereference it. return LLVMCountParamTypes(LLVMGetElementType(functionType))!! @@ -178,7 +166,7 @@ internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean { return isObjectType(value.type) } -internal fun ContextUtils.isObjectType(type: LLVMTypeRef): Boolean { +internal fun RuntimeAware.isObjectType(type: LLVMTypeRef): Boolean { return type == kObjHeaderPtr || type == kArrayHeaderPtr } @@ -200,6 +188,7 @@ internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMV val found = LLVMGetNamedGlobal(context.llvmModule, name) if (found != null) { assert (getGlobalType(found) == type) + assert (LLVMGetInitializer(found) == null) return found } else { return LLVMAddGlobal(context.llvmModule, type, name)!! @@ -227,3 +216,9 @@ internal operator fun LLVMAttributeSet.contains(attribute: LLVMAttribute): Boole return (this and attribute.value) != 0 } +fun getStructElements(type: LLVMTypeRef): List { + val count = LLVMCountStructElementTypes(type) + return (0 until count).map { + LLVMStructGetTypeAtIndex(type, it)!! + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt deleted file mode 100644 index 8b155bc9733..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt +++ /dev/null @@ -1,101 +0,0 @@ -package org.jetbrains.kotlin.backend.konan.llvm - -import org.jetbrains.kotlin.backend.konan.descriptors.isInterface -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.ClassKind.* -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.OverridingUtil -import org.jetbrains.kotlin.resolve.constants.StringValue -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny -import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty - - -private val symbolNameAnnotation = FqName("konan.SymbolName") - -private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppRuntime") - -fun typeToHashString(type: KotlinType): String { - if (TypeUtils.isTypeParameter(type)) return "GENERIC" - - var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString() - if (!type.arguments.isEmpty()) { - hashString += "<${type.arguments.map { - typeToHashString(it.type) - }.joinToString(",")}>" - } - - if (type.isMarkedNullable) hashString += "?" - return hashString -} - -private val FunctionDescriptor.signature: String - get() { - val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: "" - val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this - - val argsPart = actualDescriptor.valueParameters.map { - typeToHashString(it.type) - }.joinToString(";") - - // TODO: add return type - // (it is not simple because return type can be changed when overriding) - return "$extensionReceiverPart($argsPart)" - } - -// TODO: rename to indicate that it has signature included -internal val FunctionDescriptor.functionName: String - get() = with(this.original) { // basic support for generics - "$name$signature" - } - -internal val FunctionDescriptor.symbolName: String - get() { - this.annotations.findAnnotation(symbolNameAnnotation)?.let { - if (this.isExternal) { - return getStringValue(it)!! - } else { - // ignore; TODO: report compile error - } - } - - this.annotations.findAnnotation(exportForCppRuntimeAnnotation)?.let { - val name = getStringValue(it) ?: this.name.asString() - return name // no wrapping currently required - } - - val containingDeclarationPart = containingDeclaration.fqNameSafe.let { - if (it.isRoot) "" else "$it." - } - return "kfun:$containingDeclarationPart$functionName" - } - -private fun getStringValue(annotation: AnnotationDescriptor): String? { - annotation.allValueArguments.values.ifNotEmpty { - val stringValue = this.single() as StringValue - return stringValue.value - } - - return null -} - -internal val ClassDescriptor.symbolName: String - get() = when (this.kind) { - CLASS -> "kclass:" - INTERFACE -> "kinf:" - OBJECT -> "kclass:" - ENUM_CLASS -> "kclass:" - ENUM_ENTRY -> "kclass:" - else -> TODO("fixme: " + this.kind) - } + fqNameSafe - -internal val ClassDescriptor.typeInfoSymbolName: String - get() = "ktype:" + this.fqNameSafe.toString() - -internal val PropertyDescriptor.symbolName:String -get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}" \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index f50e23e5858..53937485183 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -1,7 +1,5 @@ package org.jetbrains.kotlin.backend.konan.llvm - -import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.* @@ -52,18 +50,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { Int32(fieldsCount) ) - // TODO: probably it should be moved out of this class and shared. - private fun createStructFor(className: FqName, fields: List): LLVMTypeRef? { - val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className) - val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray() - - memScoped { - val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr - LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0) - } - return classType - } - private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) { val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo")) if (annot != null) { @@ -97,11 +83,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val className = classDesc.fqNameSafe - val classType = createStructFor(className, classDesc.fields) + val llvmDeclarations = context.llvmDeclarations.forClass(classDesc) + + val bodyType = llvmDeclarations.bodyType val name = className.globalHash - val size = getInstanceSize(classType, className) + val size = getInstanceSize(bodyType, className) val superTypeOrAny = classDesc.getSuperClassOrAny() val superType = if (KotlinBuiltIns.isAny(classDesc)) NullPointer(runtime.typeInfoType) @@ -111,51 +99,39 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className", pointerType(runtime.typeInfoType), interfaces) - val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field -> - val type = field.returnType!! - if (isObjectType(getLLVMType(type))) { - index + // TODO: reuse offsets obtained for 'fields' below + val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type -> + if (isObjectType(type)) { + LLVMOffsetOfElement(llvmTargetData, bodyType, index) } else { null } } - // TODO: reuse offsets obtained for 'fields' below - val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(llvmTargetData, classType, it) } + val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type, objOffsets.map { Int32(it.toInt()) }) - val fields = classDesc.fields.mapIndexed { index, field -> + val fields = llvmDeclarations.fields.mapIndexed { index, field -> // Note: using FQ name because a class may have multiple fields with the same name due to property overriding val nameSignature = field.fqNameSafe.localHash // FIXME: add signature - val fieldOffset = LLVMOffsetOfElement(llvmTargetData, classType, index) + val fieldOffset = LLVMOffsetOfElement(llvmTargetData, bodyType, index) FieldTableRecord(nameSignature, fieldOffset.toInt()) }.sortedBy { it.nameSignature.value } val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields) - val vtableEntries: List - val methods: List - - if (!classDesc.isAbstract()) { - // TODO: compile-time resolution limits binary compatibility - vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress } - - methods = classDesc.methodTableEntries.map { + val methods = if (!classDesc.isAbstract()) { + classDesc.methodTableEntries.map { val nameSignature = it.functionName.localHash // TODO: compile-time resolution limits binary compatibility - val methodEntryPoint = it.implementation.entryPointAddress + val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress MethodTableRecord(nameSignature, methodEntryPoint) }.sortedBy { it.nameSignature.value } } else { - vtableEntries = emptyList() - methods = emptyList() + emptyList() } - assert (vtableEntries.size == classDesc.vtableSize) - - val vtable = ConstArray(int8TypePtr, vtableEntries) - val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods) @@ -166,9 +142,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { methodsPtr, methods.size, fieldsPtr, if (classDesc.isInterface) -1 else fields.size) - val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack - LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm) - LLVMSetGlobalConstant(typeInfoGlobal, 1) + val typeInfoGlobal = llvmDeclarations.typeInfoGlobal + + val typeInfoGlobalValue = if (classDesc.isAbstract()) { + typeInfo + } else { + // TODO: compile-time resolution limits binary compatibility + val vtableEntries = classDesc.vtableEntries.map { it.resolveFakeOverride().original.entryPointAddress } + val vtable = ConstArray(int8TypePtr, vtableEntries) + Struct(typeInfo, vtable) + } + + typeInfoGlobal.setInitializer(typeInfoGlobalValue) + typeInfoGlobal.setConstant(true) exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt index 76c97f54cc2..43a7e145052 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt @@ -3,6 +3,10 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* +interface RuntimeAware { + val runtime: Runtime +} + class Runtime(private val bitcodeFile: String) { val llvmModule: LLVMModuleRef diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt index 94f3706a4a2..61ff132c58d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt @@ -15,34 +15,34 @@ internal class StaticData(override val context: Context): ContextUtils { class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) { companion object { - private fun createLlvmGlobal(module: LLVMModuleRef, type: LLVMTypeRef, name: String): LLVMValueRef { - val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`. + private fun createLlvmGlobal(module: LLVMModuleRef, + type: LLVMTypeRef, + name: String, + isExported: Boolean + ): LLVMValueRef { - if (!isUnnamed) { - LLVMGetNamedGlobal(module, name)?.let { existingGlobal -> - if (getGlobalType(existingGlobal) != type || LLVMGetInitializer(existingGlobal) != null) { - throw IllegalArgumentException("Global '$name' already exists") - } else { - return existingGlobal - } - } + if (isExported && LLVMGetNamedGlobal(module, name) != null) { + throw IllegalArgumentException("Global '$name' already exists") } val llvmGlobal = LLVMAddGlobal(module, type, name)!! - if (isUnnamed) { - // Currently using unnamed globals may result in link errors due to symbol redefinition; - // apply the workaround: + if (!isExported) { LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMPrivateLinkage) } return llvmGlobal } - fun create(staticData: StaticData, type: LLVMTypeRef, name: String): Global { + fun create(staticData: StaticData, type: LLVMTypeRef, name: String, isExported: Boolean): Global { val module = staticData.context.llvmModule - val llvmGlobal = createLlvmGlobal(module!!, type, name) + val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`. + if (isUnnamed && isExported) { + throw IllegalArgumentException("unnamed global can't be exported") + } + + val llvmGlobal = createLlvmGlobal(module!!, type, name, isExported) return Global(staticData, llvmGlobal) } } @@ -110,15 +110,15 @@ internal class StaticData(override val context: Context): ContextUtils { * * It is external until explicitly initialized with [Global.setInitializer]. */ - fun createGlobal(type: LLVMTypeRef, name: String): Global { - return Global.create(this, type, name) + fun createGlobal(type: LLVMTypeRef, name: String, isExported: Boolean = false): Global { + return Global.create(this, type, name, isExported) } /** * Creates [Global] with given name and value. */ - fun placeGlobal(name: String, initializer: ConstValue): Global { - val global = createGlobal(initializer.llvmType, name) + fun placeGlobal(name: String, initializer: ConstValue, isExported: Boolean = false): Global { + val global = createGlobal(initializer.llvmType, name, isExported) global.setInitializer(initializer) return global } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt index 97d9cd547ab..9e51168714b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt @@ -117,16 +117,23 @@ internal fun StaticData.createArrayList(elementType: TypeProjection, array: Cons return createKotlinObject(type, body) } -private val theUnitInstanceName = "kobj:kotlin.Unit" - -internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor): ConstPointer { +internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor, + bodyType: LLVMTypeRef, + typeInfo: ConstPointer +): ConstPointer { assert (descriptor.isUnit()) - assert (descriptor.fields.isEmpty()) - val typeInfo = descriptor.defaultType.typeInfoPtr!! + assert (getStructElements(bodyType).isEmpty()) val objHeader = objHeader(typeInfo) - val global = this.placeGlobal(theUnitInstanceName, objHeader) + val global = this.placeGlobal(theUnitInstanceName, objHeader, isExported = true) return global.pointer } internal val ContextUtils.theUnitInstanceRef: ConstPointer - get() = constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType)) \ No newline at end of file + get() { + val unitDescriptor = context.builtIns.unit + return if (isExternal(unitDescriptor)) { + constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType)) + } else { + context.llvmDeclarations.getUnitInstanceRef() + } + } \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index dc2fa175581..743da531602 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -1,7 +1,7 @@ package konan.internal @ExportForCppRuntime -internal fun ThrowNullPointerException(): Nothing { +fun ThrowNullPointerException(): Nothing { throw NullPointerException() } @@ -11,7 +11,7 @@ internal fun ThrowArrayIndexOutOfBoundsException(): Nothing { } @ExportForCppRuntime -internal fun ThrowClassCastException(): Nothing { +fun ThrowClassCastException(): Nothing { throw ClassCastException() } @@ -20,14 +20,14 @@ internal fun ThrowArithmeticException() : Nothing { throw ArithmeticException() } -internal fun ThrowNoWhenBranchMatchedException(): Nothing { +fun ThrowNoWhenBranchMatchedException(): Nothing { throw NoWhenBranchMatchedException() } @ExportForCppRuntime internal fun TheEmptyString() = "" -internal fun > valueOfForEnum(name: String, arr: Array) : T +fun > valueOfForEnum(name: String, arr: Array) : T { for (x in arr) if (x.name == name) @@ -35,7 +35,7 @@ internal fun > valueOfForEnum(name: String, arr: Array) : T throw Exception("Invalid enum name: $name") } -internal fun > valuesForEnum(values: Array): Array +fun > valuesForEnum(values: Array): Array { return values.clone() as Array } \ No newline at end of file