From 674713f429f04e2dea050eb08071d3dcb5f68f8d Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 7 Nov 2016 01:03:53 +0700 Subject: [PATCH] backend: instantiate string literals statically also improve other code for static instantiation --- .../kotlin/kotlin_native/interop/Utils.kt | 5 +- .../kotlin/backend/native/DescriptorUtils.kt | 6 +- .../kotlin/backend/native/llvm/Context.kt | 2 + .../backend/native/llvm/ContextUtils.kt | 101 +++++++--------- .../kotlin/backend/native/llvm/HashUtils.kt | 2 +- .../kotlin/backend/native/llvm/IrToBitcode.kt | 3 +- .../kotlin/backend/native/llvm/LlvmUtils.kt | 61 +++++++--- .../backend/native/llvm/RTTIGenerator.kt | 46 +++++--- .../kotlin/backend/native/llvm/Runtime.kt | 16 ++- .../kotlin/backend/native/llvm/StaticData.kt | 111 ++++++++++++++++++ .../backend/native/llvm/StaticDataUtils.kt | 27 +++++ .../backend/native/llvm/StaticObjects.kt | 56 +++++++++ 12 files changed, 333 insertions(+), 103 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticData.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticDataUtils.kt create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticObjects.kt diff --git a/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt b/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt index 8027e9bc5e6..ae0f1f701fb 100644 --- a/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt +++ b/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt @@ -10,7 +10,7 @@ fun free(ptr: NativePtr?) { fun free(ref: NativeRef?) = free(ref.getNativePtr()) -fun Placement.allocNativeArrayOf(elemType: NativeRef.Type, vararg elements: T?): NativeArray> { +fun Placement.allocNativeArrayOf(elemType: NativeRef.Type, elements: List): NativeArray> { val res = this.alloc(array[elements.size](elemType.ref)) elements.forEachIndexed { i, element -> res[i].value = element @@ -18,6 +18,9 @@ fun Placement.allocNativeArrayOf(elemType: NativeRef.Type, va return res } +fun Placement.allocNativeArrayOf(elemType: NativeRef.Type, vararg elements: T?) = + allocNativeArrayOf(elemType, elements.toList()) + fun mallocNativeArrayOf(elemType: NativeRef.Type, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements) fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt index 16528b966ee..21579da3470 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/DescriptorUtils.kt @@ -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 \ No newline at end of file + get() = this.fqNameSafe.asString() in intrinsicTypes + +internal val ClassDescriptor.isInterface: Boolean + get() = (this.kind == ClassKind.INTERFACE) \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt index 22d48eb8bea..160daba0070 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Context.kt @@ -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") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt index baf347d22fa..9ad3c3c7d98 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/ContextUtils.kt @@ -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 { - 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 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.kt index e5129d32972..fe8ef689589 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.kt @@ -21,4 +21,4 @@ internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash return res } -internal class LocalHash(val value: Long) : CompileTimeValue by Int64(value) \ No newline at end of file +internal class LocalHash(val value: Long) : ConstValue by Int64(value) \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt index 9ea49cf4dd0..b581bd69c2e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt @@ -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).getLlvmValue() IrConstKind.Float -> TODO() IrConstKind.Double -> TODO() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt index 6a5cb75d9c4..67315391e2a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt @@ -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 { +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 { override fun getLlvmValue(): LLVMOpaqueValue? { val values = elements.map { it.getLlvmValue() }.toTypedArray() @@ -32,9 +54,9 @@ internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List) : CompileTimeValue { +internal open class Struct(val type: LLVMOpaqueType?, val elements: List) : 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.asCompileTimeValue(size: Int): ConstArray { - return ConstArray(int8Type, (0 .. size-1).map { - Int8(this[it].value) - }) -} +internal fun NativeArray.getBytes(size: Int) = + (0 .. size-1).map { this[it].value }.toByteArray() internal fun getFunctionType(ptrToFunction: LLVMOpaqueValue?): LLVMOpaqueType { val typeOfPtrToFunction = LLVMTypeOf(ptrToFunction) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt index d9fed8dcbea..132768ffe30 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/RTTIGenerator.kt @@ -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 { + 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 { + 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()) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt index faf748978ae..6dca4a9abcf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt @@ -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)!! } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticData.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticData.kt new file mode 100644 index 00000000000..78e54b8e3f8 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticData.kt @@ -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): Global { + val initializer = ConstArray(elemType, elements) + val global = placeGlobal(name, initializer) + + return global + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticDataUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticDataUtils.kt new file mode 100644 index 00000000000..96d97f0d1f2 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticDataUtils.kt @@ -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): 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) +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticObjects.kt new file mode 100644 index 00000000000..b98fcf311f1 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/StaticObjects.kt @@ -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): 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 +} \ No newline at end of file