backend: instantiate string literals statically
also improve other code for static instantiation
This commit is contained in:
committed by
SvyatoslavScherbina
parent
fddaea5624
commit
674713f429
@@ -10,7 +10,7 @@ fun free(ptr: NativePtr?) {
|
||||
|
||||
fun free(ref: NativeRef?) = free(ref.getNativePtr())
|
||||
|
||||
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?): NativeArray<RefBox<T>> {
|
||||
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, elements: List<T?>): NativeArray<RefBox<T>> {
|
||||
val res = this.alloc(array[elements.size](elemType.ref))
|
||||
elements.forEachIndexed { i, element ->
|
||||
res[i].value = element
|
||||
@@ -18,6 +18,9 @@ fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, va
|
||||
return res
|
||||
}
|
||||
|
||||
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) =
|
||||
allocNativeArrayOf(elemType, elements.toList())
|
||||
|
||||
fun <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements)
|
||||
|
||||
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
|
||||
|
||||
+5
-1
@@ -1,6 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.native
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
@@ -42,4 +43,7 @@ private val intrinsicTypes = setOf(
|
||||
)
|
||||
|
||||
internal val ClassDescriptor.isIntrinsic: Boolean
|
||||
get() = this.fqNameSafe.asString() in intrinsicTypes
|
||||
get() = this.fqNameSafe.asString() in intrinsicTypes
|
||||
|
||||
internal val ClassDescriptor.isInterface: Boolean
|
||||
get() = (this.kind == ClassKind.INTERFACE)
|
||||
+2
@@ -20,6 +20,8 @@ internal class Context(val irModule: IrModuleFragment, val runtime: Runtime, val
|
||||
return LLVMAddFunction(llvmModule, name, functionType)!!
|
||||
}
|
||||
|
||||
val staticData = StaticData(this)
|
||||
|
||||
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
|
||||
|
||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||
|
||||
+43
-58
@@ -22,9 +22,12 @@ internal interface ContextUtils {
|
||||
val runtime: Runtime
|
||||
get() = context.runtime
|
||||
|
||||
val llvmTargetData: LLVMOpaqueTargetData?
|
||||
val llvmTargetData: LLVMOpaqueTargetData
|
||||
get() = runtime.targetData
|
||||
|
||||
val staticData: StaticData
|
||||
get() = context.staticData
|
||||
|
||||
/**
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
@@ -53,7 +56,7 @@ internal interface ContextUtils {
|
||||
* LLVM function generated from the Kotlin function.
|
||||
* It may be declared as external function prototype.
|
||||
*/
|
||||
val FunctionDescriptor.llvmFunction: CompileTimeValue
|
||||
val FunctionDescriptor.llvmFunction: ConstValue
|
||||
get() {
|
||||
assert (this.kind.isReal)
|
||||
val globalName = this.symbolName
|
||||
@@ -61,86 +64,68 @@ internal interface ContextUtils {
|
||||
|
||||
val functionType = getLlvmFunctionType(this)
|
||||
val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType)
|
||||
return compileTimeValue(function)
|
||||
return constValue(function)
|
||||
}
|
||||
|
||||
/**
|
||||
* Address of entry point of [llvmFunction].
|
||||
*/
|
||||
val FunctionDescriptor.entryPointAddress: CompileTimeValue
|
||||
val FunctionDescriptor.entryPointAddress: ConstValue
|
||||
get() {
|
||||
val result = LLVMConstBitCast(this.llvmFunction.getLlvmValue(), pointerType(LLVMInt8Type()))
|
||||
return compileTimeValue(result)
|
||||
return constValue(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer to type info for given class.
|
||||
* It may be declared as pointer to external variable.
|
||||
*/
|
||||
val ClassDescriptor.llvmTypeInfoPtr: CompileTimeValue
|
||||
val ClassDescriptor.llvmTypeInfoPtr: ConstPointer
|
||||
get() {
|
||||
val module = context.llvmModule
|
||||
val globalName = this.typeInfoSymbolName
|
||||
val globalPtr = LLVMGetNamedGlobal(module, globalName) ?:
|
||||
LLVMAddGlobal(module, runtime.typeInfoType, globalName)
|
||||
|
||||
return compileTimeValue(globalPtr)
|
||||
return constPointer(globalPtr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds global variable with given name and value to [Context.llvmModule] of [context].
|
||||
* Returns pointer to this variable.
|
||||
*/
|
||||
fun addGlobalConst(name: String, value: CompileTimeValue): CompileTimeValue {
|
||||
val global = LLVMAddGlobal(context.llvmModule, value.getLlvmType(), name)
|
||||
LLVMSetInitializer(global, value.getLlvmValue())
|
||||
LLVMSetGlobalConstant(global, 1)
|
||||
return compileTimeValue(global)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pointer to first element of given array.
|
||||
* Returns contents of this [GlobalHash].
|
||||
*
|
||||
* Note: this function doesn't depend on the context
|
||||
*
|
||||
* @param arrayPtr pointer to array
|
||||
* It must be declared identically with [Runtime.globalHashType].
|
||||
*/
|
||||
private fun getPtrToFirstElem(arrayPtr: CompileTimeValue): CompileTimeValue {
|
||||
val indices = longArrayOf(0, 0).map { LLVMConstInt(LLVMInt32Type(), it, 0) }.toTypedArray()
|
||||
|
||||
memScoped {
|
||||
val indicesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *indices)[0]
|
||||
|
||||
return compileTimeValue(LLVMConstGEP(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 addGlobalConstArray(name: String, elemType: LLVMOpaqueType?, elements: List<CompileTimeValue>): CompileTimeValue {
|
||||
return if (elements.size > 0) {
|
||||
getPtrToFirstElem(addGlobalConst(name, ConstArray(elemType, elements)))
|
||||
} else {
|
||||
Zero(pointerType(elemType))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this [GlobalHash] to compile-time value of [runtime]-defined `GlobalHash` type.
|
||||
*
|
||||
* These types must be defined identically.
|
||||
*/
|
||||
private fun GlobalHash.asCompileTimeValue(): Struct {
|
||||
fun GlobalHash.getBytes(): ByteArray {
|
||||
val size = GlobalHash.size
|
||||
assert(size.toLong() == LLVMStoreSizeOfType(llvmTargetData, runtime.globalhHashType))
|
||||
assert(size.toLong() == LLVMStoreSizeOfType(llvmTargetData, runtime.globalHashType))
|
||||
|
||||
return Struct(runtime.globalhHashType, bits.asCompileTimeValue(size))
|
||||
// TODO: implement such transformation more generally using LLVM bitcast
|
||||
// (seems to require some investigation)
|
||||
return this.bits.getBytes(size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns global hash of this string contents.
|
||||
*/
|
||||
val String.globalHashBytes: ByteArray
|
||||
get() = memScoped {
|
||||
val hash = globalHash(stringAsBytes(this@globalHashBytes), memScope)
|
||||
hash.getBytes()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return base64 representation for global hash of this string contents.
|
||||
*/
|
||||
val String.globalHashBase64: String
|
||||
get() {
|
||||
val bytes = this.toByteArray(Charsets.UTF_8)
|
||||
val hashBytes = this.globalHashBytes
|
||||
|
||||
val base64Bytes = java.util.Base64.getEncoder().encode(globalHashBytes)
|
||||
// TODO: do not use JRE for base64
|
||||
// (it is impossible right now due to native interop limitations)
|
||||
|
||||
return String(base64Bytes, Charsets.US_ASCII)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
|
||||
*
|
||||
@@ -151,13 +136,13 @@ internal interface ContextUtils {
|
||||
val String.localHash: LocalHash
|
||||
get() = LocalHash(localHash(stringAsBytes(this)))
|
||||
|
||||
val String.globalHash: CompileTimeValue
|
||||
val String.globalHash: ConstValue
|
||||
get() = memScoped {
|
||||
val hash = globalHash(stringAsBytes(this@globalHash), memScope)
|
||||
hash.asCompileTimeValue()
|
||||
val hashBytes = this@globalHash.globalHashBytes
|
||||
return Struct(runtime.globalHashType, ConstArray(int8Type, hashBytes.map { Int8(it) }))
|
||||
}
|
||||
|
||||
val FqName.globalHash: CompileTimeValue
|
||||
val FqName.globalHash: ConstValue
|
||||
get() = this.toString().globalHash
|
||||
|
||||
val Name.localHash: LocalHash
|
||||
|
||||
+1
-1
@@ -21,4 +21,4 @@ internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash
|
||||
return res
|
||||
}
|
||||
|
||||
internal class LocalHash(val value: Long) : CompileTimeValue by Int64(value)
|
||||
internal class LocalHash(val value: Long) : ConstValue by Int64(value)
|
||||
+2
-1
@@ -279,7 +279,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
IrConstKind.Short -> return LLVMConstInt(LLVMInt32Type(), (value.value as Short).toLong(), 1)
|
||||
IrConstKind.Int -> return LLVMConstInt(LLVMInt32Type(), (value.value as Int).toLong(), 1)
|
||||
IrConstKind.Long -> return LLVMConstInt(LLVMInt64Type(), value.value as Long, 1)
|
||||
IrConstKind.String -> TODO()
|
||||
IrConstKind.String ->
|
||||
return context.staticData.createStringLiteral(value as IrConst<String>).getLlvmValue()
|
||||
IrConstKind.Float -> TODO()
|
||||
IrConstKind.Double -> TODO()
|
||||
}
|
||||
|
||||
+44
-17
@@ -9,17 +9,39 @@ import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
/**
|
||||
* Represents the value which can be emitted as bitcode const value
|
||||
*/
|
||||
internal interface CompileTimeValue {
|
||||
internal interface ConstValue {
|
||||
|
||||
fun getLlvmValue(): LLVMOpaqueValue?
|
||||
|
||||
fun getLlvmType(): LLVMOpaqueType? {
|
||||
return LLVMTypeOf(getLlvmValue())
|
||||
fun getLlvmType(): LLVMOpaqueType {
|
||||
return LLVMTypeOf(getLlvmValue())!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue {
|
||||
internal interface ConstPointer : ConstValue {
|
||||
fun getElementPtr(index: Int): ConstPointer = ConstGetElementPtr(this, index)
|
||||
}
|
||||
|
||||
internal fun constPointer(value: LLVMOpaqueValue?) = object : ConstPointer {
|
||||
override fun getLlvmValue() = value
|
||||
}
|
||||
|
||||
private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer {
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
// TODO: probably it should be computed once when initialized?
|
||||
// TODO: squash multiple GEPs
|
||||
val indices = intArrayOf(0, index).map { Int32(it).getLlvmValue() }
|
||||
memScoped {
|
||||
val indicesArray = allocNativeArrayOf(LLVMOpaqueValue, indices)
|
||||
return LLVMConstInBoundsGEP(pointer.getLlvmValue(), indicesArray[0], indices.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ConstPointer.bitcast(toType: LLVMOpaqueType) = constPointer(LLVMConstBitCast(this.getLlvmValue(), toType))
|
||||
|
||||
internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<ConstValue>) : ConstValue {
|
||||
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
||||
@@ -32,9 +54,9 @@ internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<Comp
|
||||
}
|
||||
}
|
||||
|
||||
internal open class Struct(val type: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue {
|
||||
internal open class Struct(val type: LLVMOpaqueType?, val elements: List<ConstValue>) : ConstValue {
|
||||
|
||||
constructor(type: LLVMOpaqueType?, vararg elements: CompileTimeValue) : this(type, elements.toList())
|
||||
constructor(type: LLVMOpaqueType?, vararg elements: ConstValue) : this(type, elements.toList())
|
||||
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
||||
@@ -45,23 +67,27 @@ internal open class Struct(val type: LLVMOpaqueType?, val elements: List<Compile
|
||||
}
|
||||
}
|
||||
|
||||
internal class Int8(val value: Byte) : CompileTimeValue {
|
||||
internal class Int8(val value: Byte) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstInt(LLVMInt8Type(), value.toLong(), 1)
|
||||
}
|
||||
|
||||
internal class Int32(val value: Int) : CompileTimeValue {
|
||||
internal class Int32(val value: Int) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstInt(LLVMInt32Type(), value.toLong(), 1)
|
||||
}
|
||||
|
||||
internal class Int64(val value: Long) : CompileTimeValue {
|
||||
internal class Int64(val value: Long) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstInt(LLVMInt64Type(), value, 1)
|
||||
}
|
||||
|
||||
internal class Zero(val type: LLVMOpaqueType?) : CompileTimeValue {
|
||||
internal class Zero(val type: LLVMOpaqueType?) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstNull(type)
|
||||
}
|
||||
|
||||
internal fun compileTimeValue(value: LLVMOpaqueValue?) = object : CompileTimeValue {
|
||||
internal class NullPointer(val pointeeType: LLVMOpaqueType?): ConstPointer {
|
||||
override fun getLlvmValue() = LLVMConstNull(pointerType(pointeeType))
|
||||
}
|
||||
|
||||
internal fun constValue(value: LLVMOpaqueValue?) = object : ConstValue {
|
||||
override fun getLlvmValue() = value
|
||||
}
|
||||
|
||||
@@ -70,6 +96,10 @@ internal val int32Type = LLVMInt32Type()
|
||||
|
||||
internal fun pointerType(pointeeType: LLVMOpaqueType?) = LLVMPointerType(pointeeType, 0)
|
||||
|
||||
internal fun structType(vararg types: LLVMOpaqueType?): LLVMOpaqueType = memScoped {
|
||||
LLVMStructType(allocNativeArrayOf(LLVMOpaqueType, *types)[0], types.size, 0)!!
|
||||
}
|
||||
|
||||
internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType? {
|
||||
val returnType = getLLVMType(function.returnType!!)
|
||||
val params = function.dispatchReceiverParameter.singletonOrEmptyList() +
|
||||
@@ -91,13 +121,10 @@ internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType?
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents [size] bytes contained in this array as [ConstArray].
|
||||
* Reads [size] bytes contained in this array.
|
||||
*/
|
||||
internal fun NativeArray<Int8Box>.asCompileTimeValue(size: Int): ConstArray {
|
||||
return ConstArray(int8Type, (0 .. size-1).map {
|
||||
Int8(this[it].value)
|
||||
})
|
||||
}
|
||||
internal fun NativeArray<Int8Box>.getBytes(size: Int) =
|
||||
(0 .. size-1).map { this[it].value }.toByteArray()
|
||||
|
||||
internal fun getFunctionType(ptrToFunction: LLVMOpaqueValue?): LLVMOpaqueType {
|
||||
val typeOfPtrToFunction = LLVMTypeOf(ptrToFunction)
|
||||
|
||||
+27
-19
@@ -6,6 +6,7 @@ import kotlin_native.interop.memScoped
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.native.implementation
|
||||
import org.jetbrains.kotlin.backend.native.implementedInterfaces
|
||||
import org.jetbrains.kotlin.backend.native.isInterface
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -20,19 +21,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
private inner class FieldTableRecord(val nameSignature: LocalHash, val fieldOffset: Int) :
|
||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
||||
|
||||
private inner class MethodTableRecord(val nameSignature: LocalHash, val methodEntryPoint: CompileTimeValue) :
|
||||
private inner class MethodTableRecord(val nameSignature: LocalHash, val methodEntryPoint: ConstValue) :
|
||||
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
||||
|
||||
private inner class TypeInfo(val name: CompileTimeValue, val size: Int,
|
||||
val superType: CompileTimeValue,
|
||||
val objOffsets: CompileTimeValue,
|
||||
private inner class TypeInfo(val name: ConstValue, val size: Int,
|
||||
val superType: ConstValue,
|
||||
val objOffsets: ConstValue,
|
||||
val objOffsetsCount: Int,
|
||||
val interfaces: CompileTimeValue,
|
||||
val interfaces: ConstValue,
|
||||
val interfacesCount: Int,
|
||||
val vtable: CompileTimeValue,
|
||||
val methods: CompileTimeValue,
|
||||
val vtable: ConstValue,
|
||||
val methods: ConstValue,
|
||||
val methodsCount: Int,
|
||||
val fields: CompileTimeValue,
|
||||
val fields: ConstValue,
|
||||
val fieldsCount: Int) :
|
||||
Struct(
|
||||
runtime.typeInfoType,
|
||||
@@ -76,6 +77,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
// TODO: optimize
|
||||
private fun getVtableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||
if (classDesc.isInterface) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val superVtableEntries = if (KotlinBuiltIns.isAny(classDesc)) {
|
||||
emptyList()
|
||||
} else {
|
||||
@@ -102,6 +107,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||
if (classDesc.isInterface) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val contributedDescriptors = classDesc.unsubstitutedMemberScope.getContributedDescriptors()
|
||||
// (includes declarations from supers)
|
||||
|
||||
@@ -149,14 +158,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
val name = className.globalHash
|
||||
|
||||
val isInterface = classDesc.kind == ClassKind.INTERFACE
|
||||
|
||||
val size = getInstanceSize(classType, className)
|
||||
|
||||
val superType = classDesc.getSuperClassOrAny().llvmTypeInfoPtr
|
||||
|
||||
val interfaces = classDesc.implementedInterfaces.map { it.llvmTypeInfoPtr }
|
||||
val interfacesPtr = addGlobalConstArray("kintf:$className", pointerType(runtime.typeInfoType), interfaces)
|
||||
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
||||
pointerType(runtime.typeInfoType), interfaces)
|
||||
|
||||
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
|
||||
val type = field.returnType!!
|
||||
@@ -168,7 +176,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
// TODO: reuse offsets obtained for 'fields' below
|
||||
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(runtime.targetData, classType, it) }
|
||||
val objOffsetsPtr = addGlobalConstArray("krefs:$className", int32Type, objOffsets.map { Int32(it.toInt()) })
|
||||
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
|
||||
objOffsets.map { Int32(it.toInt()) })
|
||||
|
||||
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
|
||||
@@ -177,13 +186,12 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
|
||||
val fieldsPtr = if (isInterface) Zero(pointerType(runtime.fieldTableRecordType))
|
||||
else addGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields)
|
||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||
runtime.fieldTableRecordType, fields)
|
||||
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress }
|
||||
val vtablePtr = if (isInterface) Zero(pointerType(pointerType(int8Type))) else
|
||||
addGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable)
|
||||
val vtablePtr = staticData.placeGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable)
|
||||
|
||||
val methods = getMethodTableEntries(classDesc).map {
|
||||
val nameSignature = it.name.localHash // FIXME: add signature
|
||||
@@ -192,8 +200,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
|
||||
val methodsPtr = if (isInterface) Zero(pointerType(runtime.methodTableRecordType)) else
|
||||
addGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods)
|
||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||
runtime.methodTableRecordType, methods)
|
||||
|
||||
val typeInfo = TypeInfo(name, size,
|
||||
superType,
|
||||
@@ -201,7 +209,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
interfacesPtr, interfaces.size,
|
||||
vtablePtr,
|
||||
methodsPtr, methods.size,
|
||||
fieldsPtr, if (isInterface) -1 else fields.size)
|
||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
|
||||
|
||||
val typeInfoGlobal = classDesc.llvmTypeInfoPtr.getLlvmValue() // TODO: it is a hack
|
||||
LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue())
|
||||
|
||||
+11
-5
@@ -30,15 +30,21 @@ class Runtime(private val bitcodeFile: String) {
|
||||
}
|
||||
}
|
||||
|
||||
val typeInfoType = LLVMGetTypeByName(llvmModule, "struct.TypeInfo")
|
||||
val fieldTableRecordType = LLVMGetTypeByName(llvmModule, "struct.FieldTableRecord")
|
||||
val methodTableRecordType = LLVMGetTypeByName(llvmModule, "struct.MethodTableRecord")
|
||||
val globalhHashType = LLVMGetTypeByName(llvmModule, "struct.GlobalHash")
|
||||
private fun getStructType(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name")!!
|
||||
|
||||
val typeInfoType = getStructType("TypeInfo")
|
||||
val fieldTableRecordType = getStructType("FieldTableRecord")
|
||||
val methodTableRecordType = getStructType("MethodTableRecord")
|
||||
val globalHashType = getStructType("GlobalHash")
|
||||
|
||||
val containerHeaderType = getStructType("ContainerHeader")
|
||||
val objHeaderType = getStructType("ObjHeader")
|
||||
val arrayHeaderType = getStructType("ArrayHeader")
|
||||
|
||||
val target = LLVMGetTarget(llvmModule)!!.asCString().toString()
|
||||
|
||||
val dataLayout = LLVMGetDataLayout(llvmModule)!!.asCString().toString()
|
||||
|
||||
val targetData = LLVMCreateTargetData(dataLayout)
|
||||
val targetData = LLVMCreateTargetData(dataLayout)!!
|
||||
|
||||
}
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package org.jetbrains.kotlin.backend.native.llvm
|
||||
|
||||
import llvm.*
|
||||
|
||||
/**
|
||||
* Provides utilities to create static data.
|
||||
*/
|
||||
internal class StaticData(override val context: Context): ContextUtils {
|
||||
|
||||
/**
|
||||
* Represents the LLVM global variable.
|
||||
*/
|
||||
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMOpaqueValue) {
|
||||
companion object {
|
||||
fun create(staticData: StaticData, type: LLVMOpaqueType, name: String): Global {
|
||||
val module = staticData.context.llvmModule
|
||||
if (LLVMGetNamedGlobal(module, name) != null) {
|
||||
throw IllegalArgumentException("Global '$name' already exists")
|
||||
}
|
||||
|
||||
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
||||
return Global(staticData, llvmGlobal)
|
||||
}
|
||||
}
|
||||
|
||||
fun setInitializer(value: ConstValue) {
|
||||
LLVMSetInitializer(llvmGlobal, value.getLlvmValue())
|
||||
}
|
||||
|
||||
fun setConstant(value: Boolean) {
|
||||
LLVMSetGlobalConstant(llvmGlobal, if (value) 1 else 0)
|
||||
}
|
||||
|
||||
val pointer = Pointer.to(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the pointer to static data.
|
||||
* It can be a pointer to either a global or any its element.
|
||||
*
|
||||
* TODO: this class is probably should be implemented more optimally
|
||||
*/
|
||||
class Pointer private constructor(val global: Global,
|
||||
private val delegate: ConstPointer,
|
||||
val offsetInGlobal: Long) : ConstPointer by delegate {
|
||||
|
||||
companion object {
|
||||
fun to(global: Global) = Pointer(global, constPointer(global.llvmGlobal), 0L)
|
||||
}
|
||||
|
||||
private fun getElementOffset(index: Int): Long {
|
||||
val llvmTargetData = global.staticData.llvmTargetData
|
||||
val type = LLVMGetElementType(delegate.getLlvmType())
|
||||
return when (LLVMGetTypeKind(type)) {
|
||||
LLVMTypeKind.LLVMStructTypeKind -> LLVMOffsetOfElement(llvmTargetData, type, index)
|
||||
LLVMTypeKind.LLVMArrayTypeKind -> LLVMABISizeOfType(llvmTargetData, LLVMGetElementType(type)) * index
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementPtr(index: Int): Pointer {
|
||||
return Pointer(global, delegate.getElementPtr(index), offsetInGlobal + this.getElementOffset(index))
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the distance from other pointer to this.
|
||||
*
|
||||
* @throws UnsupportedOperationException if it is not possible to represent the distance as [Int] value
|
||||
*/
|
||||
fun sub(other: Pointer): Int {
|
||||
if (this.global != other.global) {
|
||||
throw UnsupportedOperationException("pointers must belong to the same global")
|
||||
}
|
||||
|
||||
val res = this.offsetInGlobal - other.offsetInGlobal
|
||||
if (res.toInt().toLong() != res) {
|
||||
throw UnsupportedOperationException("result doesn't fit into Int")
|
||||
}
|
||||
|
||||
return res.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates [Global] with given type and name.
|
||||
*
|
||||
* It is external until explicitly initialized with [Global.setInitializer].
|
||||
*/
|
||||
fun createGlobal(type: LLVMOpaqueType, name: String): Global {
|
||||
return Global.create(this, type, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates [Global] with given name and value.
|
||||
*/
|
||||
fun placeGlobal(name: String, initializer: ConstValue): Global {
|
||||
val global = createGlobal(initializer.getLlvmType(), name)
|
||||
global.setInitializer(initializer)
|
||||
return global
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates array-typed global with given name and value.
|
||||
*/
|
||||
fun placeGlobalArray(name: String, elemType: LLVMOpaqueType?, elements: List<ConstValue>): Global {
|
||||
val initializer = ConstArray(elemType, elements)
|
||||
val global = placeGlobal(name, initializer)
|
||||
|
||||
return global
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.jetbrains.kotlin.backend.native.llvm
|
||||
|
||||
import llvm.*
|
||||
|
||||
|
||||
/**
|
||||
* Creates const array-typed global with given name and value.
|
||||
* Returns pointer to the first element of the array.
|
||||
*
|
||||
* If [elements] is empty, then null pointer is returned.
|
||||
*/
|
||||
internal fun StaticData.placeGlobalConstArray(name: String,
|
||||
elemType: LLVMOpaqueType?,
|
||||
elements: List<ConstValue>): ConstPointer {
|
||||
if (elements.size > 0) {
|
||||
val global = this.placeGlobalArray(name, elemType, elements)
|
||||
global.setConstant(true)
|
||||
return global.pointer.getElementPtr(0)
|
||||
} else {
|
||||
return NullPointer(elemType)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun StaticData.createAlias(name: String, aliasee: ConstPointer): ConstPointer {
|
||||
val alias = LLVMAddAlias(context.llvmModule, aliasee.getLlvmType(), aliasee.getLlvmValue(), name)!!
|
||||
return constPointer(alias)
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.kotlin.backend.native.llvm
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
|
||||
private fun StaticData.objHeader(containerOffsetNegative: Int, typeInfo: ConstPointer): Struct {
|
||||
assert (containerOffsetNegative >= 0)
|
||||
return Struct(runtime.objHeaderType, typeInfo, Int32(containerOffsetNegative))
|
||||
}
|
||||
|
||||
private fun StaticData.arrayHeader(containerOffsetNegative: Int, typeInfo: ConstPointer, length: Int): Struct {
|
||||
assert (length >= 0)
|
||||
val objHeader = objHeader(containerOffsetNegative, typeInfo)
|
||||
return Struct(runtime.arrayHeaderType, objHeader, Int32(length))
|
||||
}
|
||||
|
||||
private fun StaticData.staticContainerHeader(): Struct {
|
||||
val CONTAINER_TAG_NOCOUNT = 1 // FIXME: copy-pasted from runtime
|
||||
return Struct(runtime.containerHeaderType, Int32(CONTAINER_TAG_NOCOUNT))
|
||||
}
|
||||
|
||||
internal fun StaticData.createStringLiteral(value: IrConst<String>): ConstPointer {
|
||||
val base64Str = value.value.globalHashBase64
|
||||
val valueBytes = value.value.toByteArray(Charsets.UTF_8)
|
||||
|
||||
val arrayCount = valueBytes.size
|
||||
val arrayType = LLVMArrayType(int8Type, arrayCount)
|
||||
|
||||
val compositeType = structType(runtime.containerHeaderType, runtime.arrayHeaderType, arrayType)
|
||||
|
||||
// TODO: use C types alignments instead of LLVM ones
|
||||
val global = this.createGlobal(compositeType, "kstrcont:$base64Str")
|
||||
LLVMSetLinkage(global.llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
|
||||
|
||||
val containerHeader = staticContainerHeader()
|
||||
|
||||
val objHeaderPtr = global.pointer.getElementPtr(1)
|
||||
val containerHeaderPtr = global.pointer.getElementPtr(0)
|
||||
val containerOffsetNegative = objHeaderPtr.sub(containerHeaderPtr)
|
||||
|
||||
val stringClass = value.type.constructor.declarationDescriptor as ClassDescriptor
|
||||
assert (stringClass.fqNameSafe.asString() == "kotlin.String")
|
||||
val arrayHeader = arrayHeader(containerOffsetNegative, stringClass.llvmTypeInfoPtr, arrayCount)
|
||||
|
||||
val array = ConstArray(int8Type, valueBytes.map { Int8(it) } )
|
||||
|
||||
global.setInitializer(Struct(compositeType, containerHeader, arrayHeader, array))
|
||||
|
||||
val stringRef = objHeaderPtr.bitcast(getLLVMType(value.type))
|
||||
val res = createAlias("kstr:$base64Str", stringRef)
|
||||
LLVMSetLinkage(res.getLlvmValue(), LLVMLinkage.LLVMWeakAnyLinkage)
|
||||
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user