From 61e1d91c0331b6a3b517526a2dca9d3b52575e54 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 19 Oct 2016 11:31:42 +0300 Subject: [PATCH] backend.native: use common/hash to compute hashes for RTTI --- .../kotlin/kotlin_native/interop/Utils.kt | 17 +++--- .../backend/native/llvm/ContextUtils.kt | 52 ++++++++++++++++++- .../kotlin/backend/native/llvm/HashUtils.kt | 24 +++++++++ .../kotlin/backend/native/llvm/LlvmUtils.kt | 11 ++++ .../kotlin/backend/native/llvm/NameUtils.kt | 18 ------- .../backend/native/llvm/RTTIGenerator.kt | 26 ++++------ 6 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.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 db5fb71e7d7..8027e9bc5e6 100644 --- a/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt +++ b/Interop/Runtime/src/main/kotlin/kotlin_native/interop/Utils.kt @@ -2,15 +2,6 @@ package kotlin_native.interop fun malloc(type: NativeRef.TypeWithSize) = type.byPtr(bridge.malloc(type.size)) -inline fun malloc(type: NativeRef.TypeWithSize, action: (T) -> R): R { - val ref = malloc(type) - try { - return action(ref) - } finally { - free(ref) - } -} - fun free(ptr: NativePtr?) { if (ptr != null) { bridge.free(ptr) @@ -29,6 +20,14 @@ fun Placement.allocNativeArrayOf(elemType: NativeRef.Type, va fun mallocNativeArrayOf(elemType: NativeRef.Type, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements) +fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray { + val res = this.alloc(array[elements.size](Int8Box)) + elements.forEachIndexed { i, element -> + res[i].value = element + } + return res +} + fun CString.Companion.fromString(str: String?): CString? { if (str == null) { return null 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 63d2df89378..97dc76328ac 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 @@ -1,11 +1,14 @@ package org.jetbrains.kotlin.backend.native.llvm -import kotlin_native.interop.mallocNativeArrayOf +import kotlin_native.interop.* import llvm.* +import org.jetbrains.kotlin.backend.native.hash.GlobalHash import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny @@ -16,6 +19,12 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny internal interface ContextUtils { val context: Context + val runtime: Runtime + get() = context.runtime + + val llvmTargetData: LLVMOpaqueTargetData? + get() = runtime.targetData + /** * All fields of the class instance. * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. @@ -73,7 +82,7 @@ internal interface ContextUtils { val module = context.llvmModule val globalName = this.typeInfoSymbolName val globalPtr = LLVMGetNamedGlobal(module, globalName) ?: - LLVMAddGlobal(module, context.runtime.typeInfoType, globalName) + LLVMAddGlobal(module, runtime.typeInfoType, globalName) return compileTimeValue(globalPtr) } @@ -114,4 +123,43 @@ internal interface ContextUtils { 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 + assert(size.toLong() == LLVMStoreSizeOfType(llvmTargetData, runtime.globalhHashType)) + + return Struct(runtime.globalhHashType, bits.asCompileTimeValue(size)) + // TODO: implement such transformation more generally using LLVM bitcast + // (seems to require some investigation) + } + + /** + * Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc. + * + * TODO: share this implementation + */ + private fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8) + + val String.localHash: LocalHash + get() = LocalHash(localHash(stringAsBytes(this))) + + val String.globalHash: CompileTimeValue + get() = memScoped { + val hash = globalHash(stringAsBytes(this@globalHash), memScope) + hash.asCompileTimeValue() + } + + val FqName.globalHash: CompileTimeValue + get() = this.toString().globalHash + + val Name.localHash: LocalHash + get() = this.toString().localHash + + val FqName.localHash: LocalHash + get() = this.toString().localHash } \ No newline at end of file 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 new file mode 100644 index 00000000000..e5129d32972 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/HashUtils.kt @@ -0,0 +1,24 @@ +package org.jetbrains.kotlin.backend.native.llvm + +import kotlin_native.interop.* +import org.jetbrains.kotlin.backend.native.hash.* + +internal fun localHash(data: ByteArray): Long { + memScoped { + val hashBox = alloc(Int64Box) + val bytes = allocNativeArrayOf(data) + MakeLocalHash(bytes.ptr, data.size, hashBox) + return hashBox.value + } +} + +internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash { + val res = retValPlacement.alloc(GlobalHash) + memScoped { + val bytes = allocNativeArrayOf(data) + MakeGlobalHash(bytes.ptr, data.size, res) + } + return res +} + +internal class LocalHash(val value: Long) : CompileTimeValue 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/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt index bcc8a661229..1f8e008b4a1 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 @@ -1,5 +1,7 @@ package org.jetbrains.kotlin.backend.native.llvm +import kotlin_native.interop.Int8Box +import kotlin_native.interop.NativeArray import kotlin_native.interop.mallocNativeArrayOf import llvm.* import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -75,3 +77,12 @@ internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType? val paramTypesPtr = mallocNativeArrayOf(LLVMOpaqueType, *paramTypes)[0] // TODO: dispose return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0) } + +/** + * Represents [size] bytes contained in this array as [ConstArray]. + */ +internal fun NativeArray.asCompileTimeValue(size: Int): ConstArray { + return ConstArray(int8Type, (0 .. size-1).map { + Int8(this[it].value) + }) +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt index 6f9f743cbac..754d64790df 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt @@ -2,25 +2,7 @@ package org.jetbrains.kotlin.backend.native.llvm import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import java.util.zip.CRC32 - -private fun crc32(str: String): Long { - val c = CRC32() - c.update(str.toByteArray()) - return c.value -} - -internal val String.nameHash: Long - get() = crc32(this) - -internal val Name.nameHash: Long - get() = this.toString().nameHash - -internal val FqName.nameHash: Long - get() = this.toString().nameHash internal val FunctionDescriptor.symbolName: String get() = "kfun:" + this.fqNameSafe.toString() // FIXME: add signature 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 18e08942248..c7226b5ef54 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 @@ -15,17 +15,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny internal class RTTIGenerator(override val context: Context) : ContextUtils { - val runtime: Runtime - get() = context.runtime + 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) : + Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint) - private inner class FieldTableRecord(val nameSignature: Long, val fieldOffset: Int) : - Struct(runtime.fieldTableRecordType, Int64(nameSignature), Int32(fieldOffset)) - - private inner class MethodTableRecord(val nameSignature: Long, val methodEntryPoint: CompileTimeValue) : - Struct(runtime.methodTableRecordType, Int64(nameSignature), methodEntryPoint) - - private inner class TypeInfo(val name: Long, val size: Int, + private inner class TypeInfo(val name: CompileTimeValue, val size: Int, val superType: CompileTimeValue, val objOffsets: CompileTimeValue, val objOffsetsCount: Int, @@ -39,7 +35,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { Struct( runtime.typeInfoType, - Struct(runtime.globalhHashType, ConstArray(LLVMInt8Type(), Array(20, {i -> Int8(1)}).toList())), + name, Int32(size), superType, @@ -122,7 +118,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val classType = createStructFor(className, classDesc.fields) - val name = className.nameHash + val name = className.globalHash val size = LLVMStoreSizeOfType(runtime.targetData, classType).toInt() @@ -145,10 +141,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { 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 - val nameSignature = field.fqNameSafe.nameHash // FIXME: add signature + val nameSignature = field.fqNameSafe.localHash // FIXME: add signature val fieldOffset = LLVMOffsetOfElement(runtime.targetData, classType, index) FieldTableRecord(nameSignature, fieldOffset.toInt()) - }.sortedBy { it.nameSignature } + }.sortedBy { it.nameSignature.value } val fieldsPtr = addGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields) @@ -157,11 +153,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val vtablePtr = addGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable) val methods = getMethodTableEntries(classDesc).map { - val nameSignature = it.name.nameHash // FIXME: add signature + val nameSignature = it.name.localHash // FIXME: add signature // TODO: compile-time resolution limits binary compatibility val methodEntryPoint = it.implementation.entryPointAddress MethodTableRecord(nameSignature, methodEntryPoint) - }.sortedBy { it.nameSignature } + }.sortedBy { it.nameSignature.value } val methodsPtr = addGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods)