Merged different RTTI-related structure builders

This includes vTable, fields and associated objects
This commit is contained in:
Igor Chevdar
2019-05-17 14:51:56 +03:00
parent 9946fcb8c1
commit 46edc3595d
9 changed files with 92 additions and 114 deletions
@@ -257,10 +257,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
KonanReflectionTypes(moduleDescriptor, KonanFqNames.internalPackageName)
}
private val vtableBuilders = mutableMapOf<IrClass, ClassVtablesBuilder>()
val layoutBuilders = mutableMapOf<IrClass, ClassLayoutBuilder>()
fun getVtableBuilder(classDescriptor: IrClass) = vtableBuilders.getOrPut(classDescriptor) {
ClassVtablesBuilder(classDescriptor, this)
fun getLayoutBuilder(irClass: IrClass) = layoutBuilders.getOrPut(irClass) {
ClassLayoutBuilder(irClass, this)
}
// We serialize untouched descriptor tree and inline IR bodies
@@ -12,11 +12,10 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.overrides
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
internal class OverriddenFunctionInfo(
val function: IrSimpleFunction,
@@ -78,7 +77,7 @@ internal class OverriddenFunctionInfo(
}
}
internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
private val DEBUG = 0
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
@@ -98,7 +97,7 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
emptyList()
} else {
val superClass = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner
context.getVtableBuilder(superClass).vtableEntries
context.getLayoutBuilder(superClass).vtableEntries
}
val methods = irClass.sortedOverridableOrOverridingMethods
@@ -172,6 +171,71 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) {
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
/**
* All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/
val fields: List<IrField> by lazy {
val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fields else emptyList()
superFields + getDeclaredFields()
}
val associatedObjects by lazy {
val result = mutableMapOf<IrClass, IrClass>()
irClass.annotations.forEach {
val irFile = irClass.getContainingFile()
val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass
?: error(irFile, it, "unexpected annotation")
if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) {
val argument = it.getValueArgument(0)
val irClassReference = argument as? IrClassReference
?: error(irFile, argument, "unexpected annotation argument")
val associatedObject = irClassReference.symbol.owner
if (associatedObject !is IrClass || !associatedObject.isObject) {
error(irFile, irClassReference, "argument is not a singleton")
}
if (annotationClass in result) {
error(
irFile,
it,
"duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}"
)
}
result[annotationClass] = associatedObject
}
}
result
}
/**
* Fields declared in the class.
*/
private fun getDeclaredFields(): List<IrField> {
val fields = irClass.declarations.mapNotNull {
when (it) {
is IrField -> it.takeIf { it.isReal }
is IrProperty -> it.takeIf { it.isReal }?.backingField
else -> null
}
}
if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields"))))
return fields
return fields.sortedBy { it.fqNameForIrSerialization.localHash.value }
}
private val IrClass.sortedOverridableOrOverridingMethods: List<IrSimpleFunction>
get() =
this.simpleFunctions()
@@ -722,7 +722,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
val llvmMethod = if (!owner.isInterface) {
// If this is a virtual method of the class - we can call via vtable.
val index = context.getVtableBuilder(owner).vtableIndex(anyMethod ?: irFunction)
val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction)
val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
val vtable = bitcast(kInt8PtrPtr, vtablePlace)
val slot = gep(vtable, Int32(index).llvm)
@@ -65,7 +65,6 @@ internal class LlvmDeclarations(
internal class ClassLlvmDeclarations(
val bodyType: LLVMTypeRef,
val fields: List<IrField>, // TODO: it is not an LLVM declaration.
val typeInfoGlobal: StaticData.Global,
val writableTypeInfoGlobal: StaticData.Global?,
val typeInfo: ConstPointer,
@@ -88,53 +87,6 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
internal class UniqueLlvmDeclarations(val pointer: ConstPointer)
// 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.
*/
internal fun ContextUtils.getFields(irClass: IrClass) = context.getFields(irClass)
internal fun Context.getFields(irClass: IrClass): List<IrField> {
val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) getFields(superClass) else emptyList()
return superFields + getDeclaredFields(irClass)
}
/**
* Fields declared in the class.
*/
private fun Context.getDeclaredFields(irClass: IrClass): List<IrField> {
// 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 PropertyDescriptor.
//
// We mark serialized properties with a Konan protobuf extension bit,
// so it is present in DeserializedPropertyDescriptor.
//
// In this function we check the presence of the backing field
// two ways: first we check IR, then we check the protobuf extension.
val fields = irClass.declarations.mapNotNull {
when (it) {
is IrField -> it.takeIf { it.isReal }
is IrProperty -> it.takeIf { it.isReal }?.konanBackingField
else -> null
}
}
// TODO: hack over missed parents in deserialized fields/property declarations.
fields.forEach{it.parent = irClass}
if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields"))))
return fields
return fields.sortedBy {
it.fqNameForIrSerialization.localHash.value
}
}
private fun ContextUtils.createClassBodyType(name: String, fields: List<IrField>): LLVMTypeRef {
val fieldTypes = listOf(runtime.objHeaderType) + fields.map { getLLVMType(it.type) }
// TODO: consider adding synthetic ObjHeader field to Any.
@@ -213,7 +165,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations {
val internalName = qualifyInternalName(declaration)
val fields = getFields(declaration)
val fields = context.getLayoutBuilder(declaration).fields
val bodyType = createClassBodyType("kclassbody:$internalName", fields)
val typeInfoPtr: ConstPointer
@@ -232,7 +184,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
val typeInfoWithVtableType = structType(
runtime.typeInfoType,
LLVMArrayType(int8TypePtr, context.getVtableBuilder(declaration).vtableEntries.size)!!
LLVMArrayType(int8TypePtr, context.getLayoutBuilder(declaration).vtableEntries.size)!!
)
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
@@ -290,7 +242,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
it.setZeroInitializer()
}
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
singletonDeclarations, objCDeclarations)
}
@@ -367,7 +319,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
if (containingClass != null) {
val classDeclarations = this.classes[containingClass] ?:
error(containingClass.descriptor.toString())
val allFields = classDeclarations.fields
val allFields = context.getLayoutBuilder(containingClass).fields
this.fields[declaration] = FieldLlvmDeclarations(
allFields.indexOf(declaration) + 1, // First field is ObjHeader.
classDeclarations.bodyType
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.backend.konan.ir.isAnonymousObject
import org.jetbrains.kotlin.backend.konan.ir.isLocal
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.KonanAbiVersion
@@ -47,7 +46,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun checkAcyclicClass(irClass: IrClass): Boolean = when {
irClass.symbol == context.ir.symbols.array -> false
irClass.isArray -> true
context.llvmDeclarations.forClass(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true
context.getLayoutBuilder(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true
else -> false
}
@@ -248,7 +247,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map {
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
val implementation = it.implementation
if (implementation == null || implementation.isExternalObjCClassMethod()) {
NullPointer(int8Type)
@@ -261,7 +260,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fun methodTableRecords(irClass: IrClass): List<MethodTableRecord> {
val functionNames = mutableMapOf<Long, OverriddenFunctionInfo>()
return context.getVtableBuilder(irClass).methodTableEntries.map {
return context.getLayoutBuilder(irClass).methodTableEntries.map {
val functionName = it.overriddenFunction.functionName
val nameSignature = functionName.localHash
val previous = functionNames.putIfAbsent(nameSignature.value, it)
@@ -295,11 +294,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr))
} else {
data class FieldRecord(val offset: Int, val type: Int, val name: String)
val fields = getStructElements(bodyType).drop(1).mapIndexedNotNull { index, type ->
val fields = getStructElements(bodyType).drop(1).mapIndexed { index, type ->
FieldRecord(
LLVMOffsetOfElement(llvmTargetData, bodyType, index + 1).toInt(),
mapRuntimeType(type),
llvmDeclarations.fields[index].name.asString())
context.getLayoutBuilder(irClass).fields[index].name.asString())
}
val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", int32Type,
fields.map { Int32(it.offset) })
@@ -315,7 +314,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
private fun genAssociatedObjects(irClass: IrClass): ConstPointer? {
val associatedObjects = getAssociatedObjects(irClass)
val associatedObjects = context.getLayoutBuilder(irClass).associatedObjects
if (associatedObjects.isEmpty()) {
return null
}
@@ -425,42 +424,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
}
private fun getAssociatedObjects(irClass: IrClass): Map<IrClass, IrClass> {
val result = mutableMapOf<IrClass, IrClass>()
irClass.annotations.forEach {
val irFile = irClass.getContainingFile()
val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass
?: error(irFile, it, "unexpected annotation")
if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) {
val argument = it.getValueArgument(0)
val irClassReference = argument as? IrClassReference
?: error(irFile, argument, "unexpected annotation argument")
val associatedObject = irClassReference.symbol.owner
if (associatedObject !is IrClass || !associatedObject.isObject) {
error(irFile, irClassReference, "argument is not a singleton")
}
if (annotationClass in result) {
error(
irFile,
it,
"duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}"
)
}
result[annotationClass] = associatedObject
}
}
return result
}
// Keep in sync with Konan_TypeFlags in TypeInfo.h.
private const val TF_IMMUTABLE = 1
private const val TF_ACYCLIC = 2
@@ -102,7 +102,7 @@ internal fun StaticData.createConstArrayList(array: ConstPointer, length: Int):
// Now sort these values according to the order of fields returned by getFields()
// to match the sorting order of the real ArrayList().
val sorted = linkedMapOf<String, ConstValue>()
getFields(arrayListClass).forEach {
context.getLayoutBuilder(arrayListClass).fields.forEach {
val fqName = it.fqNameForIrSerialization.asString()
sorted.put(fqName, arrayListFields[fqName]!!)
}
@@ -901,7 +901,7 @@ private fun ObjCExportCodeGenerator.vtableIndex(irFunction: IrSimpleFunction): I
return if (irClass.isInterface) {
null
} else {
context.getVtableBuilder(irClass).vtableIndex(irFunction)
context.getLayoutBuilder(irClass).vtableIndex(irFunction)
}
}
@@ -974,7 +974,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
val vtableSize = if (irClass.kind == ClassKind.INTERFACE) {
-1
} else {
context.getVtableBuilder(irClass).vtableEntries.size
context.getLayoutBuilder(irClass).vtableEntries.size
}
val vtable = if (!irClass.isInterface && !irClass.typeInfoHasVtableAttached) {
@@ -651,8 +651,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
value
)
} else {
val vTableBuilder = context.getVtableBuilder(owner)
val vtableIndex = vTableBuilder.vtableIndex(callee)
val vtableIndex = context.getLayoutBuilder(owner).vtableIndex(callee)
DataFlowIR.Node.VtableCall(
symbolTable.mapFunction(callee.target),
receiverType,
@@ -530,9 +530,9 @@ internal object DataFlowIR {
type.superTypes += irClass.superTypes.map { mapClassReferenceType(it.getClass()!!) }
if (!isAbstract) {
val vtableBuilder = context.getVtableBuilder(irClass)
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
vtableBuilder.methodTableEntries.forEach {
val layoutBuilder = context.getLayoutBuilder(irClass)
type.vtable += layoutBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) }
layoutBuilder.methodTableEntries.forEach {
type.itable[it.overriddenFunction.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!)
}
}