backend: rework descriptor usage

* handle differently internal and exported declarations
* bind LLVM declarations to descriptor instances used as keys
* fix some related issues
This commit is contained in:
Svyatoslav Scherbina
2017-01-25 17:47:17 +07:00
committed by SvyatoslavScherbina
parent cf946a7676
commit a97b3935e2
16 changed files with 658 additions and 350 deletions
@@ -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
@@ -35,16 +35,16 @@ internal val ClassDescriptor.implementedInterfaces: List<ClassDescriptor>
*
* 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<FunctionDescriptor>
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
@@ -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<ClassId, IrClass>
val classes: Map<ClassDescriptor, IrClass>
/**
* Contains all functions declared in [module]
@@ -24,7 +23,7 @@ class ModuleIndex(val module: IrModuleFragment) {
val functions = mutableMapOf<FunctionDescriptor, IrFunction>()
init {
val map = mutableMapOf<ClassId, IrClass>()
val map = mutableMapOf<ClassDescriptor, IrClass>()
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) {
@@ -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"
}
@@ -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)!!
@@ -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<PropertyDescriptor>
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<PropertyDescriptor>
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<PropertyDescriptor>()
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)!!
}
}
@@ -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().
@@ -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>): LLVMValueRef {
val realDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor)
val realDescriptor = descriptor.resolveFakeOverride().original
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
return call(descriptor, llvmFunction, args)
}
@@ -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<FunctionDescriptor, FunctionLlvmDeclarations>,
private val classes: Map<ClassDescriptor, ClassLlvmDeclarations>,
private val fields: Map<PropertyDescriptor, FieldLlvmDeclarations>,
private val staticFields: Map<PropertyDescriptor, StaticFieldLlvmDeclarations>,
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<PropertyDescriptor>, // 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<PropertyDescriptor> {
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<PropertyDescriptor> {
// 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<PropertyDescriptor>()
return properties.mapNotNull { it.backingField }
}
}
private fun ContextUtils.createClassBodyType(name: String, fields: List<PropertyDescriptor>): 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<FunctionDescriptor, FunctionLlvmDeclarations>()
val classes = mutableMapOf<ClassDescriptor, ClassLlvmDeclarations>()
val fields = mutableMapOf<PropertyDescriptor, FieldLlvmDeclarations>()
val staticFields = mutableMapOf<PropertyDescriptor, StaticFieldLlvmDeclarations>()
var theUnitInstanceRef: ConstPointer? = null
private class Namer(val prefix: String) {
private val names = mutableMapOf<DeclarationDescriptor, Name>()
private val counts = mutableMapOf<FqName, Int>()
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)
}
}
@@ -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>): 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<LLVMTypeRef> {
val count = LLVMCountStructElementTypes(type)
return (0 until count).map {
LLVMStructGetTypeAtIndex(type, it)!!
}
}
@@ -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()}"
@@ -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<PropertyDescriptor>): 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<ConstValue>
val methods: List<ConstValue>
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)
}
@@ -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
@@ -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
}
@@ -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))
get() {
val unitDescriptor = context.builtIns.unit
return if (isExternal(unitDescriptor)) {
constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
} else {
context.llvmDeclarations.getUnitInstanceRef()
}
}
@@ -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 <T: Enum<T>> valueOfForEnum(name: String, arr: Array<T>) : T
fun <T: Enum<T>> valueOfForEnum(name: String, arr: Array<T>) : T
{
for (x in arr)
if (x.name == name)
@@ -35,7 +35,7 @@ internal fun <T: Enum<T>> valueOfForEnum(name: String, arr: Array<T>) : T
throw Exception("Invalid enum name: $name")
}
internal fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
{
return values.clone() as Array<T>
}