Merge pull request #11 from JetBrains/some-stupid-coding-1

Various fixes and and improvements
This commit is contained in:
SvyatoslavScherbina
2016-10-19 21:59:19 +03:00
committed by GitHub
14 changed files with 227 additions and 74 deletions
@@ -2,15 +2,6 @@ package kotlin_native.interop
fun <T : NativeRef> malloc(type: NativeRef.TypeWithSize<T>) = type.byPtr(bridge.malloc(type.size))
inline fun <T : NativeRef, R> malloc(type: NativeRef.TypeWithSize<T>, 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 <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, va
fun <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements)
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
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
@@ -48,4 +47,29 @@ fun CString.Companion.fromString(str: String?): CString? {
fun NativeArray<Int8Box>.asCString() = CString.fromArray(this)
fun Int8Box.asCString() = CString.fromArray(NativeArray.byRefToFirstElem(this, Int8Box))
fun String.toCString() = CString.fromString(this)
fun String.toCString() = CString.fromString(this)
class MemScope private constructor(private val arena: Arena) : Placement by arena {
val memScope: Placement
get() = this
companion object {
internal inline fun <R> use(block: MemScope.()->R): R {
val memScope = MemScope(Arena())
try {
return memScope.block()
} finally {
memScope.arena.clear()
}
}
}
}
/**
* Runs given [block] providing allocation of memory
* which will be automatically disposed at the end of this scope.
*/
inline fun <R> memScoped(block: MemScope.()->R): R {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
return MemScope.use(block)
}
+4
View File
@@ -76,7 +76,11 @@ build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses'
task run(type: JavaExec) {
main 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath sourceSets.cli_bc.runtimeClasspath
args 'tests/codegen/function/sum.kt'
args project(':runtime').file('src/main/kotlin')
args '-output', "$buildDir/out.bc"
jvmArgs '-ea'
@@ -11,6 +11,10 @@ internal class CodeGenerator(override val context:Context) : ContextUtils {
var currentFunction:FunctionDescriptor? = null
fun function(declaration: IrFunction) {
if (declaration.descriptor.isExternal) {
return
}
index = 0
currentFunction = declaration.descriptor
val fn = LLVMAddFunction(context.llvmModule, declaration.descriptor.symbolName, getLlvmFunctionType(declaration.descriptor))
@@ -62,9 +66,11 @@ internal class CodeGenerator(override val context:Context) : ContextUtils {
fun load(value:LLVMOpaqueValue, varName: String):LLVMOpaqueValue = LLVMBuildLoad(context.llvmBuilder, value, varName)!!
fun store(value:LLVMOpaqueValue, ptr:LLVMOpaqueValue):LLVMOpaqueValue = LLVMBuildStore(context.llvmBuilder, value, ptr)!!
fun call(descriptor: FunctionDescriptor, args: MutableList<LLVMOpaqueValue?>, result: String): LLVMOpaqueValue? {
val rargs = malloc(array[args.size](Ref to LLVMOpaqueValue))
args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i]}
return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), rargs[0], args.size, result)
memScoped {
val rargs = alloc(array[args.size](Ref to LLVMOpaqueValue))
args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i] }
return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), rargs[0], args.size, result)
}
}
}
@@ -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)
}
@@ -92,13 +101,18 @@ internal interface ContextUtils {
/**
* Returns pointer to first element of given array.
*
* Note: this function doesn't depend on the context
*
* @param arrayPtr pointer to array
*/
private fun getPtrToFirstElem(arrayPtr: CompileTimeValue): CompileTimeValue {
val indices = longArrayOf(0, 0).map { LLVMConstInt(LLVMInt32Type(), it, 0) }.toTypedArray()
val indicesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *indices)[0] // TODO: dispose
return compileTimeValue(LLVMBuildGEP(context.llvmBuilder, arrayPtr.getLlvmValue(), indicesNativeArrayPtr, indices.size, ""))
memScoped {
val indicesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *indices)[0]
return compileTimeValue(LLVMConstGEP(arrayPtr.getLlvmValue(), indicesNativeArrayPtr, indices.size))
}
}
/**
@@ -112,4 +126,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
}
@@ -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)
@@ -34,6 +34,12 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
if (declaration.descriptor.kind == ClassKind.ANNOTATION_CLASS) {
// do not generate any RTTI for annotation classes as a workaround for link errors
return
}
generator.generate(declaration.descriptor)
}
@@ -50,6 +56,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
if (declaration.descriptor.kind == ClassKind.ANNOTATION_CLASS) {
// do not generate any code for annotation classes as a workaround for NotImplementedError
return
}
super.visitClass(declaration)
}
override fun visitSetVariable(expression: IrSetVariable) {
val value = evaluateExpression(generator.tmpVariable(), expression.value)
generator.store(value!!, generator.variable(expression.descriptor.name.asString())!!)
@@ -1,6 +1,6 @@
package org.jetbrains.kotlin.backend.native.llvm
import kotlin_native.interop.mallocNativeArrayOf
import kotlin_native.interop.*
import llvm.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.utils.singletonOrEmptyList
@@ -8,9 +8,9 @@ import org.jetbrains.kotlin.utils.singletonOrEmptyList
/**
* Represents the value which can be emitted as bitcode const value
*/
internal abstract class CompileTimeValue {
internal interface CompileTimeValue {
abstract fun getLlvmValue(): LLVMOpaqueValue?
fun getLlvmValue(): LLVMOpaqueValue?
fun getLlvmType(): LLVMOpaqueType? {
return LLVMTypeOf(getLlvmValue())
@@ -18,44 +18,49 @@ internal abstract class CompileTimeValue {
}
internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue() {
internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue {
override fun getLlvmValue(): LLVMOpaqueValue? {
val values = elements.map { it.getLlvmValue() }.toTypedArray()
val valuesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *values)[0] // FIXME: dispose
return LLVMConstArray(elemType, valuesNativeArrayPtr, values.size)
memScoped {
val valuesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *values)[0]
return LLVMConstArray(elemType, valuesNativeArrayPtr, values.size)
}
}
}
internal open class Struct(val type: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue() {
internal open class Struct(val type: LLVMOpaqueType?, val elements: List<CompileTimeValue>) : CompileTimeValue {
constructor(type: LLVMOpaqueType?, vararg elements: CompileTimeValue) : this(type, elements.toList())
override fun getLlvmValue(): LLVMOpaqueValue? {
val values = elements.map { it.getLlvmValue() }.toTypedArray()
val valuesNativeArrayPtr = mallocNativeArrayOf(LLVMOpaqueValue, *values)[0] // FIXME: dispose
return LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size)
memScoped {
val valuesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *values)[0]
return LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size)
}
}
}
internal class Int8(val value: Byte) : CompileTimeValue() {
internal class Int8(val value: Byte) : CompileTimeValue {
override fun getLlvmValue() = LLVMConstInt(LLVMInt8Type(), value.toLong(), 1)
}
internal class Int32(val value: Int) : CompileTimeValue() {
internal class Int32(val value: Int) : CompileTimeValue {
override fun getLlvmValue() = LLVMConstInt(LLVMInt32Type(), value.toLong(), 1)
}
internal class Int64(val value: Long) : CompileTimeValue() {
internal class Int64(val value: Long) : CompileTimeValue {
override fun getLlvmValue() = LLVMConstInt(LLVMInt64Type(), value, 1)
}
internal class Zero(val type: LLVMOpaqueType?) : CompileTimeValue() {
internal class Zero(val type: LLVMOpaqueType?) : CompileTimeValue {
override fun getLlvmValue() = LLVMConstNull(type)
}
internal fun compileTimeValue(value: LLVMOpaqueValue?) = object : CompileTimeValue() {
internal fun compileTimeValue(value: LLVMOpaqueValue?) = object : CompileTimeValue {
override fun getLlvmValue() = value
}
@@ -72,6 +77,18 @@ internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType?
val paramTypes = params.map { getLLVMType(it.type) }.toTypedArray()
if (paramTypes.size == 0) return LLVMFunctionType(returnType, null, 0, 0)
val paramTypesPtr = mallocNativeArrayOf(LLVMOpaqueType, *paramTypes)[0] // TODO: dispose
return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0)
memScoped {
val paramTypesPtr = allocNativeArrayOf(LLVMOpaqueType, *paramTypes)[0]
return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0)
}
}
/**
* Represents [size] bytes contained in this array as [ConstArray].
*/
internal fun NativeArray<Int8Box>.asCompileTimeValue(size: Int): ConstArray {
return ConstArray(int8Type, (0 .. size-1).map {
Int8(this[it].value)
})
}
@@ -3,27 +3,23 @@ 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.constants.StringValue
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
private val symbolNameAnnotation = FqName("kotlin_native.SymbolName")
internal val FunctionDescriptor.symbolName: String
get() = "kfun:" + this.fqNameSafe.toString() // FIXME: add signature
get() {
this.annotations.findAnnotation(symbolNameAnnotation)?.let {
if (this.isExternal) {
val nameValue = it.allValueArguments.values.single() as StringValue
return nameValue.value
} else {
// ignore; TODO: report compile error
}
}
return "kfun:" + this.fqNameSafe.toString() // FIXME: add signature
}
internal val ClassDescriptor.typeInfoSymbolName: String
get() = "ktype:" + this.fqNameSafe.toString()
@@ -1,7 +1,8 @@
package org.jetbrains.kotlin.backend.native.llvm
import kotlin_native.interop.mallocNativeArrayOf
import kotlin_native.interop.allocNativeArrayOf
import kotlin_native.interop.memScoped
import llvm.*
import org.jetbrains.kotlin.backend.native.implementation
import org.jetbrains.kotlin.backend.native.implementedInterfaces
@@ -15,17 +16,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 +36,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,
@@ -63,13 +60,16 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun createStructFor(className: FqName, fields: List<PropertyDescriptor>): LLVMOpaqueType? {
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className)
val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray()
val fieldTypesNativeArrayPtr = if (fieldTypes.size > 0) {
mallocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0] // TODO: dispose
} else {
null
}
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
memScoped {
val fieldTypesNativeArrayPtr = if (fieldTypes.size > 0) {
allocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0]
} else {
null
}
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
}
return classType
}
@@ -122,7 +122,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 +145,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 +157,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)
+2 -2
View File
@@ -15,7 +15,7 @@ RUNTIME=${TOP}/runtime
BACKEND=${TOP}/backend.native
BACKEND_CLASSES=${TOP}/backend.native/build/classes
KOTLIN_DIST=${BACKEND}/kotlin-ir/dist
KOTLIN_NATIVE_CLASSPATH=${BACKEND_CLASSES}/bc_frontend:${BACKEND_CLASSES}/cli_bc:${BACKEND_CLASSES}/compiler:${KOTLIN_DIST}/kotlinc/lib/kotlin-reflect.jar:${KOTLIN_DIST}kotlinc/lib/kotlin-stdlib.jar:${KOTLIN_DIST}/kotlinc/lib/kotlin-compiler.jar:${KOTLIN_DIST}/kotlinc/lib/kotlin-runtime.jar:${TOP}/Interop/Runtime/build/classes/main
KOTLIN_NATIVE_CLASSPATH=${BACKEND_CLASSES}/bc_frontend:${BACKEND_CLASSES}/cli_bc:${BACKEND_CLASSES}/compiler:${BACKEND_CLASSES}/llvmInteropStubs:${BACKEND_CLASSES}/hashInteropStubs:${KOTLIN_DIST}/kotlinc/lib/kotlin-reflect.jar:${KOTLIN_DIST}kotlinc/lib/kotlin-stdlib.jar:${KOTLIN_DIST}/kotlinc/lib/kotlin-compiler.jar:${KOTLIN_DIST}/kotlinc/lib/kotlin-runtime.jar:${TOP}/Interop/Runtime/build/classes/main
define PROTO
@@ -41,5 +41,5 @@ clean:
%.BCkt:%.kt
${JAVA} -cp ${KOTLIN_NATIVE_CLASSPATH} -Djava.library.path=${BACKEND}/build/nativelibs org.jetbrains.kotlin.cli.bc.K2NativeKt -output $@ -runtime ${RUNTIME}/build/runtime.bc $<
${JAVA} -cp ${KOTLIN_NATIVE_CLASSPATH} -Djava.library.path=${BACKEND}/build/nativelibs org.jetbrains.kotlin.cli.bc.K2NativeKt -output $@ -runtime ${RUNTIME}/build/runtime.bc ${RUNTIME}/src/main/kotlin $<
+3
View File
@@ -1,5 +1,8 @@
#ifndef RUNTIME_SHA1_H
#define RUNTIME_SHA1_H
#include <stdint.h>
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
@@ -0,0 +1,11 @@
package kotlin_native
/**
* Forces the compiler to use specified symbol name for the target `external` function.
*
* TODO: changing symbol name breaks the binary compatibility,
* so it should probably be allowed on `internal` and `private` functions only.
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class SymbolName(val name: String)