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 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))
|
val res = this.alloc(array[elements.size](elemType.ref))
|
||||||
elements.forEachIndexed { i, element ->
|
elements.forEachIndexed { i, element ->
|
||||||
res[i].value = element
|
res[i].value = element
|
||||||
@@ -18,6 +18,9 @@ fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, va
|
|||||||
return res
|
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 <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements)
|
||||||
|
|
||||||
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
|
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
|
||||||
|
|||||||
+4
@@ -1,6 +1,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.native
|
package org.jetbrains.kotlin.backend.native
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
@@ -43,3 +44,6 @@ private val intrinsicTypes = setOf(
|
|||||||
|
|
||||||
internal val ClassDescriptor.isIntrinsic: Boolean
|
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)!!
|
return LLVMAddFunction(llvmModule, name, functionType)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val staticData = StaticData(this)
|
||||||
|
|
||||||
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
|
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
|
||||||
|
|
||||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||||
|
|||||||
+43
-58
@@ -22,9 +22,12 @@ internal interface ContextUtils {
|
|||||||
val runtime: Runtime
|
val runtime: Runtime
|
||||||
get() = context.runtime
|
get() = context.runtime
|
||||||
|
|
||||||
val llvmTargetData: LLVMOpaqueTargetData?
|
val llvmTargetData: LLVMOpaqueTargetData
|
||||||
get() = runtime.targetData
|
get() = runtime.targetData
|
||||||
|
|
||||||
|
val staticData: StaticData
|
||||||
|
get() = context.staticData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All fields of the class instance.
|
* All fields of the class instance.
|
||||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
* 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.
|
* LLVM function generated from the Kotlin function.
|
||||||
* It may be declared as external function prototype.
|
* It may be declared as external function prototype.
|
||||||
*/
|
*/
|
||||||
val FunctionDescriptor.llvmFunction: CompileTimeValue
|
val FunctionDescriptor.llvmFunction: ConstValue
|
||||||
get() {
|
get() {
|
||||||
assert (this.kind.isReal)
|
assert (this.kind.isReal)
|
||||||
val globalName = this.symbolName
|
val globalName = this.symbolName
|
||||||
@@ -61,86 +64,68 @@ internal interface ContextUtils {
|
|||||||
|
|
||||||
val functionType = getLlvmFunctionType(this)
|
val functionType = getLlvmFunctionType(this)
|
||||||
val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType)
|
val function = LLVMGetNamedFunction(module, globalName) ?: LLVMAddFunction(module, globalName, functionType)
|
||||||
return compileTimeValue(function)
|
return constValue(function)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Address of entry point of [llvmFunction].
|
* Address of entry point of [llvmFunction].
|
||||||
*/
|
*/
|
||||||
val FunctionDescriptor.entryPointAddress: CompileTimeValue
|
val FunctionDescriptor.entryPointAddress: ConstValue
|
||||||
get() {
|
get() {
|
||||||
val result = LLVMConstBitCast(this.llvmFunction.getLlvmValue(), pointerType(LLVMInt8Type()))
|
val result = LLVMConstBitCast(this.llvmFunction.getLlvmValue(), pointerType(LLVMInt8Type()))
|
||||||
return compileTimeValue(result)
|
return constValue(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pointer to type info for given class.
|
* Pointer to type info for given class.
|
||||||
* It may be declared as pointer to external variable.
|
* It may be declared as pointer to external variable.
|
||||||
*/
|
*/
|
||||||
val ClassDescriptor.llvmTypeInfoPtr: CompileTimeValue
|
val ClassDescriptor.llvmTypeInfoPtr: ConstPointer
|
||||||
get() {
|
get() {
|
||||||
val module = context.llvmModule
|
val module = context.llvmModule
|
||||||
val globalName = this.typeInfoSymbolName
|
val globalName = this.typeInfoSymbolName
|
||||||
val globalPtr = LLVMGetNamedGlobal(module, globalName) ?:
|
val globalPtr = LLVMGetNamedGlobal(module, globalName) ?:
|
||||||
LLVMAddGlobal(module, runtime.typeInfoType, 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 contents of this [GlobalHash].
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Note: this function doesn't depend on the context
|
* It must be declared identically with [Runtime.globalHashType].
|
||||||
*
|
|
||||||
* @param arrayPtr pointer to array
|
|
||||||
*/
|
*/
|
||||||
private fun getPtrToFirstElem(arrayPtr: CompileTimeValue): CompileTimeValue {
|
fun GlobalHash.getBytes(): ByteArray {
|
||||||
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 {
|
|
||||||
val size = GlobalHash.size
|
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))
|
return this.bits.getBytes(size)
|
||||||
// TODO: implement such transformation more generally using LLVM bitcast
|
|
||||||
// (seems to require some investigation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* 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
|
val String.localHash: LocalHash
|
||||||
get() = LocalHash(localHash(stringAsBytes(this)))
|
get() = LocalHash(localHash(stringAsBytes(this)))
|
||||||
|
|
||||||
val String.globalHash: CompileTimeValue
|
val String.globalHash: ConstValue
|
||||||
get() = memScoped {
|
get() = memScoped {
|
||||||
val hash = globalHash(stringAsBytes(this@globalHash), memScope)
|
val hashBytes = this@globalHash.globalHashBytes
|
||||||
hash.asCompileTimeValue()
|
return Struct(runtime.globalHashType, ConstArray(int8Type, hashBytes.map { Int8(it) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
val FqName.globalHash: CompileTimeValue
|
val FqName.globalHash: ConstValue
|
||||||
get() = this.toString().globalHash
|
get() = this.toString().globalHash
|
||||||
|
|
||||||
val Name.localHash: LocalHash
|
val Name.localHash: LocalHash
|
||||||
|
|||||||
+1
-1
@@ -21,4 +21,4 @@ internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash
|
|||||||
return res
|
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.Short -> return LLVMConstInt(LLVMInt32Type(), (value.value as Short).toLong(), 1)
|
||||||
IrConstKind.Int -> return LLVMConstInt(LLVMInt32Type(), (value.value as Int).toLong(), 1)
|
IrConstKind.Int -> return LLVMConstInt(LLVMInt32Type(), (value.value as Int).toLong(), 1)
|
||||||
IrConstKind.Long -> return LLVMConstInt(LLVMInt64Type(), value.value as Long, 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.Float -> TODO()
|
||||||
IrConstKind.Double -> 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
|
* Represents the value which can be emitted as bitcode const value
|
||||||
*/
|
*/
|
||||||
internal interface CompileTimeValue {
|
internal interface ConstValue {
|
||||||
|
|
||||||
fun getLlvmValue(): LLVMOpaqueValue?
|
fun getLlvmValue(): LLVMOpaqueValue?
|
||||||
|
|
||||||
fun getLlvmType(): LLVMOpaqueType? {
|
fun getLlvmType(): LLVMOpaqueType {
|
||||||
return LLVMTypeOf(getLlvmValue())
|
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? {
|
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
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? {
|
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
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)
|
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)
|
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)
|
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)
|
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
|
override fun getLlvmValue() = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +96,10 @@ internal val int32Type = LLVMInt32Type()
|
|||||||
|
|
||||||
internal fun pointerType(pointeeType: LLVMOpaqueType?) = LLVMPointerType(pointeeType, 0)
|
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? {
|
internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType? {
|
||||||
val returnType = getLLVMType(function.returnType!!)
|
val returnType = getLLVMType(function.returnType!!)
|
||||||
val params = function.dispatchReceiverParameter.singletonOrEmptyList() +
|
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 {
|
internal fun NativeArray<Int8Box>.getBytes(size: Int) =
|
||||||
return ConstArray(int8Type, (0 .. size-1).map {
|
(0 .. size-1).map { this[it].value }.toByteArray()
|
||||||
Int8(this[it].value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getFunctionType(ptrToFunction: LLVMOpaqueValue?): LLVMOpaqueType {
|
internal fun getFunctionType(ptrToFunction: LLVMOpaqueValue?): LLVMOpaqueType {
|
||||||
val typeOfPtrToFunction = LLVMTypeOf(ptrToFunction)
|
val typeOfPtrToFunction = LLVMTypeOf(ptrToFunction)
|
||||||
|
|||||||
+27
-19
@@ -6,6 +6,7 @@ import kotlin_native.interop.memScoped
|
|||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.native.implementation
|
import org.jetbrains.kotlin.backend.native.implementation
|
||||||
import org.jetbrains.kotlin.backend.native.implementedInterfaces
|
import org.jetbrains.kotlin.backend.native.implementedInterfaces
|
||||||
|
import org.jetbrains.kotlin.backend.native.isInterface
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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) :
|
private inner class FieldTableRecord(val nameSignature: LocalHash, val fieldOffset: Int) :
|
||||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
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)
|
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
||||||
|
|
||||||
private inner class TypeInfo(val name: CompileTimeValue, val size: Int,
|
private inner class TypeInfo(val name: ConstValue, val size: Int,
|
||||||
val superType: CompileTimeValue,
|
val superType: ConstValue,
|
||||||
val objOffsets: CompileTimeValue,
|
val objOffsets: ConstValue,
|
||||||
val objOffsetsCount: Int,
|
val objOffsetsCount: Int,
|
||||||
val interfaces: CompileTimeValue,
|
val interfaces: ConstValue,
|
||||||
val interfacesCount: Int,
|
val interfacesCount: Int,
|
||||||
val vtable: CompileTimeValue,
|
val vtable: ConstValue,
|
||||||
val methods: CompileTimeValue,
|
val methods: ConstValue,
|
||||||
val methodsCount: Int,
|
val methodsCount: Int,
|
||||||
val fields: CompileTimeValue,
|
val fields: ConstValue,
|
||||||
val fieldsCount: Int) :
|
val fieldsCount: Int) :
|
||||||
Struct(
|
Struct(
|
||||||
runtime.typeInfoType,
|
runtime.typeInfoType,
|
||||||
@@ -76,6 +77,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
// TODO: optimize
|
// TODO: optimize
|
||||||
private fun getVtableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
private fun getVtableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||||
|
if (classDesc.isInterface) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val superVtableEntries = if (KotlinBuiltIns.isAny(classDesc)) {
|
val superVtableEntries = if (KotlinBuiltIns.isAny(classDesc)) {
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
@@ -102,6 +107,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||||
|
if (classDesc.isInterface) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val contributedDescriptors = classDesc.unsubstitutedMemberScope.getContributedDescriptors()
|
val contributedDescriptors = classDesc.unsubstitutedMemberScope.getContributedDescriptors()
|
||||||
// (includes declarations from supers)
|
// (includes declarations from supers)
|
||||||
|
|
||||||
@@ -149,14 +158,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
val name = className.globalHash
|
val name = className.globalHash
|
||||||
|
|
||||||
val isInterface = classDesc.kind == ClassKind.INTERFACE
|
|
||||||
|
|
||||||
val size = getInstanceSize(classType, className)
|
val size = getInstanceSize(classType, className)
|
||||||
|
|
||||||
val superType = classDesc.getSuperClassOrAny().llvmTypeInfoPtr
|
val superType = classDesc.getSuperClassOrAny().llvmTypeInfoPtr
|
||||||
|
|
||||||
val interfaces = classDesc.implementedInterfaces.map { it.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 refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
|
||||||
val type = field.returnType!!
|
val type = field.returnType!!
|
||||||
@@ -168,7 +176,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
// TODO: reuse offsets obtained for 'fields' below
|
// TODO: reuse offsets obtained for 'fields' below
|
||||||
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(runtime.targetData, classType, it) }
|
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 ->
|
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
|
// 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())
|
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
||||||
}.sortedBy { it.nameSignature.value }
|
}.sortedBy { it.nameSignature.value }
|
||||||
|
|
||||||
val fieldsPtr = if (isInterface) Zero(pointerType(runtime.fieldTableRecordType))
|
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||||
else addGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields)
|
runtime.fieldTableRecordType, fields)
|
||||||
|
|
||||||
// TODO: compile-time resolution limits binary compatibility
|
// TODO: compile-time resolution limits binary compatibility
|
||||||
val vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress }
|
val vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress }
|
||||||
val vtablePtr = if (isInterface) Zero(pointerType(pointerType(int8Type))) else
|
val vtablePtr = staticData.placeGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable)
|
||||||
addGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable)
|
|
||||||
|
|
||||||
val methods = getMethodTableEntries(classDesc).map {
|
val methods = getMethodTableEntries(classDesc).map {
|
||||||
val nameSignature = it.name.localHash // FIXME: add signature
|
val nameSignature = it.name.localHash // FIXME: add signature
|
||||||
@@ -192,8 +200,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||||
}.sortedBy { it.nameSignature.value }
|
}.sortedBy { it.nameSignature.value }
|
||||||
|
|
||||||
val methodsPtr = if (isInterface) Zero(pointerType(runtime.methodTableRecordType)) else
|
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||||
addGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods)
|
runtime.methodTableRecordType, methods)
|
||||||
|
|
||||||
val typeInfo = TypeInfo(name, size,
|
val typeInfo = TypeInfo(name, size,
|
||||||
superType,
|
superType,
|
||||||
@@ -201,7 +209,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
interfacesPtr, interfaces.size,
|
interfacesPtr, interfaces.size,
|
||||||
vtablePtr,
|
vtablePtr,
|
||||||
methodsPtr, methods.size,
|
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
|
val typeInfoGlobal = classDesc.llvmTypeInfoPtr.getLlvmValue() // TODO: it is a hack
|
||||||
LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue())
|
LLVMSetInitializer(typeInfoGlobal, typeInfo.getLlvmValue())
|
||||||
|
|||||||
+11
-5
@@ -30,15 +30,21 @@ class Runtime(private val bitcodeFile: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typeInfoType = LLVMGetTypeByName(llvmModule, "struct.TypeInfo")
|
private fun getStructType(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name")!!
|
||||||
val fieldTableRecordType = LLVMGetTypeByName(llvmModule, "struct.FieldTableRecord")
|
|
||||||
val methodTableRecordType = LLVMGetTypeByName(llvmModule, "struct.MethodTableRecord")
|
val typeInfoType = getStructType("TypeInfo")
|
||||||
val globalhHashType = LLVMGetTypeByName(llvmModule, "struct.GlobalHash")
|
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 target = LLVMGetTarget(llvmModule)!!.asCString().toString()
|
||||||
|
|
||||||
val dataLayout = LLVMGetDataLayout(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