From 059f890a1e357b8fccdd6339a837ee393a349808 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 3 Oct 2016 16:28:32 +0700 Subject: [PATCH] backend.native: support inheritance in RTTI also * distribute the code among several files * use descriptors instead of IR (where it is possible) * add other minor improvements --- .../kotlin/backend/native/DescriptorUtils.kt | 36 ++++ .../kotlin/backend/native/ModuleIndex.kt | 37 ++++ .../kotlin/backend/native/llvm/Context.kt | 17 ++ .../backend/native/llvm/ContextUtils.kt | 113 ++++++++++ .../kotlin/backend/native/llvm/DataLayout.kt | 16 ++ .../kotlin/backend/native/llvm/IrToBitcode.kt | 27 ++- .../kotlin/backend/native/llvm/LlvmUtils.kt | 58 ++++++ .../kotlin/backend/native/llvm/NameUtils.kt | 29 +++ .../backend/native/llvm/RTTIGenerator.kt | 197 ++++-------------- 9 files changed, 373 insertions(+), 157 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/ModuleIndex.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/DataLayout.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt new file mode 100644 index 00000000000..0478aa56326 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt @@ -0,0 +1,36 @@ +package org.jetbrains.kotlin.backend.native + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces + +/** + * List of all implemented interfaces (including those which implemented by a super class) + */ +internal val ClassDescriptor.implementedInterfaces: List + get() { + val superClassImplementedInterfaces = this.getSuperClassNotAny()?.implementedInterfaces ?: emptyList() + return (superClassImplementedInterfaces + this.getSuperInterfaces()).distinctBy { it.classId } + } + + +/** + * Implementation of given method. + * + * 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 + } + } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/ModuleIndex.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/ModuleIndex.kt new file mode 100644 index 00000000000..f6d4dd7c1e8 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/ModuleIndex.kt @@ -0,0 +1,37 @@ +package org.jetbrains.kotlin.backend.native + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +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 + + init { + val map = mutableMapOf() + + module.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + super.visitClass(declaration) + + map[declaration.descriptor.classId] = declaration + } + + }) + + classes = map + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt new file mode 100644 index 00000000000..3eb80618614 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import llvm.LLVMCreateBuilder +import llvm.LLVMDisposeBuilder +import llvm.LLVMOpaqueModule +import org.jetbrains.kotlin.backend.native.ModuleIndex +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment + +internal class Context(val irModule: IrModuleFragment, val runtime: Runtime, val llvmModule: LLVMOpaqueModule) { + val moduleIndex = ModuleIndex(irModule) + + val llvmBuilder = LLVMCreateBuilder() + + fun dispose() { + LLVMDisposeBuilder(llvmBuilder) + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt new file mode 100644 index 00000000000..133e20bdf8c --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt @@ -0,0 +1,113 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import kotlin_native.interop.mallocNativeArrayOf +import llvm.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny + +/** + * Provides utility methods to the implementer. + */ +internal interface ContextUtils { + val context: Context + + /** + * 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: do not use IR to get fields + val irClass = context.moduleIndex.classes[this.classId] ?: + throw IllegalArgumentException("Class ${this.fqNameSafe} is not found in current module") + + return irClass.declarations.mapNotNull { (it as? IrProperty)?.backingField?.descriptor } + } + + /** + * LLVM function generated from the Kotlin function. + * It may be declared as external function prototype. + */ + val FunctionDescriptor.llvmFunction: CompileTimeValue + get() { + assert (this.kind.isReal) + val globalName = this.symbolName + val module = context.llvmModule + val functionType = LLVMFunctionType(LLVMVoidType(), null, 0, 0) // FIXME: use correct types + val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType) + return compileTimeValue(function) + } + + /** + * Address of entry point of [llvmFunction]. + */ + val FunctionDescriptor.entryPointAddress: CompileTimeValue + get() { + val result = LLVMConstBitCast(this.llvmFunction.getLlvmValue(), pointerType(LLVMInt8Type())) + return compileTimeValue(result) + } + + /** + * Pointer to type info for given class. + * It may be declared as pointer to external variable. + */ + val ClassDescriptor.llvmTypeInfoPtr: CompileTimeValue + get() { + val module = context.llvmModule + val globalName = this.typeInfoSymbolName + val globalPtr = LLVMGetNamedGlobal(module, globalName) ?: + LLVMAddGlobal(module, context.runtime.typeInfoType, globalName) + + return compileTimeValue(globalPtr) + } + + /** + * Adds global variable with given name and value to [Context.llvmModule] of [context]. + * Returns pointer to this variable. + */ + fun addGlobalVar(name: String, value: CompileTimeValue): CompileTimeValue { + val global = LLVMAddGlobal(context.llvmModule, value.getLlvmType(), name) + LLVMSetInitializer(global, value.getLlvmValue()) + return compileTimeValue(global) + } + + /** + * Returns pointer to first element of given array. + * + * @param arrayPtr pointer to array + */ + private fun getPtrToFirstElem(arrayPtr: CompileTimeValue): CompileTimeValue { + val indices = longArrayOf(0, 0).map { LLVMConstInt(LLVMInt32Type(), it, 0) }.toTypedArray() + val indicesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *indices)[0] // TODO: dispose + + return compileTimeValue(LLVMBuildGEP(context.llvmBuilder, arrayPtr.getLlvmValue(), indicesNativeArrayPtr, indices.size, "")) + } + + /** + * Adds global array-typed variable with given name and value to [Context.llvmModule] of [context]. + * Returns pointer to the first element of the global array. + */ + fun addGlobalArray(name: String, elemType: LLVMOpaqueType?, elements: List): CompileTimeValue { + return if (elements.size > 0) { + getPtrToFirstElem(addGlobalVar(name, ConstArray(elemType, elements))) + } else { + Zero(pointerType(elemType)) + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/DataLayout.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/DataLayout.kt new file mode 100644 index 00000000000..a859f49f706 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/DataLayout.kt @@ -0,0 +1,16 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import llvm.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.types.KotlinType + +internal fun getLLVMType(type: KotlinType): LLVMOpaqueType { + return when { + KotlinBuiltIns.isBoolean(type) || KotlinBuiltIns.isByte(type) -> LLVMInt8Type() + KotlinBuiltIns.isShort(type) || KotlinBuiltIns.isChar(type) -> LLVMInt16Type() + KotlinBuiltIns.isInt(type) -> LLVMInt32Type() + KotlinBuiltIns.isLong(type) -> LLVMInt64Type() + !KotlinBuiltIns.isPrimitiveType(type) -> LLVMPointerType(LLVMInt8Type(), 0) + else -> throw NotImplementedError() + }!! +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt index cc713b6d847..f3dd316e968 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt @@ -1,13 +1,34 @@ package org.jetbrains.kotlin.backend.native.llvm import llvm.* +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { - val llvmModule = LLVMModuleCreateWithName("out")!! - val runtime = Runtime(runtimeFile) + val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose + val runtime = Runtime(runtimeFile) // TODO: dispose LLVMSetDataLayout(llvmModule, runtime.dataLayout) LLVMSetTarget(llvmModule, runtime.target) - module.accept(RTTIGenerator(llvmModule, runtime), null) + + val context = Context(module, runtime, llvmModule) // TODO: dispose + + module.accept(RTTIGeneratorVisitor(context), null) LLVMWriteBitcodeToFile(llvmModule, outFile) +} + +internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid { + val generator = RTTIGenerator(context) + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + super.visitClass(declaration) + generator.generate(declaration.descriptor) + } + } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt new file mode 100644 index 00000000000..c64179906fc --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt @@ -0,0 +1,58 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import kotlin_native.interop.mallocNativeArrayOf +import llvm.* + +/** + * Represents the value which can be emitted as bitcode const value + */ +internal abstract class CompileTimeValue { + + abstract fun getLlvmValue(): LLVMOpaqueValue? + + fun getLlvmType(): LLVMOpaqueType? { + return LLVMTypeOf(getLlvmValue()) + } + +} + +internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List) : CompileTimeValue() { + + override fun getLlvmValue(): LLVMOpaqueValue? { + val values = elements.map { it.getLlvmValue() }.toTypedArray() + val valuesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *values)[0] // FIXME: dispose + + return LLVMConstArray(elemType, valuesNativeArrayPtr, values.size) + } +} + +internal open class Struct(val type: LLVMOpaqueType?, val elements: List) : CompileTimeValue() { + + constructor(type: LLVMOpaqueType?, vararg elements: CompileTimeValue) : this(type, elements.toList()) + + override fun getLlvmValue(): LLVMOpaqueValue? { + val values = elements.map { it.getLlvmValue() }.toTypedArray() + val valuesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *values)[0] // FIXME: dispose + return LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size) + } +} + +internal class Int32(val value: Int) : CompileTimeValue() { + override fun getLlvmValue() = LLVMConstInt(LLVMInt32Type(), value.toLong(), 1) +} + +internal class Int64(val value: Long) : CompileTimeValue() { + override fun getLlvmValue() = LLVMConstInt(LLVMInt64Type(), value, 1) +} + +internal class Zero(val type: LLVMOpaqueType?) : CompileTimeValue() { + override fun getLlvmValue() = LLVMConstNull(type) +} + +internal fun compileTimeValue(value: LLVMOpaqueValue?) = object : CompileTimeValue() { + override fun getLlvmValue() = value +} + +internal val int32Type = LLVMInt32Type() + +internal fun pointerType(pointeeType: LLVMOpaqueType?) = LLVMPointerType(pointeeType, 0) \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt new file mode 100644 index 00000000000..6f9f743cbac --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt @@ -0,0 +1,29 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import java.util.zip.CRC32 + +private fun crc32(str: String): Long { + val c = CRC32() + c.update(str.toByteArray()) + return c.value +} + +internal val String.nameHash: Long + get() = crc32(this) + +internal val Name.nameHash: Long + get() = this.toString().nameHash + +internal val FqName.nameHash: Long + get() = this.toString().nameHash + +internal val FunctionDescriptor.symbolName: String + get() = "kfun:" + this.fqNameSafe.toString() // FIXME: add signature + +internal val ClassDescriptor.typeInfoSymbolName: String + get() = "ktype:" + this.fqNameSafe.toString() \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt index 9bac4e4fc72..5045f0101d5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt @@ -1,101 +1,23 @@ package org.jetbrains.kotlin.backend.native.llvm -import kotlin_native.interop.* +import kotlin_native.interop.mallocNativeArrayOf import llvm.* +import org.jetbrains.kotlin.backend.native.implementation +import org.jetbrains.kotlin.backend.native.implementedInterfaces import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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 -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces -import java.util.zip.CRC32 -private fun crc32(str: String): Long { - val c = CRC32() - c.update(str.toByteArray()) - return c.value -} -private fun crc32(name: Name) = crc32(name.toString()) +internal class RTTIGenerator(override val context: Context) : ContextUtils { -private fun getKotlinType(field: IrField) = field.descriptor.returnType!! - -private fun getLLVMType(field: IrField): LLVMOpaqueType { - val type = getKotlinType(field) - return when { - KotlinBuiltIns.isBoolean(type) || KotlinBuiltIns.isByte(type) -> LLVMInt8Type() - KotlinBuiltIns.isShort(type) || KotlinBuiltIns.isChar(type) -> LLVMInt16Type() - KotlinBuiltIns.isInt(type) -> LLVMInt32Type() - KotlinBuiltIns.isLong(type) -> LLVMInt64Type() - !KotlinBuiltIns.isPrimitiveType(type) -> LLVMPointerType(LLVMInt8Type(), 0) - else -> throw NotImplementedError() - }!! -} - -class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrElementVisitorVoid { - - /** - * Represents the value which can be emitted as bitcode const value - */ - private inner abstract class CompileTimeValue { - - abstract fun getLlvmValue(): LLVMOpaqueValue? - - fun getLlvmType(): LLVMOpaqueType? { - return LLVMTypeOf(getLlvmValue()) - } - - } - - private inner class ConstArray(val elemType: LLVMOpaqueType?, val elements: List) : CompileTimeValue() { - - override fun getLlvmValue(): LLVMOpaqueValue? { - val values = elements.map { it.getLlvmValue() }.toTypedArray() - val valuesNativeArrayPtr = arena.allocNativeArrayOf(LLVMOpaqueValue, *values)[0] - - return LLVMConstArray(elemType, valuesNativeArrayPtr, values.size) - } - } - - private inner open class Struct(val type: LLVMOpaqueType?, val elements: List) : CompileTimeValue() { - - constructor(type: LLVMOpaqueType?, vararg elements: CompileTimeValue) : this(type, elements.toList()) - - override fun getLlvmValue(): LLVMOpaqueValue? { - val values = elements.map { it.getLlvmValue() }.toTypedArray() - val valuesNativeArrayPtr = arena.allocNativeArrayOf(LLVMOpaqueValue, *values)[0] - return LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size) - } - } - - private inner class Int32(val value: Int) : CompileTimeValue() { - override fun getLlvmValue() = LLVMConstInt(LLVMInt32Type(), value.toLong(), 1) - } - - private inner class Int64(val value: Long) : CompileTimeValue() { - override fun getLlvmValue() = LLVMConstInt(LLVMInt64Type(), value, 1) - } - - private inner class Zero(val type: LLVMOpaqueType?) : CompileTimeValue() { - override fun getLlvmValue() = LLVMConstNull(type) - } - - private fun compileTimeValue(value: LLVMOpaqueValue?) = object : CompileTimeValue() { - override fun getLlvmValue() = value - } - - private val int32Type = LLVMInt32Type() - - private fun pointer(type: LLVMOpaqueType?) = LLVMPointerType(type, 0) // TODO: why 0? + val runtime: Runtime + get() = context.runtime private inner class FieldTableRecord(val nameSignature: Long, val fieldOffset: Int) : @@ -135,83 +57,50 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme Int32(fieldsCount) ) - private val builder = LLVMCreateBuilder() // TODO: dispose - private val arena = Arena() // TODO: dispose - - private fun addGlobalConst(name: String, value: CompileTimeValue): CompileTimeValue { - val global = LLVMAddGlobal(module, value.getLlvmType(), name) - LLVMSetInitializer(global, value.getLlvmValue()) - return compileTimeValue(global) - } - - private fun getPtrToFirstElem(arrayPtr: CompileTimeValue): CompileTimeValue { - val indices = longArrayOf(0, 0).map { LLVMConstInt(LLVMInt32Type(), it, 0) }.toTypedArray() - val indicesNativeArrayPtr = arena.allocNativeArrayOf(LLVMOpaqueValue, *indices)[0] - - return compileTimeValue(LLVMBuildGEP(builder, arrayPtr.getLlvmValue(), indicesNativeArrayPtr, indices.size, "")) - } - - private fun addGlobalArray(name: String, elemType: LLVMOpaqueType?, elements: List): CompileTimeValue { - return if (elements.size > 0) { - getPtrToFirstElem(addGlobalConst(name, ConstArray(elemType, elements))) + // TODO: probably it should be moved out of this class and shared. + private fun createStructFor(className: FqName, fields: List): LLVMOpaqueType? { + val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className) + val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray() + val fieldTypesNativeArrayPtr = if (fieldTypes.size > 0) { + mallocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0] // TODO: dispose } else { - Zero(pointer(elemType)) + null } - } - private fun typeInfoFor(classDesc: ClassDescriptor): LLVMOpaqueValue? { - val globalName = "ktype:" + classDesc.fqNameSafe.toString() - return LLVMGetNamedGlobal(module, globalName) ?: LLVMAddGlobal(module, runtime.typeInfoType, globalName) - } - - private fun createStructFor(className: FqName, declaredFields: List): LLVMOpaqueType? { - val fieldTypes = declaredFields.map { getLLVMType(it) }.toTypedArray() - val classType = LLVMStructCreateNamed(LLVMGetModuleContext(module), "kclass:" + className) - val fieldTypesNativeArray = mallocNativeArrayOf(LLVMOpaqueType, *fieldTypes) - LLVMStructSetBody(classType, fieldTypesNativeArray[0], fieldTypes.size, 0) + LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0) return classType } - private fun methodEntryPoint(function: FunctionDescriptor): CompileTimeValue { - val globalName = "kfun:" + function.fqNameSafe.toString() // FIXME: add signature - val functionType = LLVMFunctionType(LLVMVoidType(), null, 0, 0) // FIXME: use correct types - val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType) - val result = compileTimeValue(LLVMConstBitCast(function, pointer(LLVMInt8Type()))) - return result + private fun getMethodTableEntries(classDesc: ClassDescriptor): List { + val contributedDescriptors = classDesc.unsubstitutedMemberScope.getContributedDescriptors() + // (includes declarations from supers) + + val methods = contributedDescriptors.filterIsInstance() + + val properties = contributedDescriptors.filterIsInstance() + val getters = properties.mapNotNull { it.getter } + val setters = properties.mapNotNull { it.setter } + + return methods + getters + setters } - private fun getDeclaredFields(irClass: IrClass) = irClass.declarations.mapNotNull { (it as? IrProperty)?.backingField } + fun generate(classDesc: ClassDescriptor) { - private fun getDeclaredMethods(irClass: IrClass): List { - val functions = irClass.declarations.filterIsInstance() - val properties = irClass.declarations.filterIsInstance() - return functions + properties.mapNotNull { it.getter } + properties.mapNotNull { it.setter } - } + val className = classDesc.fqNameSafe - override fun visitElement(element: IrElement) { - element.acceptChildren(this, null) - } + val classType = createStructFor(className, classDesc.fields) - override fun visitClass(declaration: IrClass) { - visitElement(declaration) - - val className = declaration.descriptor.fqNameSafe - - val declaredFields = getDeclaredFields(declaration) - - val classType = createStructFor(className, declaredFields) - - val name = crc32(className.toString()) + val name = className.nameHash val size = LLVMStoreSizeOfType(runtime.targetData, classType).toInt() - val superType = compileTimeValue(typeInfoFor(declaration.descriptor.getSuperClassOrAny())) + val superType = classDesc.getSuperClassOrAny().llvmTypeInfoPtr - val interfaces = declaration.descriptor.getSuperInterfaces().map { compileTimeValue(typeInfoFor(it)) } - val interfacesPtr = addGlobalArray("kintf:$className", pointer(runtime.typeInfoType), interfaces) + val interfaces = classDesc.implementedInterfaces.map { it.llvmTypeInfoPtr } + val interfacesPtr = addGlobalArray("kintf:$className", pointerType(runtime.typeInfoType), interfaces) - val refFieldIndices = declaredFields.mapIndexedNotNull { index, field -> - val type = getKotlinType(field) + val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field -> + val type = field.returnType!! if (!KotlinBuiltIns.isPrimitiveType(type)) { index } else { @@ -222,19 +111,19 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(runtime.targetData, classType, it) } val objOffsetsPtr = addGlobalArray("krefs:$className", int32Type, objOffsets.map { Int32(it.toInt()) }) - // TODO: add fields from supers - val fields = declaredFields.mapIndexed { index, field -> - val nameSignature = crc32(field.descriptor.name) // FIXME: add signature + val fields = classDesc.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.nameHash // FIXME: add signature val fieldOffset = LLVMOffsetOfElement(runtime.targetData, classType, index) FieldTableRecord(nameSignature, fieldOffset.toInt()) }.sortedBy { it.nameSignature } val fieldsPtr = addGlobalArray("kfields:$className", runtime.fieldTableRecordType, fields) - // TODO: add methods from supers - val methods = getDeclaredMethods(declaration).map { - val nameSignature = crc32(it.descriptor.name) // FIXME: add signature - val methodEntryPoint = methodEntryPoint(it.descriptor) + val methods = getMethodTableEntries(classDesc).map { + val nameSignature = it.name.nameHash // FIXME: add signature + // TODO: compile-time resolution limits binary compatibility + val methodEntryPoint = it.implementation.entryPointAddress MethodTableRecord(nameSignature, methodEntryPoint) }.sortedBy { it.nameSignature } @@ -247,7 +136,7 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme methodsPtr, methods.size, fieldsPtr, fields.size) - val typeInfoGlobal = typeInfoFor(declaration.descriptor) + val typeInfoGlobal = classDesc.llvmTypeInfoPtr.getLlvmValue() // TODO: it is a hack LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue()) }