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
This commit is contained in:
+36
@@ -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<ClassDescriptor>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -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<ClassId, IrClass>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val map = mutableMapOf<ClassId, IrClass>()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+113
@@ -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<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: 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>): CompileTimeValue {
|
||||||
|
return if (elements.size > 0) {
|
||||||
|
getPtrToFirstElem(addGlobalVar(name, ConstArray(elemType, elements)))
|
||||||
|
} else {
|
||||||
|
Zero(pointerType(elemType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -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()
|
||||||
|
}!!
|
||||||
|
}
|
||||||
+24
-3
@@ -1,13 +1,34 @@
|
|||||||
package org.jetbrains.kotlin.backend.native.llvm
|
package org.jetbrains.kotlin.backend.native.llvm
|
||||||
|
|
||||||
import 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.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
|
||||||
fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) {
|
fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) {
|
||||||
val llvmModule = LLVMModuleCreateWithName("out")!!
|
val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose
|
||||||
val runtime = Runtime(runtimeFile)
|
val runtime = Runtime(runtimeFile) // TODO: dispose
|
||||||
LLVMSetDataLayout(llvmModule, runtime.dataLayout)
|
LLVMSetDataLayout(llvmModule, runtime.dataLayout)
|
||||||
LLVMSetTarget(llvmModule, runtime.target)
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+58
@@ -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>) : 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>) : 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)
|
||||||
+29
@@ -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()
|
||||||
+43
-154
@@ -1,101 +1,23 @@
|
|||||||
package org.jetbrains.kotlin.backend.native.llvm
|
package org.jetbrains.kotlin.backend.native.llvm
|
||||||
|
|
||||||
|
|
||||||
import kotlin_native.interop.*
|
import kotlin_native.interop.mallocNativeArrayOf
|
||||||
import llvm.*
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
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.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
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!!
|
val runtime: Runtime
|
||||||
|
get() = context.runtime
|
||||||
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>) : 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>) : 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?
|
|
||||||
|
|
||||||
|
|
||||||
private inner class FieldTableRecord(val nameSignature: Long, val fieldOffset: Int) :
|
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)
|
Int32(fieldsCount)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val builder = LLVMCreateBuilder() // TODO: dispose
|
// TODO: probably it should be moved out of this class and shared.
|
||||||
private val arena = Arena() // TODO: dispose
|
private fun createStructFor(className: FqName, fields: List<PropertyDescriptor>): LLVMOpaqueType? {
|
||||||
|
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className)
|
||||||
private fun addGlobalConst(name: String, value: CompileTimeValue): CompileTimeValue {
|
val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray()
|
||||||
val global = LLVMAddGlobal(module, value.getLlvmType(), name)
|
val fieldTypesNativeArrayPtr = if (fieldTypes.size > 0) {
|
||||||
LLVMSetInitializer(global, value.getLlvmValue())
|
mallocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0] // TODO: dispose
|
||||||
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>): CompileTimeValue {
|
|
||||||
return if (elements.size > 0) {
|
|
||||||
getPtrToFirstElem(addGlobalConst(name, ConstArray(elemType, elements)))
|
|
||||||
} else {
|
} else {
|
||||||
Zero(pointer(elemType))
|
null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun typeInfoFor(classDesc: ClassDescriptor): LLVMOpaqueValue? {
|
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
|
||||||
val globalName = "ktype:" + classDesc.fqNameSafe.toString()
|
|
||||||
return LLVMGetNamedGlobal(module, globalName) ?: LLVMAddGlobal(module, runtime.typeInfoType, globalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createStructFor(className: FqName, declaredFields: List<IrField>): 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
|
return classType
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun methodEntryPoint(function: FunctionDescriptor): CompileTimeValue {
|
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||||
val globalName = "kfun:" + function.fqNameSafe.toString() // FIXME: add signature
|
val contributedDescriptors = classDesc.unsubstitutedMemberScope.getContributedDescriptors()
|
||||||
val functionType = LLVMFunctionType(LLVMVoidType(), null, 0, 0) // FIXME: use correct types
|
// (includes declarations from supers)
|
||||||
val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType)
|
|
||||||
val result = compileTimeValue(LLVMConstBitCast(function, pointer(LLVMInt8Type())))
|
val methods = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
|
||||||
return result
|
|
||||||
|
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
|
||||||
|
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<IrFunction> {
|
val className = classDesc.fqNameSafe
|
||||||
val functions = irClass.declarations.filterIsInstance<IrFunction>()
|
|
||||||
val properties = irClass.declarations.filterIsInstance<IrProperty>()
|
|
||||||
return functions + properties.mapNotNull { it.getter } + properties.mapNotNull { it.setter }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitElement(element: IrElement) {
|
val classType = createStructFor(className, classDesc.fields)
|
||||||
element.acceptChildren(this, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitClass(declaration: IrClass) {
|
val name = className.nameHash
|
||||||
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 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 interfaces = classDesc.implementedInterfaces.map { it.llvmTypeInfoPtr }
|
||||||
val interfacesPtr = addGlobalArray("kintf:$className", pointer(runtime.typeInfoType), interfaces)
|
val interfacesPtr = addGlobalArray("kintf:$className", pointerType(runtime.typeInfoType), interfaces)
|
||||||
|
|
||||||
val refFieldIndices = declaredFields.mapIndexedNotNull { index, field ->
|
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
|
||||||
val type = getKotlinType(field)
|
val type = field.returnType!!
|
||||||
if (!KotlinBuiltIns.isPrimitiveType(type)) {
|
if (!KotlinBuiltIns.isPrimitiveType(type)) {
|
||||||
index
|
index
|
||||||
} else {
|
} else {
|
||||||
@@ -222,19 +111,19 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme
|
|||||||
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(runtime.targetData, classType, it) }
|
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(runtime.targetData, classType, it) }
|
||||||
val objOffsetsPtr = addGlobalArray("krefs:$className", int32Type, objOffsets.map { Int32(it.toInt()) })
|
val objOffsetsPtr = addGlobalArray("krefs:$className", int32Type, objOffsets.map { Int32(it.toInt()) })
|
||||||
|
|
||||||
// TODO: add fields from supers
|
val fields = classDesc.fields.mapIndexed { index, field ->
|
||||||
val fields = declaredFields.mapIndexed { index, field ->
|
// Note: using FQ name because a class may have multiple fields with the same name due to property overriding
|
||||||
val nameSignature = crc32(field.descriptor.name) // FIXME: add signature
|
val nameSignature = field.fqNameSafe.nameHash // FIXME: add signature
|
||||||
val fieldOffset = LLVMOffsetOfElement(runtime.targetData, classType, index)
|
val fieldOffset = LLVMOffsetOfElement(runtime.targetData, classType, index)
|
||||||
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
||||||
}.sortedBy { it.nameSignature }
|
}.sortedBy { it.nameSignature }
|
||||||
|
|
||||||
val fieldsPtr = addGlobalArray("kfields:$className", runtime.fieldTableRecordType, fields)
|
val fieldsPtr = addGlobalArray("kfields:$className", runtime.fieldTableRecordType, fields)
|
||||||
|
|
||||||
// TODO: add methods from supers
|
val methods = getMethodTableEntries(classDesc).map {
|
||||||
val methods = getDeclaredMethods(declaration).map {
|
val nameSignature = it.name.nameHash // FIXME: add signature
|
||||||
val nameSignature = crc32(it.descriptor.name) // FIXME: add signature
|
// TODO: compile-time resolution limits binary compatibility
|
||||||
val methodEntryPoint = methodEntryPoint(it.descriptor)
|
val methodEntryPoint = it.implementation.entryPointAddress
|
||||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||||
}.sortedBy { it.nameSignature }
|
}.sortedBy { it.nameSignature }
|
||||||
|
|
||||||
@@ -247,7 +136,7 @@ class RTTIGenerator(val module: LLVMOpaqueModule, val runtime: Runtime): IrEleme
|
|||||||
methodsPtr, methods.size,
|
methodsPtr, methods.size,
|
||||||
fieldsPtr, fields.size)
|
fieldsPtr, fields.size)
|
||||||
|
|
||||||
val typeInfoGlobal = typeInfoFor(declaration.descriptor)
|
val typeInfoGlobal = classDesc.llvmTypeInfoPtr.getLlvmValue() // TODO: it is a hack
|
||||||
LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue())
|
LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user