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 fa99c1ccca1..9bac4e4fc72 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 @@ -5,12 +5,18 @@ import kotlin_native.interop.* import llvm.* 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.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 { @@ -19,13 +25,18 @@ private fun crc32(str: String): Long { return c.value } +private fun crc32(name: Name) = crc32(name.toString()) + +private fun getKotlinType(field: IrField) = field.descriptor.returnType!! + private fun getLLVMType(field: IrField): LLVMOpaqueType { - val type = field.descriptor.returnType!! + 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() }!! } @@ -47,8 +58,6 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme private inner class ConstArray(val elemType: 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] @@ -92,23 +101,35 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme private inner class FieldTableRecord(val nameSignature: Long, val fieldOffset: Int) : Struct(runtime.fieldTableRecordType, Int64(nameSignature), Int32(fieldOffset)) - private inner class TypeInfo(val name: Long, val size: Int, val superType: CompileTimeValue, val fields: CompileTimeValue, - val fieldsCount: Int) : + private inner class MethodTableRecord(val nameSignature: Long, val methodEntryPoint: CompileTimeValue) : + Struct(runtime.methodTableRecordType, Int64(nameSignature), methodEntryPoint) + + private inner class TypeInfo(val name: Long, val size: Int, + val superType: CompileTimeValue, + val objOffsets: CompileTimeValue, + val objOffsetsCount: Int, + val interfaces: CompileTimeValue, + val interfacesCount: Int, + val methods: CompileTimeValue, + val methodsCount: Int, + val fields: CompileTimeValue, + val fieldsCount: Int) : Struct( runtime.typeInfoType, + Int64(name), Int32(size), superType, - Zero(pointer(int32Type)), - Int32(0), + objOffsets, + Int32(objOffsetsCount), - Zero(pointer(pointer(runtime.typeInfoType))), - Int32(0), + interfaces, + Int32(interfacesCount), - Zero(pointer(runtime.methodTableRecordType)), - Int32(0), + methods, + Int32(methodsCount), fields, Int32(fieldsCount) @@ -117,50 +138,115 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme private val builder = LLVMCreateBuilder() // TODO: dispose private val arena = Arena() // TODO: dispose - private fun addGlobalConst(name: String, value: CompileTimeValue): LLVMOpaqueValue? { + private fun addGlobalConst(name: String, value: CompileTimeValue): CompileTimeValue { val global = LLVMAddGlobal(module, value.getLlvmType(), name) LLVMSetInitializer(global, value.getLlvmValue()) - return global + return compileTimeValue(global) } - private fun getGlobalArrayPtr(array: LLVMOpaqueValue?): LLVMOpaqueValue? { + 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 LLVMBuildGEP(builder, array, indicesNativeArrayPtr, indices.size, "") + 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))) + } else { + Zero(pointer(elemType)) + } } private fun typeInfoFor(classDesc: ClassDescriptor): LLVMOpaqueValue? { - val globalName = classDesc.name.toString() + "_type" // FIXME: FQDN + 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) + 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 getDeclaredFields(irClass: IrClass) = irClass.declarations.mapNotNull { (it as? IrProperty)?.backingField } + + 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 } + } + override fun visitElement(element: IrElement) { element.acceptChildren(this, null) } override fun visitClass(declaration: IrClass) { - val className = declaration.descriptor.name // FIXME: FQDN - val fields = declaration.declarations.filterIsInstance().mapNotNull { it.backingField } - val fieldTypes = fields.map { getLLVMType(it) }.toTypedArray() - val classType = LLVMStructCreateNamed(LLVMGetModuleContext(module), "class." + className) - LLVMStructSetBody(classType, mallocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0], fieldTypes.size, 0) + visitElement(declaration) + + val className = declaration.descriptor.fqNameSafe + + val declaredFields = getDeclaredFields(declaration) + + val classType = createStructFor(className, declaredFields) val name = crc32(className.toString()) + val size = LLVMStoreSizeOfType(runtime.targetData, classType).toInt() + val superType = compileTimeValue(typeInfoFor(declaration.descriptor.getSuperClassOrAny())) - val fieldTableRecords = fields.mapIndexed { index, field -> - val nameSignature = crc32(field.descriptor.name.toString()) // FIXME: add signature + val interfaces = declaration.descriptor.getSuperInterfaces().map { compileTimeValue(typeInfoFor(it)) } + val interfacesPtr = addGlobalArray("kintf:$className", pointer(runtime.typeInfoType), interfaces) + + val refFieldIndices = declaredFields.mapIndexedNotNull { index, field -> + val type = getKotlinType(field) + if (!KotlinBuiltIns.isPrimitiveType(type)) { + index + } else { + null + } + } + // TODO: reuse offsets obtained for 'fields' below + 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 fieldOffset = LLVMOffsetOfElement(runtime.targetData, classType, index) FieldTableRecord(nameSignature, fieldOffset.toInt()) }.sortedBy { it.nameSignature } - val fieldsArray = ConstArray(runtime.fieldTableRecordType, fieldTableRecords) - val fieldsGlobal = addGlobalConst("${className}_fields", fieldsArray) - val fieldsPtr = compileTimeValue(getGlobalArrayPtr(fieldsGlobal)) + 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) + MethodTableRecord(nameSignature, methodEntryPoint) + }.sortedBy { it.nameSignature } + + val methodsPtr = addGlobalArray("kmethods:$className", runtime.methodTableRecordType, methods) + + val typeInfo = TypeInfo(name, size, + superType, + objOffsetsPtr, objOffsets.size, + interfacesPtr, interfaces.size, + methodsPtr, methods.size, + fieldsPtr, fields.size) - val typeInfo = TypeInfo(name, size, superType, fieldsPtr, fieldTableRecords.size) val typeInfoGlobal = typeInfoFor(declaration.descriptor) LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue()) }