backend: instantiate string literals statically

also improve other code for static instantiation
This commit is contained in:
Svyatoslav Scherbina
2016-11-07 01:03:53 +07:00
committed by SvyatoslavScherbina
parent fddaea5624
commit 674713f429
12 changed files with 333 additions and 103 deletions
@@ -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> {
@@ -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)
@@ -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")
@@ -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
@@ -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)
@@ -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()
}
@@ -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)
@@ -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())
@@ -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)!!
}
@@ -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
}
}
@@ -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)
}
@@ -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
}