Skip to content

Commit

Permalink
[LLVM 16] Opaque pointers: fix generating TypeInfo
Browse files Browse the repository at this point in the history
Currently, when the Kotlin/Native compiler generates a TypeInfo, it
has to know which fields are object references (the GC uses that info).

The compiler checks field LLVM types for that. But with LLVM opaque
pointers, it won't be possible to distinguish object reference from
another pointer type.

This commit addresses that by using IR types or other contextual
information for such checks and storing that in `ClassLlvmDeclarations`.

^KT-67692

Co-authored-by: Svyatoslav Scherbina <svyatoslav.scherbina@jetbrains.com>
  • Loading branch information
2 people authored and Space Cloud committed May 21, 2024
1 parent 23026db commit 79be7f9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ internal class StackLocalsManagerImpl(

override fun alloc(irClass: IrClass): LLVMValueRef = with(functionGenerationContext) {
val classInfo = llvmDeclarations.forClass(irClass)
val type = classInfo.bodyType
val type = classInfo.bodyType.llvmBodyType
val stackLocal = appendingTo(bbInitStackLocals) {
val stackSlot = LLVMBuildAlloca(builder, type, "")!!
LLVMSetAlignment(stackSlot, classInfo.alignment)
Expand Down Expand Up @@ -512,7 +512,7 @@ internal class StackLocalsManagerImpl(
}
} else {
val info = llvmDeclarations.forClass(stackLocal.irClass)
val type = info.bodyType
val type = info.bodyType.llvmBodyType
for (fieldIndex in info.fieldIndices.values.sorted()) {
val fieldType = LLVMStructGetTypeAtIndex(type, fieldIndex)!!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDe

}

internal class ObjectBodyType(val llvmBodyType: LLVMTypeRef, objectFieldIndices: List<Int>) {
val sortedIndicesOfObjectFields = objectFieldIndices.sorted()
}

internal class ClassLlvmDeclarations(
val bodyType: LLVMTypeRef,
val bodyType: ObjectBodyType,
val typeInfoGlobal: StaticData.Global,
val writableTypeInfoGlobal: StaticData.Global?,
val typeInfo: ConstPointer,
Expand Down Expand Up @@ -252,6 +256,14 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
context.getLayoutBuilder(declaration).getFields(llvm)
val (bodyType, alignment, fieldIndices) = createClassBody("kclassbody:$internalName", fields)

val objectFieldIndices = fields.mapNotNull {
if (it.type.binaryTypeIsReference()) {
fieldIndices.getValue(it.irFieldSymbol)
} else {
null
}
}

require(alignment == runtime.objectAlignment) {
"Over-aligned objects are not supported yet: expected alignment for ${declaration.fqNameWhenAvailable} is $alignment"
}
Expand Down Expand Up @@ -335,7 +347,15 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
it.setZeroInitializer()
}

return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr, objCDeclarations, alignment, fieldIndices)
return ClassLlvmDeclarations(
ObjectBodyType(bodyType, objectFieldIndices),
typeInfoGlobal,
writableTypeInfoGlobal,
typeInfoPtr,
objCDeclarations,
alignment,
fieldIndices
)
}

private fun createUniqueDeclarations(
Expand Down Expand Up @@ -392,12 +412,13 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
val classDeclarations = (containingClass.metadata as? KonanMetadata.Class)?.llvm
?: error(containingClass.render())
val index = classDeclarations.fieldIndices[declaration.symbol]!!
val bodyType = classDeclarations.bodyType.llvmBodyType
declaration.metadata = KonanMetadata.InstanceField(
declaration,
FieldLlvmDeclarations(
index,
classDeclarations.bodyType,
gcd(LLVMOffsetOfElement(llvm.runtime.targetData, classDeclarations.bodyType, index), llvm.runtime.objectAlignment.toLong()).toInt()
bodyType,
gcd(LLVMOffsetOfElement(llvm.runtime.targetData, bodyType, index), llvm.runtime.objectAlignment.toLong()).toInt()
)
)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ internal class RTTIGenerator(
)

// Keep in sync with Konan_RuntimeType.
private val runtimeTypeMap = mapOf(
llvm.kObjHeaderPtr to 1,
private val RT_OBJECT = 1
private val primitiveRuntimeTypeMap = mapOf(
llvm.int8Type to 2,
llvm.int16Type to 3,
llvm.int32Type to 4,
Expand Down Expand Up @@ -208,7 +208,7 @@ internal class RTTIGenerator(

val llvmDeclarations = generationState.llvmDeclarations.forClass(irClass)

val bodyType = llvmDeclarations.bodyType
val bodyType = llvmDeclarations.bodyType.llvmBodyType

val instanceSize = getInstanceSize(bodyType, irClass)

Expand All @@ -227,7 +227,7 @@ internal class RTTIGenerator(
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
pointerType(runtime.typeInfoType), interfaces)

val objOffsets = getObjOffsets(bodyType)
val objOffsets = getObjOffsets(llvmDeclarations.bodyType)

val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", llvm.int32Type, objOffsets)

Expand Down Expand Up @@ -264,7 +264,7 @@ internal class RTTIGenerator(
associatedObjects = genAssociatedObjects(irClass),
processObjectInMark = when {
irClass.symbol == context.ir.symbols.array -> llvm.Kotlin_processArrayInMark.toConstPointer()
else -> genProcessObjectInMark(bodyType)
else -> genProcessObjectInMark(llvmDeclarations.bodyType)
},
requiredAlignment = llvmDeclarations.alignment
)
Expand All @@ -282,16 +282,9 @@ internal class RTTIGenerator(
exportTypeInfoIfRequired(irClass, irClass.llvmTypeInfoPtr)
}

private fun getIndicesOfObjectFields(bodyType: LLVMTypeRef) : List<Int> =
getStructElements(bodyType).mapIndexedNotNull { index, type ->
index.takeIf {
isObjectType(type)
}
}

private fun getObjOffsets(bodyType: LLVMTypeRef): List<ConstInt32> =
getIndicesOfObjectFields(bodyType).map { index ->
llvm.constInt32(LLVMOffsetOfElement(llvmTargetData, bodyType, index).toInt())
private fun getObjOffsets(bodyType: ObjectBodyType): List<ConstInt32> =
bodyType.sortedIndicesOfObjectFields.map { index ->
llvm.constInt32(LLVMOffsetOfElement(llvmTargetData, bodyType.llvmBodyType, index).toInt())
}

fun vtable(irClass: IrClass): ConstArray {
Expand Down Expand Up @@ -392,8 +385,14 @@ internal class RTTIGenerator(
}
}

private fun mapRuntimeType(type: LLVMTypeRef): Int =
runtimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
private fun mapRuntimeType(type: LLVMTypeRef, isObjectType: Boolean): Int {
if (isObjectType) {
require(type == llvm.kObjHeaderPtr) { "Expected object type, got ${llvmtype2string(type)}" }
return RT_OBJECT
}

return primitiveRuntimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
}

private val debugRuntimeOrNull: LLVMModuleRef? by lazy {
context.config.runtimeNativeLibraries.singleOrNull { it.endsWith("debug.bc")}?.let {
Expand Down Expand Up @@ -426,24 +425,28 @@ internal class RTTIGenerator(

val className = irClass.fqNameForIrSerialization.toString()
val llvmDeclarations = generationState.llvmDeclarations.forClass(irClass)
val bodyType = llvmDeclarations.bodyType
val bodyType = llvmDeclarations.bodyType.llvmBodyType
val elementType = getElementType(irClass)

val value = if (elementType != null) {
// An array type.
val runtimeElementType = mapRuntimeType(elementType)
val isElementTypeObject = irClass.isKotlinArray()
val runtimeElementType = mapRuntimeType(elementType, isElementTypeObject)
Struct(runtime.extendedTypeInfoType,
llvm.constInt32(-runtimeElementType),
NullPointer(llvm.int32Type), NullPointer(llvm.int8Type), NullPointer(llvm.int8PtrType),
debugOperationsSize, debugOperations)
} else {
class FieldRecord(val offset: Int, val type: Int, val name: String)

val objectFieldIndices = llvmDeclarations.bodyType.sortedIndicesOfObjectFields.toSet()

val fields = context.getLayoutBuilder(irClass).getFields(llvm).map {
val index = llvmDeclarations.fieldIndices[it.irFieldSymbol]!!
val isObjectType = index in objectFieldIndices
FieldRecord(
LLVMOffsetOfElement(llvmTargetData, bodyType, index).toInt(),
mapRuntimeType(LLVMStructGetTypeAtIndex(bodyType, index)!!),
mapRuntimeType(LLVMStructGetTypeAtIndex(bodyType, index)!!, isObjectType),
it.name)
}
val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", llvm.int32Type,
Expand Down Expand Up @@ -482,8 +485,8 @@ internal class RTTIGenerator(
)
}

private fun genProcessObjectInMark(classType: LLVMTypeRef) : ConstPointer {
val indicesOfObjectFields = getIndicesOfObjectFields(classType)
private fun genProcessObjectInMark(classType: ObjectBodyType): ConstPointer {
val indicesOfObjectFields = classType.sortedIndicesOfObjectFields
return when {
indicesOfObjectFields.isEmpty() -> {
// TODO: Try to generate it here instead of importing from the runtime.
Expand All @@ -500,12 +503,12 @@ internal class RTTIGenerator(
fun generateSyntheticInterfaceImpl(
irClass: IrClass,
methodImpls: Map<IrFunction, ConstPointer>,
bodyType: LLVMTypeRef,
bodyType: ObjectBodyType,
immutable: Boolean = false
): ConstPointer {
assert(irClass.isInterface)

val size = LLVMStoreSizeOfType(llvmTargetData, bodyType).toInt()
val size = LLVMStoreSizeOfType(llvmTargetData, bodyType.llvmBodyType).toInt()

val superClass = context.ir.symbols.any.owner

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
// but only if it is equivalent to its dynamic translation result. If block returns void, then it's not like that:
val useSeparateHolder = bridge.returnsVoid

val bodyType = if (useSeparateHolder) {
llvm.structType(codegen.kObjHeader, codegen.kObjHeaderPtr)
val objectBodyType = if (useSeparateHolder) {
ObjectBodyType(
llvm.structType(codegen.kObjHeader, codegen.kObjHeaderPtr),
objectFieldIndices = listOf(1)
)
} else {
llvm.structType(codegen.kObjHeader)
ObjectBodyType(
llvm.structType(codegen.kObjHeader),
objectFieldIndices = emptyList()
)
}
val bodyType = objectBodyType.llvmBodyType

val invokeImpl = functionGenerator(
LlvmFunctionSignature(invokeMethod, codegen).toProto(
Expand Down Expand Up @@ -87,7 +94,7 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
val typeInfo = rttiGenerator.generateSyntheticInterfaceImpl(
irInterface,
mapOf(invokeMethod to invokeImpl.toConstPointer()),
bodyType,
objectBodyType,
immutable = true
)
val functionSig = LlvmFunctionSignature(codegen.kObjHeaderPtrReturnType, listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(codegen.kObjHeaderPtrPtr)))
Expand Down

0 comments on commit 79be7f9

Please sign in to comment.