Implement C stubs generation in compiler

Use it in
* C varargs support (thus dropping libffi from runtime)
* C callbacks support (thus enabling callbacks with structs)
This commit is contained in:
Svyatoslav Scherbina
2019-01-10 13:59:58 +03:00
committed by SvyatoslavScherbina
parent 7f2103a0ee
commit 6ca196399a
40 changed files with 1781 additions and 623 deletions
@@ -27,14 +27,11 @@ private class StructDeclImpl(spelling: String, override val location: Location)
private class StructDefImpl( private class StructDefImpl(
size: Long, align: Int, decl: StructDecl, size: Long, align: Int, decl: StructDecl,
hasNaturalLayout: Boolean override val kind: Kind
) : StructDef( ) : StructDef(
size, align, decl, size, align, decl
hasNaturalLayout = hasNaturalLayout
) { ) {
override val members = mutableListOf<StructMember>()
override val fields = mutableListOf<Field>()
override val bitFields = mutableListOf<BitField>()
} }
private class EnumDefImpl(spelling: String, type: Type, override val location: Location) : EnumDef(spelling, type) { private class EnumDefImpl(spelling: String, type: Type, override val location: Location) : EnumDef(spelling, type) {
@@ -196,39 +193,56 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
private fun createStructDef(structDecl: StructDeclImpl, cursor: CValue<CXCursor>) { private fun createStructDef(structDecl: StructDeclImpl, cursor: CValue<CXCursor>) {
val type = clang_getCursorType(cursor) val type = clang_getCursorType(cursor)
val fields = mutableListOf<StructMember>()
addDeclaredFields(fields, type, type)
val size = clang_Type_getSizeOf(type) val size = clang_Type_getSizeOf(type)
val align = clang_Type_getAlignOf(type).toInt() val align = clang_Type_getAlignOf(type).toInt()
val structDef = StructDefImpl( val structDef = StructDefImpl(
size, align, structDecl, size, align, structDecl,
hasNaturalLayout = structHasNaturalLayout(cursor) when (cursor.kind) {
CXCursorKind.CXCursor_UnionDecl -> StructDef.Kind.UNION
CXCursorKind.CXCursor_StructDecl -> StructDef.Kind.STRUCT
else -> error(cursor.kind)
}
) )
structDecl.def = structDef structDef.members += fields
addDeclaredFields(structDef, type, type) structDecl.def = structDef
} }
private fun addDeclaredFields(structDef: StructDefImpl, structType: CValue<CXType>, containerType: CValue<CXType>) { private fun addDeclaredFields(result: MutableList<StructMember>, structType: CValue<CXType>, containerType: CValue<CXType>) {
getFields(containerType).forEach { fieldCursor -> getFields(containerType).forEach { fieldCursor ->
val name = getCursorSpelling(fieldCursor) val name = getCursorSpelling(fieldCursor)
if (name.isNotEmpty()) { if (name.isNotEmpty()) {
val fieldType = convertCursorType(fieldCursor) val fieldType = convertCursorType(fieldCursor)
val offset = clang_Type_getOffsetOf(structType, name) val offset = clang_Type_getOffsetOf(structType, name)
if (clang_Cursor_isBitField(fieldCursor) == 0) { val member = if (offset < 0) {
val typeAlign = clang_Type_getAlignOf(clang_getCursorType(fieldCursor)) IncompleteField(name, fieldType)
structDef.fields.add(Field(name, fieldType, offset, typeAlign)) } else if (clang_Cursor_isBitField(fieldCursor) == 0) {
val canonicalFieldType = clang_getCanonicalType(clang_getCursorType(fieldCursor))
Field(
name,
fieldType,
offset,
clang_Type_getSizeOf(canonicalFieldType),
clang_Type_getAlignOf(canonicalFieldType)
)
} else { } else {
val size = clang_getFieldDeclBitWidth(fieldCursor) val size = clang_getFieldDeclBitWidth(fieldCursor)
structDef.bitFields.add(BitField(name, fieldType, offset, size)) BitField(name, fieldType, offset, size)
} }
result.add(member)
} else { } else {
// Unnamed field. // Unnamed field.
val fieldType = clang_getCursorType(fieldCursor) val fieldType = clang_getCursorType(fieldCursor)
when (fieldType.kind) { when (fieldType.kind) {
CXTypeKind.CXType_Record -> { CXTypeKind.CXType_Record -> {
// Unnamed struct fields also contribute their fields: // Unnamed struct fields also contribute their fields:
addDeclaredFields(structDef, structType, fieldType) addDeclaredFields(result, structType, fieldType)
} }
else -> { else -> {
// Nothing. // Nothing.
@@ -447,33 +461,6 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
return Typedef(typedefDef) return Typedef(typedefDef)
} }
/**
* Computes [StructDef.hasNaturalLayout] property.
*/
fun structHasNaturalLayout(structDefCursor: CValue<CXCursor>): Boolean {
val defKind = structDefCursor.kind
when (defKind) {
CXCursorKind.CXCursor_UnionDecl -> return false
CXCursorKind.CXCursor_StructDecl -> {
var hasAttributes = false
visitChildren(structDefCursor) { cursor, _ ->
if (clang_isAttribute(cursor.kind) != 0) {
hasAttributes = true
}
CXChildVisitResult.CXChildVisit_Continue
}
return !hasAttributes
}
else -> throw IllegalArgumentException(defKind.toString())
}
}
private fun convertCursorType(cursor: CValue<CXCursor>) = private fun convertCursorType(cursor: CValue<CXCursor>) =
convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor)) convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor))
@@ -85,15 +85,24 @@ interface TypeDeclaration {
val location: Location val location: Location
} }
sealed class StructMember(val name: String, val type: Type) {
abstract val offset: Long?
}
/** /**
* C struct field. * C struct field.
*/ */
class Field(val name: String, val type: Type, val offset: Long, val typeAlign: Long) class Field(name: String, type: Type, override val offset: Long, val typeSize: Long, val typeAlign: Long)
: StructMember(name, type)
val Field.isAligned: Boolean val Field.isAligned: Boolean
get() = offset % (typeAlign * 8) == 0L get() = offset % (typeAlign * 8) == 0L
class BitField(val name: String, val type: Type, val offset: Long, val size: Int) class BitField(name: String, type: Type, override val offset: Long, val size: Int) : StructMember(name, type)
class IncompleteField(name: String, type: Type) : StructMember(name, type) {
override val offset: Long? get() = null
}
/** /**
* C struct declaration. * C struct declaration.
@@ -109,13 +118,17 @@ abstract class StructDecl(val spelling: String) : TypeDeclaration {
* @param hasNaturalLayout must be `false` if the struct has unnatural layout, e.g. it is `packed`. * @param hasNaturalLayout must be `false` if the struct has unnatural layout, e.g. it is `packed`.
* May be `false` even if the struct has natural layout. * May be `false` even if the struct has natural layout.
*/ */
abstract class StructDef(val size: Long, val align: Int, abstract class StructDef(val size: Long, val align: Int, val decl: StructDecl) {
val decl: StructDecl,
val hasNaturalLayout: Boolean) {
abstract val fields: List<Field> enum class Kind {
// TODO: merge two lists to preserve declaration order. STRUCT, UNION
abstract val bitFields: List<BitField> }
abstract val members: List<StructMember>
abstract val kind: Kind
val fields: List<Field> get() = members.filterIsInstance<Field>()
val bitFields: List<BitField> get() = members.filterIsInstance<BitField>()
} }
/** /**
@@ -183,6 +183,23 @@ internal fun getFields(type: CValue<CXType>): List<CValue<CXCursor>> {
return result return result
} }
fun StructDef.fieldsHaveDefaultAlignment(): Boolean {
fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and (alignment - 1).inv()
var offset = 0L
this.members.forEach {
when (it) {
is Field -> {
if (alignUp(offset, it.typeAlign) * 8 != it.offset) return false
offset = it.offset / 8 + it.typeSize
}
is BitField -> return false
}
}
return true
}
internal fun CValue<CXCursor>.isLeaf(): Boolean { internal fun CValue<CXCursor>.isLeaf(): Boolean {
var hasChildren = false var hasChildren = false
@@ -107,7 +107,11 @@ class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
* @param T must not be abstract * @param T must not be abstract
*/ */
inline fun <reified T : CVariable> NativePlacement.alloc(): T = inline fun <reified T : CVariable> NativePlacement.alloc(): T =
alloc(sizeOf<T>(), alignOf<T>()).reinterpret() alloc(typeOf<T>()).reinterpret()
@PublishedApi
internal fun NativePlacement.alloc(type: CVariable.Type): NativePointed =
alloc(type.size, type.align)
/** /**
* Allocates variable of given type and initializes it applying given block. * Allocates variable of given type and initializes it applying given block.
@@ -263,9 +267,12 @@ fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
} }
} }
@PublishedApi internal fun <T : CVariable> CPointed.readValue(type: CVariable.Type): CValue<T> =
readValue(type.size, type.align)
// Note: can't be declared as property due to possible clash with a struct field. // Note: can't be declared as property due to possible clash with a struct field.
// TODO: find better name. // TODO: find better name.
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>(), alignOf<T>()) inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(typeOf<T>())
fun CValue<*>.write(location: NativePtr) { fun CValue<*>.write(location: NativePtr) {
// TODO: probably CValue must be redesigned. // TODO: probably CValue must be redesigned.
@@ -65,89 +65,3 @@ import kotlin.native.internal.ExportForCompiler
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R @TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R @TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
@ExportForCompiler
private fun invokeImplUnitRet(ptr: COpaquePointer, vararg args: Any?): Unit = memScoped {
callWithVarargs(ptr.rawValue, nativeNullPtr, FFI_TYPE_KIND_VOID, args, null, memScope)
}
@ExportForCompiler
private fun invokeImplBooleanRet(ptr: COpaquePointer, vararg args: Any?): Boolean =
invokeImplByteRet(ptr, *args).toBoolean()
@ExportForCompiler
private fun invokeImplByteRet(ptr: COpaquePointer, vararg args: Any?): Byte = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<ByteVar>(ByteVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT8, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplShortRet(ptr: COpaquePointer, vararg args: Any?): Short = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<ShortVar>(ShortVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT16, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplIntRet(ptr: COpaquePointer, vararg args: Any?): Int = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<IntVar>(IntVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT32, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplLongRet(ptr: COpaquePointer, vararg args: Any?): Long = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<LongVar>(LongVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT64, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplUByteRet(ptr: COpaquePointer, vararg args: Any?): UByte = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UByteVar>(UByteVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT8, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplUShortRet(ptr: COpaquePointer, vararg args: Any?): UShort = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UShortVar>(UShortVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT16, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplUIntRet(ptr: COpaquePointer, vararg args: Any?): UInt = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UIntVar>(UIntVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT32, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplULongRet(ptr: COpaquePointer, vararg args: Any?): ULong = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<ULongVar>(ULongVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT64, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplFloatRet(ptr: COpaquePointer, vararg args: Any?): Float = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<FloatVar>(FloatVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_FLOAT, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplDoubleRet(ptr: COpaquePointer, vararg args: Any?): Double = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<DoubleVar>(DoubleVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_DOUBLE, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplPointerRet(ptr: COpaquePointer, vararg args: Any?): COpaquePointer? = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<COpaquePointerVar>(COpaquePointerVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_POINTER, args, null, memScope)
resultBuffer.value
}
@@ -1,204 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlinx.cinterop
import kotlin.native.*
private const val MAX_ARGUMENT_SIZE = 8
typealias FfiTypeKind = Int
// Also declared in Interop.cpp
const val FFI_TYPE_KIND_VOID: FfiTypeKind = 0
const val FFI_TYPE_KIND_SINT8: FfiTypeKind = 1
const val FFI_TYPE_KIND_SINT16: FfiTypeKind = 2
const val FFI_TYPE_KIND_SINT32: FfiTypeKind = 3
const val FFI_TYPE_KIND_SINT64: FfiTypeKind = 4
const val FFI_TYPE_KIND_FLOAT: FfiTypeKind = 5
const val FFI_TYPE_KIND_DOUBLE: FfiTypeKind = 6
const val FFI_TYPE_KIND_POINTER: FfiTypeKind = 7
const val FFI_TYPE_KIND_UINT8: FfiTypeKind = 8
const val FFI_TYPE_KIND_UINT16: FfiTypeKind = 9
const val FFI_TYPE_KIND_UINT32: FfiTypeKind = 10
const val FFI_TYPE_KIND_UINT64: FfiTypeKind = 11
private tailrec fun convertArgument(
argument: Any?, isVariadic: Boolean, location: COpaquePointer,
additionalPlacement: AutofreeScope
): FfiTypeKind = when (argument) { // FIXME: optimize
is CValuesRef<*>? -> {
location.reinterpret<CPointerVar<*>>()[0] = argument?.getPointer(additionalPlacement)
FFI_TYPE_KIND_POINTER
}
is String -> {
location.reinterpret<CPointerVar<*>>()[0] = if (!isVariadic) {
// If it is fixed argument, then it is not C string because it must have been already converted;
// then treat it as NSString.
// TODO: handle fixed NSString arguments in the stub instead.
interpretCPointer<COpaque>(argument.objcPtr())
} else {
// It is passed as variadic argument; no type information available, so treat it as C string.
argument.cstr.getPointer(additionalPlacement)
}
FFI_TYPE_KIND_POINTER
}
is Int -> {
location.reinterpret<IntVar>()[0] = argument
FFI_TYPE_KIND_SINT32
}
is Long -> {
location.reinterpret<LongVar>()[0] = argument
FFI_TYPE_KIND_SINT64
}
is Boolean -> convertArgument(argument.toByte(), isVariadic, location, additionalPlacement)
is Byte -> if (isVariadic) {
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<ByteVar>()[0] = argument
FFI_TYPE_KIND_SINT8
}
is Short -> if (isVariadic) {
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<ShortVar>()[0] = argument
FFI_TYPE_KIND_SINT16
}
is UInt -> {
location.reinterpret<UIntVar>()[0] = argument
FFI_TYPE_KIND_UINT32
}
is ULong -> {
location.reinterpret<ULongVar>()[0] = argument
FFI_TYPE_KIND_UINT64
}
is UByte -> if (isVariadic) {
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<UByteVar>()[0] = argument
FFI_TYPE_KIND_UINT8
}
is UShort -> if (isVariadic) {
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<UShortVar>()[0] = argument
FFI_TYPE_KIND_UINT16
}
is Double -> {
location.reinterpret<DoubleVar>()[0] = argument
FFI_TYPE_KIND_DOUBLE
}
is Float -> if (isVariadic) {
convertArgument(argument.toDouble(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<FloatVar>()[0] = argument
FFI_TYPE_KIND_FLOAT
}
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
is ForeignObjCObject -> {
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.objcPtr())
FFI_TYPE_KIND_POINTER
}
else -> throw Error("unsupported argument: $argument")
}
inline fun <reified T : CVariable> NativePlacement.allocFfiReturnValueBuffer(type: CVariable.Type): T {
var size = type.size
var align = type.align
// libffi requires return value buffer to be no smaller than system register size;
// TODO: system register size is not exactly the same as pointer size.
if (size < pointerSize) {
size = pointerSize.toLong()
}
if (align < pointerSize) {
align = pointerSize
}
return this.alloc(size, align).reinterpret<T>()
}
fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKind: FfiTypeKind,
fixedArguments: Array<out Any?>, variadicArguments: Array<out Any?>?,
argumentsPlacement: AutofreeScope) {
val totalArgumentsNumber = fixedArguments.size + if (variadicArguments == null) 0 else variadicArguments.size
// All supported arguments take at most 8 bytes each:
val argumentsStorage = argumentsPlacement.allocArray<LongVar>(totalArgumentsNumber)
val arguments = argumentsPlacement.allocArray<CPointerVar<*>>(totalArgumentsNumber)
val types = argumentsPlacement.allocArray<COpaquePointerVar>(totalArgumentsNumber)
var index = 0
inline fun addArgument(argument: Any?, isVariadic: Boolean) {
val storage = (argumentsStorage + index)!!
val typeKind = convertArgument(argument, isVariadic = isVariadic,
location = storage, additionalPlacement = argumentsPlacement)
types[index] = typeKind.toLong().toCPointer()
arguments[index] = storage
++index
}
for (argument in fixedArguments) {
addArgument(argument, isVariadic = false)
}
val variadicArgumentsNumber: Int
if (variadicArguments != null) {
for (argument in variadicArguments) {
addArgument(argument, isVariadic = true)
}
variadicArgumentsNumber = variadicArguments.size
} else {
variadicArgumentsNumber = -1
}
assert (index == totalArgumentsNumber)
callFunctionPointer(codePtr, returnValuePtr, returnTypeKind, arguments.rawValue, types.rawValue,
totalArgumentsNumber, variadicArgumentsNumber)
}
@SymbolName("Kotlin_Interop_callFunctionPointer")
private external fun callFunctionPointer(
codePtr: NativePtr,
returnValuePtr: NativePtr,
returnTypeKind: FfiTypeKind,
arguments: NativePtr,
argumentTypeKinds: NativePtr,
totalArgumentsNumber: Int,
variadicArgumentsNumber: Int
)
@@ -0,0 +1,17 @@
package kotlinx.cinterop.internal
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class CStruct(val spelling: String)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
public annotation class CCall(val id: String) {
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.BINARY)
annotation class CString
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.BINARY)
annotation class WCString
}
@@ -342,8 +342,13 @@ private fun Type.isIntegerLikeType(): Boolean = when (this) {
false false
} else { } else {
def.size <= 4 && def.size <= 4 &&
def.bitFields.all { it.type.isIntegerLikeType() } && def.members.all {
def.fields.all { it.offset == 0L && it.type.isIntegerLikeType() } when (it) {
is BitField -> it.type.isIntegerLikeType()
is Field -> it.offset == 0L && it.type.isIntegerLikeType()
is IncompleteField -> false
}
}
} }
} }
is ObjCPointer, is PointerType, CharType, BoolType -> true is ObjCPointer, is PointerType, CharType, BoolType -> true
@@ -0,0 +1,82 @@
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.native.interop.indexer.*
fun tryRenderStructOrUnion(def: StructDef): String? = when (def.kind) {
StructDef.Kind.STRUCT -> tryRenderStruct(def)
StructDef.Kind.UNION -> tryRenderUnion(def)
}
private fun tryRenderStruct(def: StructDef): String? {
val isPackedStruct = def.fields.any { !it.isAligned }
var offset = 0L
return buildString {
append("struct")
if (isPackedStruct) append(" __attribute__((packed))")
append(" { ")
def.members.forEachIndexed { index, it ->
val name = "p$index"
val decl = when (it) {
is Field -> {
val defaultAlignment = if (isPackedStruct) 1L else it.typeAlign
val alignment = guessAlignment(offset, it.offsetBytes, defaultAlignment) ?: return null
offset = it.offsetBytes + it.typeSize
tryRenderVar(it.type, name)
?.plus(if (alignment == defaultAlignment) "" else "__attribute__((aligned($alignment)))")
}
is BitField, // TODO: tryRenderVar(it.type, name)?.plus(" : ${it.size}")
is IncompleteField -> null // e.g. flexible array member.
} ?: return null
append("$decl; ")
}
append("}")
}
}
private fun guessAlignment(offset: Long, paddedOffset: Long, defaultAlignment: Long): Long? =
longArrayOf(defaultAlignment, 1L, 2L, 4L, 8L, 16L, 32L).firstOrNull {
alignUp(offset, it) == paddedOffset
}
private fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and ((alignment - 1).inv())
private fun tryRenderUnion(def: StructDef): String? =
if (def.members.any { it.offset != 0L }) null else buildString {
append("union { ")
def.members.forEachIndexed { index, it ->
val decl = when (it) {
is Field -> tryRenderVar(it.type, "p$index")
is BitField, is IncompleteField -> null
} ?: return null
append("$decl; ")
}
append("}")
}
private fun tryRenderVar(type: Type, name: String): String? = when (type) {
CharType, BoolType -> "char $name"
is IntegerType -> "${type.spelling} $name"
is FloatingType -> "${type.spelling} $name"
is RecordType -> "${tryRenderStructOrUnion(type.decl.def!!)} $name"
is EnumType -> tryRenderVar(type.def.baseType, name)
is PointerType -> "void* $name"
is ConstArrayType -> tryRenderVar(type.elemType, "$name[${type.length}]")
is IncompleteArrayType -> tryRenderVar(type.elemType, "$name[]")
is Typedef -> tryRenderVar(type.def.aliased, name)
is ObjCPointer -> "void* $name"
else -> null
}
private val Field.offsetBytes: Long get() {
require(this.offset % 8 == 0L)
return this.offset / 8
}
@@ -354,8 +354,12 @@ class StubGenerator(
} }
if (platform == KotlinPlatform.JVM) { if (platform == KotlinPlatform.JVM) {
if (def.hasNaturalLayout) { if (def.kind == StructDef.Kind.STRUCT && def.fieldsHaveDefaultAlignment()) {
out("@CNaturalStruct(${def.fields.joinToString { it.name.quoteAsKotlinLiteral() }})") out("@CNaturalStruct(${def.members.joinToString { it.name.quoteAsKotlinLiteral() }})")
}
} else {
tryRenderStructOrUnion(def)?.let {
out("@CStruct".applyToStrings(it))
} }
} }
@@ -368,8 +372,6 @@ class StubGenerator(
for (field in def.fields) { for (field in def.fields) {
try { try {
assert(field.name.isNotEmpty()) assert(field.name.isNotEmpty())
if (field.offset < 0) throw NotImplementedError();
assert(field.offset % 8 == 0L) assert(field.offset % 8 == 0L)
val offset = field.offset / 8 val offset = field.offset / 8
val fieldRefType = mirror(field.type) val fieldRefType = mirror(field.type)
@@ -588,16 +590,13 @@ class StubGenerator(
return stubs return stubs
} }
private fun FunctionDecl.generateAsFfiVarargs(): Boolean = (platform == KotlinPlatform.NATIVE && this.isVararg &&
// Neither takes nor returns structs by value:
!this.returnsRecord() && this.parameters.all { it.type.unwrapTypedefs() !is RecordType })
private fun FunctionDecl.returnsRecord(): Boolean = this.returnType.unwrapTypedefs() is RecordType
private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
private inner class KotlinFunctionStub(val func: FunctionDecl) : KotlinStub, NativeBacked { private inner class KotlinFunctionStub(val func: FunctionDecl) : KotlinStub, NativeBacked {
override fun generate(context: StubGenerationContext): Sequence<String> = override fun generate(context: StubGenerationContext): Sequence<String> =
if (context.nativeBridges.isSupported(this)) { if (isCCall) {
sequenceOf("@CCall".applyToStrings(cCallSymbolName!!), "external $header")
} else if (context.nativeBridges.isSupported(this)) {
block(header, bodyLines) block(header, bodyLines)
} else { } else {
sequenceOf( sequenceOf(
@@ -608,6 +607,8 @@ class StubGenerator(
private val header: String private val header: String
private val bodyLines: List<String> private val bodyLines: List<String>
private val isCCall: Boolean
private val cCallSymbolName: String?
init { init {
// TODO: support dumpShims // TODO: support dumpShims
@@ -627,11 +628,19 @@ class StubGenerator(
val representAsValuesRef = representCFunctionParameterAsValuesRef(parameter.type) val representAsValuesRef = representCFunctionParameterAsValuesRef(parameter.type)
val bridgeArgument = if (representCFunctionParameterAsString(func, parameter.type)) { val bridgeArgument = if (representCFunctionParameterAsString(func, parameter.type)) {
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable()) val annotations = when (platform) {
KotlinPlatform.JVM -> ""
KotlinPlatform.NATIVE -> "@CCall.CString "
}
kotlinParameters.add(annotations + parameterName to KotlinTypes.string.makeNullable())
bodyGenerator.pushMemScoped() bodyGenerator.pushMemScoped()
"$parameterName?.cstr?.getPointer(memScope)" "$parameterName?.cstr?.getPointer(memScope)"
} else if (representCFunctionParameterAsWString(func, parameter.type)) { } else if (representCFunctionParameterAsWString(func, parameter.type)) {
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable()) val annotations = when (platform) {
KotlinPlatform.JVM -> ""
KotlinPlatform.NATIVE -> "@CCall.WCString "
}
kotlinParameters.add(annotations + parameterName to KotlinTypes.string.makeNullable())
bodyGenerator.pushMemScoped() bodyGenerator.pushMemScoped()
"$parameterName?.wcstr?.getPointer(memScope)" "$parameterName?.wcstr?.getPointer(memScope)"
} else if (representAsValuesRef != null) { } else if (representAsValuesRef != null) {
@@ -647,7 +656,7 @@ class StubGenerator(
bridgeArguments.add(TypedKotlinValue(parameter.type, bridgeArgument)) bridgeArguments.add(TypedKotlinValue(parameter.type, bridgeArgument))
} }
if (!func.generateAsFfiVarargs()) { if (!func.isVararg || platform != KotlinPlatform.NATIVE) {
val result = mappingBridgeGenerator.kotlinToNative( val result = mappingBridgeGenerator.kotlinToNative(
bodyGenerator, bodyGenerator,
this, this,
@@ -657,37 +666,19 @@ class StubGenerator(
"${func.name}(${nativeValues.joinToString()})" "${func.name}(${nativeValues.joinToString()})"
} }
bodyGenerator.out("return $result") bodyGenerator.out("return $result")
isCCall = false
cCallSymbolName = null
} else { } else {
val returnTypeKind = getFfiTypeKind(func.returnType)
kotlinParameters.add("vararg variadicArguments" to KotlinTypes.any.makeNullable()) kotlinParameters.add("vararg variadicArguments" to KotlinTypes.any.makeNullable())
bodyGenerator.pushMemScoped() isCCall = true // TODO: don't generate unused body in this case.
cCallSymbolName = "knifunptr_" + pkgName.replace('.', '_') + nextUniqueId()
val resultVar = "kniResult" simpleBridgeGenerator.insertNativeBridge(
val resultPtr = if (!func.returnsVoid()) {
val returnType = mirror(func.returnType).pointedType.render(kotlinFile)
bodyGenerator.out("val $resultVar = allocFfiReturnValueBuffer<$returnType>(typeOf<$returnType>())")
"$resultVar.rawPtr"
} else {
"nativeNullPtr"
}
val fixedArguments = bridgeArguments.joinToString(", ") { it.value }
val functionPtr = simpleBridgeGenerator.kotlinToNative(
this, this,
BridgedType.NATIVE_PTR, emptyList(),
emptyList() listOf("extern const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});",
) { "extern const void* $cCallSymbolName = &${func.name};")
func.name )
}
bodyGenerator.out("callWithVarargs($functionPtr, $resultPtr, $returnTypeKind, " +
"arrayOf($fixedArguments), variadicArguments, memScope)")
if (!func.returnsVoid()) {
bodyGenerator.out("return $resultVar.value")
}
} }
val returnType = if (func.returnsVoid()) { val returnType = if (func.returnsVoid()) {
@@ -705,28 +696,6 @@ class StubGenerator(
} }
} }
private fun getFfiTypeKind(type: Type): String {
val unwrappedType = type.unwrapTypedefs()
return when (unwrappedType) {
is VoidType -> "FFI_TYPE_KIND_VOID"
is PointerType -> "FFI_TYPE_KIND_POINTER"
is IntegerType -> when (unwrappedType.size) {
1 -> "FFI_TYPE_KIND_SINT8"
2 -> "FFI_TYPE_KIND_SINT16"
4 -> "FFI_TYPE_KIND_SINT32"
8 -> "FFI_TYPE_KIND_SINT64"
else -> TODO(unwrappedType.toString())
}
is FloatingType -> when (unwrappedType.size) {
4 -> "FFI_TYPE_KIND_FLOAT"
8 -> "FFI_TYPE_KIND_DOUBLE"
else -> TODO(unwrappedType.toString())
}
is EnumType -> getFfiTypeKind(unwrappedType.def.baseType)
else -> TODO(unwrappedType.toString())
}
}
private fun integerLiteral(type: Type, value: Long): String? { private fun integerLiteral(type: Type, value: Long): String? {
val integerType = type.unwrapTypedefs() as? IntegerType ?: return null val integerType = type.unwrapTypedefs() as? IntegerType ?: return null
return integerLiteral(integerType.size, declarationMapper.isMappedToSigned(integerType), value) return integerLiteral(integerType.size, declarationMapper.isMappedToSigned(integerType), value)
@@ -943,6 +912,7 @@ class StubGenerator(
} }
if (platform == KotlinPlatform.NATIVE) { if (platform == KotlinPlatform.NATIVE) {
out("import kotlin.native.SymbolName") out("import kotlin.native.SymbolName")
out("import kotlinx.cinterop.internal.*")
} }
out("import kotlinx.cinterop.*") out("import kotlinx.cinterop.*")
@@ -0,0 +1,81 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.*
import org.jetbrains.kotlin.konan.target.ClangArgs
class CStubsManager {
fun getUniqueName(prefix: String) = "$prefix${counter++}"
fun addStub(kotlinLocation: CompilerMessageLocation?, lines: List<String>) {
stubs += Stub(kotlinLocation, lines)
}
fun compile(clang: ClangArgs, messageCollector: MessageCollector, verbose: Boolean): File? {
if (stubs.isEmpty()) return null
val cSource = createTempFile("cstubs", ".c").deleteOnExit()
cSource.writeLines(stubs.flatMap { it.lines })
val bitcode = createTempFile("cstubs", ".bc").deleteOnExit()
val cSourcePath = cSource.absolutePath
val clangCommand = clang.clangC(cSourcePath, "-emit-llvm", "-c", "-o", bitcode.absolutePath)
val result = Command(clangCommand).getResult(withErrors = true)
if (result.exitCode != 0) {
reportCompilationErrors(cSourcePath, result, messageCollector, verbose)
}
return bitcode
}
private fun reportCompilationErrors(
cSourcePath: String,
result: Command.Result,
messageCollector: MessageCollector,
verbose: Boolean
): Nothing {
val regex = Regex("${Regex.escape(cSourcePath)}:([0-9]+):[0-9]+: error: .*")
val errorLines = result.outputLines.mapNotNull { line ->
regex.matchEntire(line)?.let { matchResult ->
matchResult.groupValues[1].toInt()
}
}
val lineToStub = ArrayList<Stub>()
stubs.forEach { stub ->
repeat(stub.lines.size) { lineToStub.add(stub) }
}
val cSourceCopyPath = "cstubs.c"
if (verbose) {
File(cSourcePath).copyTo(File(cSourceCopyPath))
}
if (errorLines.isNotEmpty()) {
errorLines.forEach {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Unable to compile C bridge" + if (verbose) " at $cSourceCopyPath:$it" else "",
lineToStub[it - 1].kotlinLocation
)
}
} else {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Unable to compile C bridges",
null
)
}
throw KonanCompilationException()
}
private val stubs = mutableListOf<Stub>()
private class Stub(val kotlinLocation: CompilerMessageLocation?, val lines: List<String>)
private var counter = 0
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import llvm.LLVMLinkModules2 import llvm.LLVMLinkModules2
import llvm.LLVMModuleRef
import llvm.LLVMWriteBitcodeToFile import llvm.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile
@@ -26,6 +27,12 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
val tempFiles = context.config.tempFiles val tempFiles = context.config.tempFiles
val produce = config.get(KonanConfigKeys.PRODUCE) val produce = config.get(KonanConfigKeys.PRODUCE)
phaser.phase(KonanPhase.C_STUBS) {
context.cStubsManager.compile(context.config.clang, context.messageCollector, context.phase!!.verbose)?.let {
parseAndLinkBitcodeFile(llvmModule, it.absolutePath)
}
}
when (produce) { when (produce) {
CompilerOutputKind.STATIC, CompilerOutputKind.STATIC,
CompilerOutputKind.DYNAMIC, CompilerOutputKind.DYNAMIC,
@@ -50,11 +57,7 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
phaser.phase(KonanPhase.BITCODE_LINKER) { phaser.phase(KonanPhase.BITCODE_LINKER) {
for (library in nativeLibraries) { for (library in nativeLibraries) {
val libraryModule = parseBitcodeFile(library) parseAndLinkBitcodeFile(llvmModule, library)
val failed = LLVMLinkModules2(llvmModule, libraryModule)
if (failed != 0) {
throw Error("failed to link $library") // TODO: retrieve error message from LLVM.
}
} }
} }
@@ -99,4 +102,10 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
} }
} }
private fun parseAndLinkBitcodeFile(llvmModule: LLVMModuleRef, path: String) {
val parsedModule = parseBitcodeFile(path)
val failed = LLVMLinkModules2(llvmModule, parsedModule)
if (failed != 0) {
throw Error("failed to link $path") // TODO: retrieve error message from LLVM.
}
}
@@ -314,10 +314,13 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
} }
lateinit var llvm: Llvm lateinit var llvm: Llvm
val llvmImports: LlvmImports = Llvm.ImportsImpl(this)
lateinit var llvmDeclarations: LlvmDeclarations lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String lateinit var bitcodeFileName: String
lateinit var library: KonanLibraryWriter lateinit var library: KonanLibraryWriter
val cStubsManager = CStubsManager()
var phase: KonanPhase? = null var phase: KonanPhase? = null
var depth: Int = 0 var depth: Int = 0
@@ -33,6 +33,18 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
val nativePointed = packageScope.getContributedClass(InteropFqNames.nativePointedName) val nativePointed = packageScope.getContributedClass(InteropFqNames.nativePointedName)
val cValuesRef = this.packageScope.getContributedClass("CValuesRef")
val cValues = this.packageScope.getContributedClass("CValues")
val cValue = this.packageScope.getContributedClass("CValue")
val cValueWrite = this.packageScope.getContributedFunctions("write")
.single { it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == cValue }
val cValueRead = this.packageScope.getContributedFunctions("readValue")
.single { it.valueParameters.size == 1 }
val allocType = this.packageScope.getContributedFunctions("alloc")
.single { it.extensionReceiverParameter != null
&& it.valueParameters.singleOrNull()?.name?.toString() == "type" }
val cPointer = this.packageScope.getContributedClass(InteropFqNames.cPointerName) val cPointer = this.packageScope.getContributedClass(InteropFqNames.cPointerName)
val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single() val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single()
@@ -43,6 +55,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer
} }
val cstr = packageScope.getContributedVariables("cstr").single()
val wcstr = packageScope.getContributedVariables("wcstr").single()
val memScope = packageScope.getContributedClass("MemScope")
val nativePointedRawPtrGetter = val nativePointedRawPtrGetter =
nativePointed.unsubstitutedMemberScope.getContributedVariables("rawPtr").single().getter!! nativePointed.unsubstitutedMemberScope.getContributedVariables("rawPtr").single().getter!!
@@ -61,24 +77,6 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor = private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor =
this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!! this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!
val invokeImpls = mapOf(
builtIns.unit to "invokeImplUnitRet",
builtIns.boolean to "invokeImplBooleanRet",
builtIns.byte to "invokeImplByteRet",
builtIns.short to "invokeImplShortRet",
builtIns.int to "invokeImplIntRet",
builtIns.long to "invokeImplLongRet",
builtIns.getUnsignedClass(UnsignedType.UBYTE) to "invokeImplUByteRet",
builtIns.getUnsignedClass(UnsignedType.USHORT) to "invokeImplUShortRet",
builtIns.getUnsignedClass(UnsignedType.UINT) to "invokeImplUIntRet",
builtIns.getUnsignedClass(UnsignedType.ULONG) to "invokeImplULongRet",
builtIns.float to "invokeImplFloatRet",
builtIns.double to "invokeImplDoubleRet",
cPointer to "invokeImplPointerRet"
).mapValues { (_, name) ->
packageScope.getContributedFunctions(name).single()
}.toMap()
val objCObject = packageScope.getContributedClass("ObjCObject") val objCObject = packageScope.getContributedClass("ObjCObject")
val objCObjectBase = packageScope.getContributedClass("ObjCObjectBase") val objCObjectBase = packageScope.getContributedClass("ObjCObjectBase")
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal
import org.jetbrains.kotlin.backend.konan.ir.KonanIr import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
@@ -19,6 +20,9 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.ir.builders.IrBuilder
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
abstract internal class KonanBackendContext(val config: KonanConfig) : CommonBackendContext { abstract internal class KonanBackendContext(val config: KonanConfig) : CommonBackendContext {
@@ -49,14 +53,23 @@ abstract internal class KonanBackendContext(val config: KonanConfig) : CommonBac
) )
} }
private fun IrElement.getCompilerMessageLocation(containingFile: IrFile): CompilerMessageLocation? {
val sourceRangeInfo = containingFile.fileEntry.getSourceRangeInfo(this.startOffset, this.endOffset)
return CompilerMessageLocation.create(
path = sourceRangeInfo.filePath,
line = sourceRangeInfo.startLineNumber + 1,
column = sourceRangeInfo.startColumnNumber + 1,
lineContent = null // TODO: retrieve the line content.
)
}
} }
internal fun IrElement.getCompilerMessageLocation(containingFile: IrFile): CompilerMessageLocation? =
createCompilerMessageLocation(containingFile, this.startOffset, this.endOffset)
internal fun IrBuilderWithScope.getCompilerMessageLocation(): CompilerMessageLocation? {
val declaration = this.scope.scopeOwnerSymbol.owner as? IrDeclaration ?: return null
val file = declaration.findPackage() as? IrFile ?: return null
return createCompilerMessageLocation(file, startOffset, endOffset)
}
private fun createCompilerMessageLocation(containingFile: IrFile, startOffset: Int, endOffset: Int): CompilerMessageLocation? {
val sourceRangeInfo = containingFile.fileEntry.getSourceRangeInfo(startOffset, endOffset)
return CompilerMessageLocation.create(
path = sourceRangeInfo.filePath,
line = sourceRangeInfo.startLineNumber + 1,
column = sourceRangeInfo.startColumnNumber + 1,
lineContent = null // TODO: retrieve the line content.
)
}
@@ -58,6 +58,7 @@ enum class KonanPhase(val description: String,
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG, enabled = false), // TODO: Requires devirtualization. /* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG, enabled = false), // TODO: Requires devirtualization.
/* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG), // TODO: Requires escape analysis. /* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG), // TODO: Requires escape analysis.
/* ... ... */ CODEGEN("Code Generation"), /* ... ... */ CODEGEN("Code Generation"),
/* ... ... */ C_STUBS("C stubs compilation"),
/* ... ... */ BITCODE_LINKER("Bitcode linking"), /* ... ... */ BITCODE_LINKER("Bitcode linking"),
/* */ LINK_STAGE("Link stage"), /* */ LINK_STAGE("Link stage"),
/* ... */ OBJECT_FILES("Bitcode to object file"), /* ... */ OBJECT_FILES("Bitcode to object file"),
@@ -181,8 +181,7 @@ internal class LinkStage(val context: Context, val phaser: PhaseManager) {
try { try {
File(executable).delete() File(executable).delete()
linker.linkCommands(objectFiles = objectFiles, executable = executable, linker.linkCommands(objectFiles = objectFiles, executable = executable,
libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries) + libraries = linker.linkStaticLibraries(includedBinaries) + context.config.defaultSystemLibraries,
context.config.defaultSystemLibraries,
linkerArgs = entryPointSelector + linkerArgs = entryPointSelector +
asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
BitcodeEmbedding.getLinkerOptions(context.config) + BitcodeEmbedding.getLinkerOptions(context.config) +
@@ -0,0 +1,9 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
object RuntimeNames {
val symbolName = FqName("kotlin.native.SymbolName")
val exportForCppRuntime = FqName("kotlin.native.internal.ExportForCppRuntime")
val cCall = FqName("kotlinx.cinterop.internal.CCall")
}
@@ -0,0 +1,803 @@
package org.jetbrains.kotlin.backend.konan.cgen
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.descriptors.createAnnotation
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrSpreadElement
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal interface KotlinStubs {
val irBuiltIns: IrBuiltIns
val symbols: KonanSymbols
val target: KonanTarget
fun addKotlin(declaration: IrDeclaration)
fun addC(lines: List<String>)
fun getUniqueCName(prefix: String): String
fun reportError(location: IrElement, message: String): Nothing
}
private class KotlinToCCallBuilder(
val irBuilder: IrBuilderWithScope,
cBridgeName: String,
val stubs: KotlinStubs
) {
val symbols: KonanSymbols get() = stubs.symbols
val bridgeCallBuilder = KotlinCallBuilder(irBuilder, symbols)
val bridgeBuilder = KotlinCBridgeBuilder(irBuilder.startOffset, irBuilder.endOffset, cBridgeName, stubs, isKotlinToC = true)
val cBridgeBodyLines = mutableListOf<String>()
val cCallBuilder = CCallBuilder()
val cFunctionBuilder = CFunctionBuilder()
}
private fun KotlinToCCallBuilder.passThroughBridge(argument: IrExpression, kotlinType: IrType, cType: CType): CVariable {
bridgeCallBuilder.arguments += argument
return bridgeBuilder.addParameter(kotlinType, cType).second
}
private fun KotlinToCCallBuilder.addArgument(
argument: IrExpression,
type: IrType,
variadic: Boolean,
parameter: IrValueParameter?
) {
val argumentPassing = mapParameter(type, variadic, parameter, argument)
val cArgument = with(argumentPassing) { passValue(argument) }
cCallBuilder.arguments += cArgument.expression
if (!variadic) cFunctionBuilder.addParameter(cArgument.type)
}
private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrCall) -> IrExpression = { it }): IrExpression =
bridgeCallBuilder.build(
bridgeBuilder.buildKotlinBridge().also {
this.stubs.addKotlin(it)
},
transformCall
)
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean): IrExpression {
require(expression.dispatchReceiver == null)
val cBridgeName = this.getUniqueCName("knbridge")
val callBuilder = KotlinToCCallBuilder(builder, cBridgeName, this)
val cFunctionBuilder = callBuilder.cFunctionBuilder
val callee = expression.symbol.owner
// TODO: consider computing all arguments before converting.
val targetPtrParameter: String?
val targetFunctionName: String
if (isInvoke) {
targetPtrParameter = callBuilder.passThroughBridge(
expression.extensionReceiver!!,
symbols.interopCPointer.typeWithStarProjections,
CTypes.voidPtr
).name
targetFunctionName = "targetPtr"
(0 until expression.valueArgumentsCount).forEach {
callBuilder.addArgument(
expression.getValueArgument(it)!!,
type = expression.getTypeArgument(it)!!,
variadic = false,
parameter = null
)
}
} else {
require(expression.extensionReceiver == null)
targetPtrParameter = null
targetFunctionName = this.getUniqueCName("target")
(0 until expression.valueArgumentsCount).forEach { index ->
val parameter = callee.valueParameters[index]
val argument = expression.getValueArgument(index)
if (parameter.isVararg) {
require(index == expression.valueArgumentsCount - 1)
require(argument is IrVararg?)
argument?.elements.orEmpty().forEach {
when (it) {
is IrExpression -> callBuilder.addArgument(it, it.type, variadic = true, parameter = null)
is IrSpreadElement ->
reportError(it, "spread operator is not supported for variadic C functions")
else -> error(it)
}
}
cFunctionBuilder.variadic = true
} else {
callBuilder.addArgument(argument!!, parameter.type, variadic = false, parameter = parameter)
}
}
}
val cLines = mutableListOf<String>()
val returnValuePassing = if (isInvoke) {
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
mapReturnType(returnType, TypeLocation.FunctionCallResult(expression))
} else {
mapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression))
}
val result = with(returnValuePassing) {
callBuilder.returnValue(callBuilder.cCallBuilder.build(targetFunctionName))
}
val targetFunctionVariable = CVariable(CTypes.pointer(cFunctionBuilder.getType()), targetFunctionName)
if (isInvoke) {
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = ${targetPtrParameter!!};")
} else {
val cCallSymbolName = callee.descriptor.annotations.findAnnotation(cCall)!!.getStringValue("id")
cLines += "extern const $targetFunctionVariable __asm(\"$cCallSymbolName\");" // Exported from cinterop stubs.
}
cLines += "${callBuilder.bridgeBuilder.buildCSignature(cBridgeName)} {"
cLines += callBuilder.cBridgeBodyLines
cLines += "}"
this.addC(cLines)
return result
}
private class CCallbackBuilder(
val stubs: KotlinStubs,
val location: IrElement
) {
val irBuiltIns: IrBuiltIns get() = stubs.irBuiltIns
val symbols: KonanSymbols get() = stubs.symbols
private val cBridgeName = stubs.getUniqueCName("knbridge")
fun buildCBridgeCall(): String = cBridgeCallBuilder.build(cBridgeName)
fun buildCBridge(): String = bridgeBuilder.buildCSignature(cBridgeName)
val bridgeBuilder = KotlinCBridgeBuilder(location.startOffset, location.endOffset, cBridgeName, stubs, isKotlinToC = false)
val kotlinCallBuilder = KotlinCallBuilder(bridgeBuilder.kotlinIrBuilder, symbols)
val kotlinBridgeStatements = mutableListOf<IrStatement>()
val cBridgeCallBuilder = CCallBuilder()
val cBodyLines = mutableListOf<String>()
val cFunctionBuilder = CFunctionBuilder()
}
private fun CCallbackBuilder.passThroughBridge(
cBridgeArgument: String,
cBridgeParameterType: CType,
kotlinBridgeParameterType: IrType
): IrValueParameter {
cBridgeCallBuilder.arguments += cBridgeArgument
return bridgeBuilder.addParameter(kotlinBridgeParameterType, cBridgeParameterType).first
}
private fun CCallbackBuilder.addParameter(it: IrValueParameter) {
val typeLocation = TypeLocation.FunctionPointerParameter(cFunctionBuilder.numberOfParameters, location)
val valuePassing = stubs.mapType(it.type, typeLocation)
val kotlinArgument = with(valuePassing) { receiveValue() }
kotlinCallBuilder.arguments += kotlinArgument
}
private fun CCallbackBuilder.build(function: IrSimpleFunction, signature: IrFunction): String {
val kotlinCall = kotlinCallBuilder.build(function)
with(stubs.mapReturnType(signature.returnType, TypeLocation.FunctionPointerReturnValue(location))) {
returnValue(kotlinCall)
}
val cLines = mutableListOf<String>()
val kotlinBridge = bridgeBuilder.buildKotlinBridge()
kotlinBridge.body = bridgeBuilder.kotlinIrBuilder.irBlockBody {
kotlinBridgeStatements.forEach { +it }
}
stubs.addKotlin(kotlinBridge)
val result = stubs.getUniqueCName("kncfun")
cLines += "${buildCBridge()};"
cLines += "${cFunctionBuilder.buildSignature(result)} {"
cLines += cBodyLines
cLines += "}"
stubs.addC(cLines)
return result
}
private fun KotlinStubs.generateCFunction(
function: IrSimpleFunction,
signature: IrSimpleFunction,
isObjCMethod: Boolean,
location: IrElement
): String {
val callbackBuilder = CCallbackBuilder(this, location)
signature.dispatchReceiverParameter?.let { callbackBuilder.addParameter(it) }
if (isObjCMethod) {
// assert(mapType(signature.dispatchReceiverParameter!!.type) is ObjCReferenceValuePassing)
// Selector is ignored:
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
}
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it) }
signature.valueParameters.forEach {
callbackBuilder.addParameter(it)
}
return callbackBuilder.build(function, signature)
}
internal fun KotlinStubs.generateCFunctionPointer(
function: IrSimpleFunction,
signature: IrSimpleFunction,
isObjCMethod: Boolean,
expression: IrExpression
): IrExpression {
val cFunction = generateCFunction(function, signature, isObjCMethod, expression)
val fakeFunction = createFakeKotlinExternalFunction(signature, cFunction, isObjCMethod)
addKotlin(fakeFunction)
return IrFunctionReferenceImpl(
expression.startOffset,
expression.endOffset,
expression.type,
fakeFunction.symbol,
fakeFunction.descriptor,
0
)
}
private fun KotlinStubs.createFakeKotlinExternalFunction(
signature: IrSimpleFunction,
cFunctionName: String,
isObjCMethod: Boolean
): IrSimpleFunction {
val objCMethodImpAnnotation = if (isObjCMethod) {
val methodInfo = signature.getObjCMethodInfo()!!
createObjCMethodImpAnnotation(methodInfo.selector, methodInfo.encoding, symbols)
} else {
null
}
val bridgeAnnotations = Annotations.create(
listOfNotNull(
createAnnotation(symbols.symbolName.descriptor, "value" to cFunctionName),
objCMethodImpAnnotation
)
)
val bridgeDescriptor = WrappedSimpleFunctionDescriptor(bridgeAnnotations)
val bridge = IrFunctionImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
IrDeclarationOrigin.DEFINED,
IrSimpleFunctionSymbolImpl(bridgeDescriptor),
Name.identifier(cFunctionName),
Visibilities.PRIVATE,
Modality.FINAL,
signature.returnType,
isInline = false,
isExternal = true,
isTailrec = false,
isSuspend = false
)
bridgeDescriptor.bind(bridge)
return bridge
}
private fun createObjCMethodImpAnnotation(selector: String, encoding: String, symbols: KonanSymbols) =
createAnnotation(symbols.objCMethodImp.descriptor, "selector" to selector, "encoding" to encoding)
private val cCall = RuntimeNames.cCall
private fun IrType.isUnsigned(unsignedType: UnsignedType) = this is IrSimpleType && !this.hasQuestionMark &&
(this.classifier.owner as? IrClass)?.classId == unsignedType.classId
private fun IrType.isUByte() = this.isUnsigned(UnsignedType.UBYTE)
private fun IrType.isUShort() = this.isUnsigned(UnsignedType.USHORT)
private fun IrType.isUInt() = this.isUnsigned(UnsignedType.UINT)
private fun IrType.isULong() = this.isUnsigned(UnsignedType.ULONG)
private fun IrType.isCEnumType(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
if (simpleType.hasQuestionMark) return false
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
if (!enumClass.isEnumClass) return false
return enumClass.superTypes
.any { (it.classifierOrNull?.owner as? IrClass)?.fqNameSafe == FqName("kotlinx.cinterop.CEnum") }
}
private fun IrValueParameter.isWCStringParameter() =
this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier("WCString")))
private fun IrValueParameter.isCStringParameter() =
this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier("CString")))
private fun getStructSpelling(kotlinClass: IrClass): String? =
kotlinClass.descriptor.annotations
.findAnnotation(FqName("kotlinx.cinterop.internal.CStruct"))
?.getStringValue("spelling")
private fun getCStructType(kotlinClass: IrClass): CType? =
getStructSpelling(kotlinClass)?.let { CTypes.simple(it) }
private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? {
val cStructType = getCStructType(kotlinClass) ?: return null
val name = getUniqueCName("struct")
addC(listOf("typedef ${cStructType.render(name)};"))
return CTypes.simple(name)
}
// TODO: rework Boolean support.
private fun cBoolType(target: KonanTarget): CType? = when (target.family) {
Family.IOS -> CTypes.C99Bool
else -> CTypes.signedChar
}
private fun KotlinToCCallBuilder.mapParameter(
type: IrType,
variadic: Boolean,
parameter: IrValueParameter?,
argument: IrExpression
): KotlinToCArgumentPassing {
val classifier = type.classifierOrNull
return when {
classifier == symbols.interopCValues || // Note: this should not be accepted, but is required for compatibility
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) ->
CStringArgumentPassing()
classifier == symbols.string && parameter?.isWCStringParameter() == true ->
WCStringArgumentPassing()
else -> stubs.mapType(type, TypeLocation.FunctionArgument(argument))
}
}
private sealed class TypeLocation(val element: IrElement) {
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
class FunctionCallResult(val call: IrCall) : TypeLocation(call)
class FunctionPointerParameter(val index: Int, element: IrElement) : TypeLocation(element)
class FunctionPointerReturnValue(element: IrElement) : TypeLocation(element)
}
private fun KotlinStubs.mapReturnType(type: IrType, location: TypeLocation): ValueReturning =
when {
type.isUnit() -> VoidReturning
else -> mapType(type, location)
}
private fun KotlinStubs.mapType(type: IrType, location: TypeLocation): ValuePassing =
mapType(type, { reportUnsupportedType(it, type, location) })
private fun KotlinStubs.mapType(type: IrType, reportUnsupportedType: (String) -> Nothing): ValuePassing = when {
type.isBoolean() -> BooleanValuePassing(
cBoolType(target) ?: reportUnsupportedType("unavailable on target platform"),
irBuiltIns
)
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
type.isInt() -> TrivialValuePassing(irBuiltIns.intType, CTypes.int)
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
type.isDouble() -> TrivialValuePassing(irBuiltIns.doubleType, CTypes.double)
type.classifierOrNull == symbols.interopCPointer -> TrivialValuePassing(type, CTypes.voidPtr)
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
type.isUInt() -> UnsignedValuePassing(type, CTypes.int, CTypes.unsignedInt)
type.isULong() -> UnsignedValuePassing(type, CTypes.longLong, CTypes.unsignedLongLong)
type.isCEnumType() -> {
val enumClass = type.getClass()!!
val value = enumClass.declarations
.filterIsInstance<IrProperty>()
.single { it.name.asString() == "value" }
CEnumValuePassing(enumClass, value, mapType(value.getter!!.returnType, reportUnsupportedType) as SimpleValuePassing)
}
type.classifierOrNull == symbols.interopCValue -> if (type.isNullable()) {
reportUnsupportedType("must not be nullable")
} else {
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
?: reportUnsupportedType("must be parameterized with concrete class")
StructValuePassing(kotlinClass, getNamedCStructType(kotlinClass)
?: reportUnsupportedType("not a structure or too complex"))
}
type.isFunction() -> reportUnsupportedType("")
isObjCReferenceType(type) -> ObjCReferenceValuePassing(symbols, type)
else -> reportUnsupportedType("doesn't correspond to any C type")
}
private fun KotlinStubs.isObjCReferenceType(type: IrType): Boolean {
if (target.family != Family.OSX && target.family != Family.IOS) return false
if (type.isObjCObjectType()) return true
val descriptor = type.classifierOrNull?.descriptor ?: return false
val builtIns = irBuiltIns.builtIns
return when (descriptor) {
builtIns.string,
builtIns.list, builtIns.mutableList,
builtIns.set, builtIns.mutableSet,
builtIns.map, builtIns.mutableMap -> true
else -> false
}
}
private class CExpression(val expression: String, val type: CType)
private interface KotlinToCArgumentPassing {
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression
}
private interface ValueReturning {
fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression
fun CCallbackBuilder.returnValue(expression: IrExpression)
}
private interface ValuePassing : KotlinToCArgumentPassing, ValueReturning {
fun CCallbackBuilder.receiveValue(): IrExpression
}
private abstract class SimpleValuePassing : ValuePassing {
abstract val kotlinBridgeType: IrType
abstract val cBridgeType: CType
abstract val cType: CType
abstract fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression
abstract fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression
abstract fun bridgedToC(expression: String): String
abstract fun cToBridged(expression: String): String
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
val bridgeArgument = irBuilder.kotlinToBridged(expression)
val cBridgeValue = passThroughBridge(bridgeArgument, kotlinBridgeType, cBridgeType).name
return CExpression(bridgedToC(cBridgeValue), cType)
}
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression {
cFunctionBuilder.setReturnType(cType)
bridgeBuilder.setReturnType(kotlinBridgeType, cBridgeType)
cBridgeBodyLines.add("return ${cToBridged(expression)};")
val kotlinBridgeCall = buildKotlinBridgeCall()
return irBuilder.bridgedToKotlin(kotlinBridgeCall, symbols)
}
override fun CCallbackBuilder.receiveValue(): IrExpression {
val cParameter = cFunctionBuilder.addParameter(cType)
val cBridgeArgument = cToBridged(cParameter.name)
val kotlinParameter = passThroughBridge(cBridgeArgument, cBridgeType, kotlinBridgeType)
return with(bridgeBuilder.kotlinIrBuilder) {
bridgedToKotlin(irGet(kotlinParameter), symbols)
}
}
override fun CCallbackBuilder.returnValue(expression: IrExpression) {
cFunctionBuilder.setReturnType(cType)
bridgeBuilder.setReturnType(kotlinBridgeType, cBridgeType)
kotlinBridgeStatements += with(bridgeBuilder.kotlinIrBuilder) {
irReturn(kotlinToBridged(expression))
}
val cBridgeCall = buildCBridgeCall()
cBodyLines += "return ${bridgedToC(cBridgeCall)};"
}
}
private class TrivialValuePassing(val kotlinType: IrType, override val cType: CType) : SimpleValuePassing() {
override val kotlinBridgeType: IrType
get() = kotlinType
override val cBridgeType: CType
get() = cType
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = expression
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression = expression
override fun bridgedToC(expression: String): String = expression
override fun cToBridged(expression: String): String = expression
}
private class UnsignedValuePassing(val kotlinType: IrType, val cSignedType: CType, override val cType: CType) : SimpleValuePassing() {
override val kotlinBridgeType: IrType
get() = kotlinType
override val cBridgeType: CType
get() = cSignedType
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = expression
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression = expression
override fun bridgedToC(expression: String): String = cType.cast(expression)
override fun cToBridged(expression: String): String = cBridgeType.cast(expression)
}
private class BooleanValuePassing(override val cType: CType, private val irBuiltIns: IrBuiltIns) : SimpleValuePassing() {
override val cBridgeType: CType get() = CTypes.signedChar
override val kotlinBridgeType: IrType get() = irBuiltIns.byteType
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = irIfThenElse(
irBuiltIns.byteType,
condition = expression,
thenPart = IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 1),
elsePart = IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 0)
)
override fun IrBuilderWithScope.bridgedToKotlin(
expression: IrExpression,
symbols: KonanSymbols
): IrExpression = irNot(irCall(symbols.areEqualByValue[PrimitiveBinaryType.BYTE]!!.owner).apply {
putValueArgument(0, expression)
putValueArgument(1, IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 0))
})
override fun bridgedToC(expression: String): String = cType.cast(expression)
override fun cToBridged(expression: String): String = cBridgeType.cast(expression)
}
private class StructValuePassing(private val kotlinClass: IrClass, private val cType: CType) : ValuePassing {
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
val cBridgeValue = passThroughBridge(
cValuesRefToPointer(expression),
symbols.interopCPointer.typeWithStarProjections,
CTypes.pointer(cType)
).name
return CExpression("*$cBridgeValue", cType)
}
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression = with(irBuilder) {
cFunctionBuilder.setReturnType(cType)
bridgeBuilder.setReturnType(context.irBuiltIns.unitType, CTypes.void)
val kotlinPointed = scope.createTemporaryVariable(irCall(symbols.interopAllocType.owner).apply {
extensionReceiver = bridgeCallBuilder.getMemScope()
putValueArgument(0, getTypeObject())
})
bridgeCallBuilder.prepare += kotlinPointed
val cPointer = passThroughBridge(irGet(kotlinPointed), kotlinPointedType, CTypes.pointer(cType))
cBridgeBodyLines += "*${cPointer.name} = $expression;"
buildKotlinBridgeCall {
irBlock {
at(it)
+it
+readCValue(irGet(kotlinPointed), symbols)
}
}
}
override fun CCallbackBuilder.receiveValue(): IrExpression = with(bridgeBuilder.kotlinIrBuilder) {
val cParameter = cFunctionBuilder.addParameter(cType)
val kotlinPointed = passThroughBridge("&${cParameter.name}", CTypes.voidPtr, kotlinPointedType)
readCValue(irGet(kotlinPointed), symbols)
}
private fun IrBuilderWithScope.readCValue(kotlinPointed: IrExpression, symbols: KonanSymbols): IrExpression =
irCall(symbols.interopCValueRead.owner).apply {
extensionReceiver = kotlinPointed
putValueArgument(0, getTypeObject())
}
override fun CCallbackBuilder.returnValue(expression: IrExpression) = with(bridgeBuilder.kotlinIrBuilder) {
bridgeBuilder.setReturnType(irBuiltIns.unitType, CTypes.void)
cFunctionBuilder.setReturnType(cType)
val result = "callbackResult"
val cReturnValue = CVariable(cType, result)
cBodyLines += "$cReturnValue;"
val kotlinPtr = passThroughBridge("&$result", CTypes.voidPtr, symbols.nativePtrType)
kotlinBridgeStatements += irCall(symbols.interopCValueWrite.owner).apply {
extensionReceiver = expression
putValueArgument(0, irGet(kotlinPtr))
}
val cBridgeCall = buildCBridgeCall()
cBodyLines += "$cBridgeCall;"
cBodyLines += "return $result;"
}
private val kotlinPointedType: IrType get() = kotlinClass.defaultType
private fun IrBuilderWithScope.getTypeObject() =
irGetObject(
kotlinClass.declarations.filterIsInstance<IrClass>()
.single { it.isCompanion }.symbol
)
}
private class CEnumValuePassing(
val enumClass: IrClass,
val value: IrProperty,
val baseValuePassing: SimpleValuePassing
) : SimpleValuePassing() {
override val kotlinBridgeType: IrType
get() = baseValuePassing.kotlinBridgeType
override val cBridgeType: CType
get() = baseValuePassing.cBridgeType
override val cType: CType
get() = baseValuePassing.cType
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression {
val value = irCall(value.getter!!).apply {
dispatchReceiver = expression
}
return with(baseValuePassing) { kotlinToBridged(value) }
}
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression {
val companionClass = enumClass.declarations.filterIsInstance<IrClass>().single { it.isCompanion }
val byValue = companionClass.simpleFunctions().single { it.name.asString() == "byValue" }
return irCall(byValue).apply {
dispatchReceiver = irGetObject(companionClass.symbol)
putValueArgument(0, expression)
}
}
override fun bridgedToC(expression: String): String = with(baseValuePassing) { bridgedToC(expression) }
override fun cToBridged(expression: String): String = with(baseValuePassing) { cToBridged(expression) }
}
private class ObjCReferenceValuePassing(private val symbols: KonanSymbols, private val type: IrType) : SimpleValuePassing() {
override val kotlinBridgeType: IrType
get() = symbols.nativePtrType
override val cBridgeType: CType
get() = CTypes.voidPtr
override val cType: CType
get() = CTypes.voidPtr
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression =
irCall(symbols.interopObjCObjectRawValueGetter.owner).apply {
extensionReceiver = expression
}
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression =
irCall(symbols.interopInterpretObjCPointerOrNull, listOf(type)).apply {
putValueArgument(0, expression)
}
override fun bridgedToC(expression: String): String = expression
override fun cToBridged(expression: String): String = expression
}
private class WCStringArgumentPassing : KotlinToCArgumentPassing {
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
val wcstr = irBuilder.irCall(symbols.interopWcstr.owner).apply {
extensionReceiver = expression
}
return with(CValuesRefArgumentPassing) { passValue(wcstr) }
}
}
private class CStringArgumentPassing : KotlinToCArgumentPassing {
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
val cstr = irBuilder.irCall(symbols.interopCstr.owner).apply {
extensionReceiver = expression
}
return with(CValuesRefArgumentPassing) { passValue(cstr) }
}
}
private object CValuesRefArgumentPassing : KotlinToCArgumentPassing {
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
val bridgeArgument = cValuesRefToPointer(expression)
val cBridgeValue = passThroughBridge(
bridgeArgument,
symbols.interopCPointer.typeWithStarProjections.makeNullable(),
CTypes.voidPtr
)
return CExpression(cBridgeValue.name, cBridgeValue.type)
}
}
private fun KotlinToCCallBuilder.cValuesRefToPointer(
value: IrExpression
): IrExpression = if (value.type.classifierOrNull == symbols.interopCPointer) {
value // Optimization
} else with(irBuilder) {
val getPointerFunction = symbols.interopCValuesRef.owner
.simpleFunctions()
.single { it.name.asString() == "getPointer" }
fun getPointer(expression: IrExpression) = irCall(getPointerFunction).apply {
dispatchReceiver = expression
putValueArgument(0, bridgeCallBuilder.getMemScope())
}
if (!value.type.isNullable()) {
getPointer(value) // Optimization
} else irLetS(value) { valueVarSymbol ->
val valueVar = valueVarSymbol.owner
irIfThenElse(
type = symbols.interopCPointer.typeWithStarProjections.makeNullable(),
condition = irEqeqeq(irGet(valueVar), irNull()),
thenPart = irNull(),
elsePart = getPointer(irGet(valueVar))
)
}
}
private object VoidReturning : ValueReturning {
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression {
bridgeBuilder.setReturnType(irBuilder.context.irBuiltIns.unitType, CTypes.void)
cFunctionBuilder.setReturnType(CTypes.void)
cBridgeBodyLines += "$expression;"
return buildKotlinBridgeCall()
}
override fun CCallbackBuilder.returnValue(expression: IrExpression) {
bridgeBuilder.setReturnType(irBuiltIns.unitType, CTypes.void)
cFunctionBuilder.setReturnType(CTypes.void)
kotlinBridgeStatements += bridgeBuilder.kotlinIrBuilder.irReturn(expression)
cBodyLines += "${buildCBridgeCall()};"
}
}
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
val typeLocation: String = when (location) {
is TypeLocation.FunctionArgument -> ""
is TypeLocation.FunctionCallResult -> " of return value"
is TypeLocation.FunctionPointerParameter -> " of callback parameter ${location.index + 1}"
is TypeLocation.FunctionPointerReturnValue -> " of callback return value"
}
reportError(location.element, "type ${type.toKotlinType()}$typeLocation is not supported here" +
if (reason.isNotEmpty()) ": $reason" else "")
}
@@ -0,0 +1,234 @@
package org.jetbrains.kotlin.backend.konan.cgen
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.konan.descriptors.createAnnotation
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
internal class CFunctionBuilder {
private val parameters = mutableListOf<CVariable>()
private lateinit var returnType: CType
var variadic: Boolean = false
fun setReturnType(type: CType) {
require(!::returnType.isInitialized)
returnType = type
}
fun addParameter(type: CType): CVariable {
val result = CVariable(type, "p${counter++}")
parameters += result
return result
}
val numberOfParameters: Int get() = parameters.size
private var counter = 1
fun getType(): CType = CTypes.function(returnType, parameters.map { it.type }, variadic)
fun buildSignature(name: String): String = returnType.render(buildString {
append(name)
append('(')
parameters.joinTo(this)
if (parameters.isEmpty()) {
if (!variadic) append("void")
} else {
if (variadic) append(", ...")
}
append(')')
})
}
internal class KotlinBridgeBuilder(
startOffset: Int,
endOffset: Int,
cName: String,
stubs: KotlinStubs,
isExternal: Boolean
) {
private var counter = 0
private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs.symbols, isExternal)
val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset)
fun addParameter(type: IrType): IrValueParameter {
val index = counter++
val descriptor = WrappedValueParameterDescriptor()
return IrValueParameterImpl(
bridge.startOffset, bridge.endOffset, bridge.origin,
IrValueParameterSymbolImpl(descriptor),
Name.identifier("p$index"), index, type,
null, false, false
).apply {
descriptor.bind(this)
parent = bridge
bridge.valueParameters += this
}
}
fun setReturnType(type: IrType) {
bridge.returnType = type
}
fun build(): IrFunction = bridge
}
private fun createKotlinBridge(
startOffset: Int,
endOffset: Int,
cBridgeName: String,
symbols: KonanSymbols,
isExternal: Boolean
): IrFunctionImpl {
val bridgeAnnotations = Annotations.create(
listOf(
if (isExternal) {
createAnnotation(symbols.symbolName.descriptor, "value" to cBridgeName)
} else {
createAnnotation(symbols.exportForCppRuntime.descriptor, "name" to cBridgeName)
}
)
)
val bridgeDescriptor = WrappedSimpleFunctionDescriptor(bridgeAnnotations)
val bridge = IrFunctionImpl(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
IrSimpleFunctionSymbolImpl(bridgeDescriptor),
Name.identifier(cBridgeName),
Visibilities.PRIVATE,
Modality.FINAL,
IrUninitializedType,
isInline = false,
isExternal = isExternal,
isTailrec = false,
isSuspend = false
)
bridgeDescriptor.bind(bridge)
return bridge
}
internal class KotlinCBridgeBuilder(
startOffset: Int,
endOffset: Int,
cName: String,
stubs: KotlinStubs,
isKotlinToC: Boolean
) {
private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC)
private val cBridgeBuilder = CFunctionBuilder()
val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder
fun addParameter(kotlinType: IrType, cType: CType): Pair<IrValueParameter, CVariable> {
return kotlinBridgeBuilder.addParameter(kotlinType) to cBridgeBuilder.addParameter(cType)
}
fun setReturnType(kotlinReturnType: IrType, cReturnType: CType) {
kotlinBridgeBuilder.setReturnType(kotlinReturnType)
cBridgeBuilder.setReturnType(cReturnType)
}
fun buildCSignature(name: String): String = cBridgeBuilder.buildSignature(name)
fun buildKotlinBridge() = kotlinBridgeBuilder.build()
}
internal class KotlinCallBuilder(private val irBuilder: IrBuilderWithScope, private val symbols: KonanSymbols) {
val prepare = mutableListOf<IrStatement>()
val arguments = mutableListOf<IrExpression>()
val cleanup = mutableListOf<IrStatement>()
private var memScope: IrVariable? = null
fun getMemScope(): IrExpression = with(irBuilder) {
memScope?.let { return irGet(it) }
val newMemScope = scope.createTemporaryVariable(irCall(symbols.interopMemScope.owner.constructors.single()))
memScope = newMemScope
prepare += newMemScope
val clearImpl = symbols.interopMemScope.owner.simpleFunctions().single { it.name.asString() == "clearImpl" }
cleanup += irCall(clearImpl).apply {
dispatchReceiver = irGet(memScope!!)
}
irGet(newMemScope)
}
fun build(
function: IrFunction,
transformCall: (IrCall) -> IrExpression = { it }
): IrExpression {
val arguments = this.arguments.toMutableList()
val kotlinCall = irBuilder.irCall(function).run {
if (function.dispatchReceiverParameter != null) {
dispatchReceiver = arguments.removeAt(0)
}
if (function.extensionReceiverParameter != null) {
extensionReceiver = arguments.removeAt(0)
}
assert(arguments.size == function.valueParameters.size)
arguments.forEachIndexed { index, it -> putValueArgument(index, it) }
transformCall(this)
}
return if (prepare.isEmpty() && cleanup.isEmpty()) {
kotlinCall
} else {
irBuilder.irBlock(kotlinCall) {
prepare.forEach { +it }
if (cleanup.isEmpty()) {
+kotlinCall
} else {
// Note: generating try-catch as finally blocks are already lowered.
+IrTryImpl(startOffset, endOffset, kotlinCall.type).apply {
tryResult = kotlinCall
catches += irCatch(context.irBuiltIns.throwableType).apply {
result = irBlock(kotlinCall) {
cleanup.forEach { +it }
+irThrow(irGet(catchParameter))
}
}
}
}
}
}
}
}
internal class CCallBuilder {
val arguments = mutableListOf<String>()
fun build(function: String) = buildString {
append(function)
append('(')
arguments.joinTo(this)
append(')')
}
}
@@ -0,0 +1,58 @@
package org.jetbrains.kotlin.backend.konan.cgen
internal interface CType {
fun render(name: String): String
}
internal class CVariable(val type: CType, val name: String) {
override fun toString() = type.render(name)
}
internal object CTypes {
fun simple(type: String): CType = SimpleCType(type)
fun pointer(pointee: CType): CType = PointerCType(pointee)
fun function(returnType: CType, parameterTypes: List<CType>, variadic: Boolean): CType =
FunctionCType(returnType, parameterTypes, variadic)
val void = simple("void")
val voidPtr = pointer(void)
val signedChar = simple("signed char")
val unsignedChar = simple("unsigned char")
val short = simple("short")
val unsignedShort = simple("unsigned short")
val int = simple("int")
val unsignedInt = simple("unsigned int")
val longLong = simple("long long")
val unsignedLongLong = simple("unsigned long long")
val float = simple("float")
val double = simple("double")
val C99Bool = simple("_Bool")
val char = simple("char")
}
private class SimpleCType(private val type: String) : CType {
override fun render(name: String): String = if (name.isEmpty()) type else "$type $name"
}
private class PointerCType(private val pointee: CType) : CType {
override fun render(name: String): String = pointee.render("*$name")
}
private class FunctionCType(
private val returnType: CType,
private val parameterTypes: List<CType>,
private val variadic: Boolean
) : CType {
override fun render(name: String): String = returnType.render(buildString {
append("(")
append(name)
append(")(")
parameterTypes.joinTo(this) { it.render("") }
if (parameterTypes.isEmpty()) {
if (!variadic) append("void")
} else {
if (variadic) append(", ...")
}
append(')')
})
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.konan.descriptors package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference
import org.jetbrains.kotlin.backend.konan.isObjCClass import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.backend.konan.serialization.isExported import org.jetbrains.kotlin.backend.konan.serialization.isExported
@@ -16,10 +17,12 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -270,5 +273,16 @@ fun CallableMemberDescriptor.externalSymbolOrThrow(): String? {
if (this.annotations.hasAnnotation(TypedIntrinsic)) return null if (this.annotations.hasAnnotation(TypedIntrinsic)) return null
if (this.annotations.hasAnnotation(RuntimeNames.cCall)) return null
throw Error("external function ${this} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation") throw Error("external function ${this} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation")
} }
fun createAnnotation(
descriptor: ClassDescriptor,
vararg values: Pair<String, String>
): AnnotationDescriptor = AnnotationDescriptorImpl(
descriptor.defaultType,
values.map { (name, value) -> Name.identifier(name) to StringValue(value) }.toMap(),
SourceElement.NO_SOURCE
)
@@ -132,8 +132,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val nativePtr = symbolTable.referenceClass(context.nativePtr) val nativePtr = symbolTable.referenceClass(context.nativePtr)
val nativePtrType = nativePtr.typeWith(arguments = emptyList()) val nativePtrType = nativePtr.typeWith(arguments = emptyList())
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!)
val uByte = unsignedClass(UnsignedType.UBYTE) val uByte = unsignedClass(UnsignedType.UBYTE)
val uShort = unsignedClass(UnsignedType.USHORT) val uShort = unsignedClass(UnsignedType.USHORT)
@@ -181,9 +180,25 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context)) val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
val symbolName = topLevelClass(RuntimeNames.symbolName)
val exportForCppRuntime = topLevelClass(RuntimeNames.exportForCppRuntime)
val objCMethodImp = symbolTable.referenceClass(context.interopBuiltIns.objCMethodImp)
val interopNativePointedGetRawPointer = val interopNativePointedGetRawPointer =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.nativePointedGetRawPointer) symbolTable.referenceSimpleFunction(context.interopBuiltIns.nativePointedGetRawPointer)
val interopCPointer = symbolTable.referenceClass(context.interopBuiltIns.cPointer)
val interopCstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cstr.getter!!)
val interopWcstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.wcstr.getter!!)
val interopMemScope = symbolTable.referenceClass(context.interopBuiltIns.memScope)
val interopCValue = symbolTable.referenceClass(context.interopBuiltIns.cValue)
val interopCValues = symbolTable.referenceClass(context.interopBuiltIns.cValues)
val interopCValuesRef = symbolTable.referenceClass(context.interopBuiltIns.cValuesRef)
val interopCValueWrite = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueWrite)
val interopCValueRead = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueRead)
val interopAllocType = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocType)
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue) val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject) val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
@@ -194,6 +209,12 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
.single() .single()
) )
val interopObjCRetain = symbolTable.referenceSimpleFunction(
context.interopBuiltIns.packageScope
.getContributedFunctions(Name.identifier("objc_retain"), NoLookupLocation.FROM_BACKEND)
.single()
)
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass) val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
val interopObjCObjectSuperInitCheck = val interopObjCObjectSuperInitCheck =
@@ -204,10 +225,6 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val interopObjCObjectRawValueGetter = val interopObjCObjectRawValueGetter =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr) symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr)
val interopInvokeImpls = context.interopBuiltIns.invokeImpls.mapValues { (_, function) ->
symbolTable.referenceSimpleFunction(function)
}
val interopInterpretObjCPointer = val interopInterpretObjCPointer =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointer) symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointer)
@@ -397,20 +414,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
override val coroutineImpl get() = TODO() override val coroutineImpl get() = TODO()
val baseContinuationImpl = symbolTable.referenceClass( val baseContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.BaseContinuationImpl")
builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.BaseContinuationImpl")))!!
)
val restrictedContinuationImpl = symbolTable.referenceClass( val restrictedContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.RestrictedContinuationImpl")
builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.RestrictedContinuationImpl")))!!
)
val continuationImpl = symbolTable.referenceClass( val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.ContinuationImpl")))!!
)
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction( override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
coroutinesIntrinsicsPackage coroutinesIntrinsicsPackage
@@ -418,10 +426,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
.filterNot { it.isExpect }.single().getter!! .filterNot { it.isExpect }.single().getter!!
) )
val kotlinResult = symbolTable.referenceClass( val kotlinResult = topLevelClass("kotlin.Result")
builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(FqName("kotlin.Result")))!!
)
val kotlinResultGetOrThrow = symbolTable.referenceSimpleFunction( val kotlinResultGetOrThrow = symbolTable.referenceSimpleFunction(
builtInsPackage("kotlin") builtInsPackage("kotlin")
@@ -477,6 +482,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
context.builtIns.builtInsModule.findClassAcrossModuleDependencies( context.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(FqName("kotlin.native.concurrent.SharedImmutable")))!! ClassId.topLevel(FqName("kotlin.native.concurrent.SharedImmutable")))!!
private fun topLevelClass(fqName: String): IrClassSymbol = topLevelClass(FqName(fqName))
private fun topLevelClass(fqName: FqName): IrClassSymbol = classById(ClassId.topLevel(fqName))
private fun classById(classId: ClassId): IrClassSymbol =
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(classId)!!)
private fun internalFunction(name: String): IrSimpleFunctionSymbol = private fun internalFunction(name: String): IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single()) symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.explicitParameters import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
@@ -34,6 +35,16 @@ val IrDeclarationParent.fqNameSafe: FqName get() = when (this) {
else -> error(this) else -> error(this)
} }
val IrClass.classId: ClassId?
get() {
val parent = this.parent
return when (parent) {
is IrClass -> parent.classId?.createNestedClassId(this.name)
is IrPackageFragment -> ClassId.topLevel(parent.fqName.child(this.name))
else -> null
}
}
val IrDeclaration.name: Name val IrDeclaration.name: Name
get() = when (this) { get() = when (this) {
is IrSimpleFunction -> this.name is IrSimpleFunction -> this.name
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.konan.llvm package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationValue import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationValue
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
@@ -102,11 +103,11 @@ internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
return true return true
} }
private val symbolNameAnnotation = FqName("kotlin.native.SymbolName") private val symbolNameAnnotation = RuntimeNames.symbolName
private val cnameAnnotation = FqName("kotlin.native.CName") private val cnameAnnotation = FqName("kotlin.native.CName")
private val exportForCppRuntimeAnnotation = FqName("kotlin.native.internal.ExportForCppRuntime") private val exportForCppRuntimeAnnotation = RuntimeNames.exportForCppRuntime
private val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler") private val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
@@ -302,7 +302,10 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val found = LLVMGetNamedFunction(llvmModule, name) val found = LLVMGetNamedFunction(llvmModule, name)
if (found != null) { if (found != null) {
assert(getFunctionType(found) == type) assert(getFunctionType(found) == type) {
"Expected: ${LLVMPrintTypeToString(type)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
}
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage) assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
return found return found
} else { } else {
@@ -330,11 +333,13 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
return function return function
} }
private val usedLibraries = mutableSetOf<KonanLibrary>() val imports get() = context.llvmImports
val imports = object : LlvmImports { class ImportsImpl(private val context: Context) : LlvmImports {
private val allLibraries = context.librariesWithDependencies.toSet() private val usedLibraries = mutableSetOf<KonanLibrary>()
private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
override fun add(origin: CompiledKonanModuleOrigin) { override fun add(origin: CompiledKonanModuleOrigin) {
val library = when (origin) { val library = when (origin) {
@@ -348,11 +353,13 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
usedLibraries.add(library) usedLibraries.add(library)
} }
override fun isImported(library: KonanLibrary): Boolean = library in usedLibraries
} }
val librariesToLink: List<KonanLibrary> by lazy { val librariesToLink: List<KonanLibrary> by lazy {
context.config.resolvedLibraries context.config.resolvedLibraries
.filterRoots { (!it.isDefault && !context.config.purgeUserLibs) || it.library in usedLibraries } .filterRoots { (!it.isDefault && !context.config.purgeUserLibs) || imports.isImported(it.library) }
.getFullList(TopologicalLibraryOrder) .getFullList(TopologicalLibraryOrder)
} }
@@ -11,10 +11,12 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin
import org.jetbrains.kotlin.descriptors.konan.konanModuleOrigin import org.jetbrains.kotlin.descriptors.konan.konanModuleOrigin
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
internal interface LlvmImports { internal interface LlvmImports {
fun add(origin: CompiledKonanModuleOrigin) fun add(origin: CompiledKonanModuleOrigin)
fun isImported(library: KonanLibrary): Boolean
} }
internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKonanModuleOrigin internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKonanModuleOrigin
@@ -398,9 +398,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
} }
val llvmFunction = if (descriptor.isExternal) { val llvmFunction = if (descriptor.isExternal) {
if (descriptor.isTypedIntrinsic || descriptor.isObjCBridgeBased()) { if (descriptor.isTypedIntrinsic || descriptor.isObjCBridgeBased()
return || descriptor.annotations.hasAnnotation(RuntimeNames.cCall)) return
}
context.llvm.externalFunction(descriptor.symbolName, llvmFunctionType, context.llvm.externalFunction(descriptor.symbolName, llvmFunctionType,
// Assume that `external fun` is defined in native libs attached to this module: // Assume that `external fun` is defined in native libs attached to this module:
@@ -11,12 +11,17 @@ import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.cgen.KotlinStubs
import org.jetbrains.kotlin.backend.konan.cgen.generateCCall
import org.jetbrains.kotlin.backend.konan.cgen.generateCFunctionPointer
import org.jetbrains.kotlin.backend.konan.getInlinedClass import org.jetbrains.kotlin.backend.konan.getInlinedClass
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.irasdescriptors.* import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.backend.konan.objcexport.namePrefix
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -29,6 +34,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -38,6 +44,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -743,14 +750,53 @@ internal class InteropLoweringPart2(val context: Context) : FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
val transformer = InteropTransformer(context, irFile) val transformer = InteropTransformer(context, irFile)
irFile.transformChildrenVoid(transformer) irFile.transformChildrenVoid(transformer)
irFile.addChildren(transformer.newTopLevelDeclarations)
} }
} }
private class InteropTransformer(val context: Context, val irFile: IrFile) : IrBuildingTransformer(context) { private class InteropTransformer(val context: Context, val irFile: IrFile) : IrBuildingTransformer(context) {
val newTopLevelDeclarations = mutableListOf<IrDeclaration>()
val interop = context.interopBuiltIns val interop = context.interopBuiltIns
val symbols = context.ir.symbols val symbols = context.ir.symbols
private inline fun <T> generateWithStubs(element: IrElement? = null, block: KotlinStubs.() -> T): T =
createKotlinStubs(element).block()
private fun createKotlinStubs(element: IrElement?): KotlinStubs {
val location = if (element != null) {
element.getCompilerMessageLocation(irFile)
} else {
builder.getCompilerMessageLocation()
}
return object : KotlinStubs {
override val irBuiltIns get() = context.irBuiltIns
override val symbols get() = context.ir.symbols
override fun addKotlin(declaration: IrDeclaration) {
newTopLevelDeclarations += declaration
}
override fun addC(lines: List<String>) {
context.cStubsManager.addStub(location, lines)
}
override fun getUniqueCName(prefix: String) =
"_${context.moduleDescriptor.namePrefix}_${context.cStubsManager.getUniqueName(prefix)}"
override val target get() = context.config.target
override fun reportError(location: IrElement, message: String): Nothing =
context.reportCompilationError(message, irFile, location)
}
}
private fun generateCFunctionPointer(function: IrSimpleFunction, expression: IrExpression): IrExpression =
generateWithStubs { generateCFunctionPointer(function, function, false, expression) }
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
@@ -774,7 +820,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
} }
} }
fun reportError(message: String): Nothing = context.reportCompilationError(message, irFile, expression) if (function.descriptor.annotations.hasAnnotation(RuntimeNames.cCall)) {
context.llvmImports.add(function.descriptor.llvmSymbolOrigin)
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
}
val intrinsicType = tryGetIntrinsicType(expression) val intrinsicType = tryGetIntrinsicType(expression)
@@ -801,7 +850,8 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> { IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!) val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) { if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()
|| irCallableReference.symbol !is IrSimpleFunctionSymbol) {
context.reportCompilationError( context.reportCompilationError(
"${descriptor.fqNameSafe} must take an unbound, non-capturing function or lambda", "${descriptor.fqNameSafe} must take an unbound, non-capturing function or lambda",
irFile, expression irFile, expression
@@ -813,13 +863,6 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
val target = targetSymbol.owner val target = targetSymbol.owner
val signatureTypes = target.allParameters.map { it.type } + target.returnType val signatureTypes = target.allParameters.map { it.type } + target.returnType
signatureTypes.forEachIndexed { index, type ->
type.ensureSupportedInCallbacks(
isReturnType = (index == signatureTypes.lastIndex),
reportError = ::reportError
)
}
descriptor.typeParameters.forEachIndexed { index, typeParameterDescriptor -> descriptor.typeParameters.forEachIndexed { index, typeParameterDescriptor ->
val typeArgument = expression.getTypeArgument(typeParameterDescriptor)!!.toKotlinType() val typeArgument = expression.getTypeArgument(typeParameterDescriptor)!!.toKotlinType()
val signatureType = signatureTypes[index].toKotlinType() val signatureType = signatureTypes[index].toKotlinType()
@@ -832,39 +875,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
} }
} }
IrFunctionReferenceImpl( generateCFunctionPointer(target as IrSimpleFunction, expression)
builder.startOffset, builder.endOffset,
expression.type,
targetSymbol, target.descriptor,
typeArgumentsCount = 0)
} }
IntrinsicType.INTEROP_FUNPTR_INVOKE -> { IntrinsicType.INTEROP_FUNPTR_INVOKE -> {
// Replace by `invokeImpl${type}Ret`: generateWithStubs { generateCCall(expression, builder, isInvoke = true) }
val returnType =
expression.getTypeArgument(descriptor.typeParameters.single { it.name.asString() == "R" })!!
returnType.checkCTypeNullability(::reportError)
val invokeImpl = symbols.interopInvokeImpls[returnType.getClass()?.descriptor] ?:
context.reportCompilationError(
"Invocation of C function pointer with return type '${returnType.toKotlinType()}' is not supported yet",
irFile, expression
)
builder.irCall(invokeImpl).apply {
putValueArgument(0, expression.extensionReceiver)
val varargParameter = invokeImpl.owner.valueParameters[1]
val varargArgument = IrVarargImpl(
startOffset, endOffset, varargParameter.type, varargParameter.varargElementType!!
).apply {
descriptor.valueParameters.forEach {
this.addElement(expression.getValueArgument(it)!!)
}
}
putValueArgument(varargParameter.index, varargArgument)
}
} }
IntrinsicType.INTEROP_SIGN_EXTEND, IntrinsicType.INTEROP_NARROW -> { IntrinsicType.INTEROP_SIGN_EXTEND, IntrinsicType.INTEROP_NARROW -> {
@@ -24,14 +24,17 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.* import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
@@ -44,6 +47,14 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
internal fun irBuilder(irBuiltIns: IrBuiltIns, scopeOwnerSymbol: IrSymbol): IrBuilderWithScope =
object : IrBuilderWithScope(
IrGeneratorContextBase(irBuiltIns),
Scope(scopeOwnerSymbol),
UNDEFINED_OFFSET,
UNDEFINED_OFFSET
) {}
//TODO: delete file on next kotlin dependency update //TODO: delete file on next kotlin dependency update
internal fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null internal fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
@@ -506,6 +517,26 @@ fun IrBuilderWithScope.irCallOp(
fun IrBuilderWithScope.irSetVar(variable: IrVariable, value: IrExpression) = fun IrBuilderWithScope.irSetVar(variable: IrVariable, value: IrExpression) =
irSetVar(variable.symbol, value) irSetVar(variable.symbol, value)
fun IrBuilderWithScope.irCatch(type: IrType) =
IrCatchImpl(
startOffset, endOffset,
WrappedVariableDescriptor().let { descriptor ->
IrVariableImpl(
startOffset,
endOffset,
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
IrVariableSymbolImpl(descriptor),
Name.identifier("e"),
type,
false,
false,
false
).apply {
descriptor.bind(this)
}
}
)
/** /**
* Binds the arguments explicitly represented in the IR to the parameters of the accessed function. * Binds the arguments explicitly represented in the IR to the parameters of the accessed function.
* The arguments are to be evaluated in the same order as they appear in the resulting list. * The arguments are to be evaluated in the same order as they appear in the resulting list.
+10
View File
@@ -2882,6 +2882,10 @@ kotlinNativeInterop {
defFile 'interop/basics/ctypes.def' defFile 'interop/basics/ctypes.def'
} }
ccallbacksAndVarargs {
defFile 'interop/basics/ccallbacksAndVarargs.def'
}
if (isMac()) { if (isMac()) {
objcSmoke { objcSmoke {
defFile 'interop/objc/objcSmoke.def' defFile 'interop/objc/objcSmoke.def'
@@ -2982,6 +2986,12 @@ task interop_types(type: RunInteropKonanTest) {
interop = 'ctypes' interop = 'ctypes'
} }
task interop_callbacksAndVarargs(type: RunInteropKonanTest) {
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
source = "interop/basics/callbacksAndVarargs.kt"
interop = 'ccallbacksAndVarargs'
}
task interop_echo_server(type: RunInteropKonanTest) { task interop_echo_server(type: RunInteropKonanTest) {
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
if (!isMac()) { if (!isMac()) {
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import kotlin.test.*
import kotlinx.cinterop.*
import ccallbacksAndVarargs.*
fun main(args: Array<String>) {
assertEquals(42, getX(staticCFunction { -> cValue<S> { x = 42 } }))
applyCallback(cValue { x = 17 }, staticCFunction { it: CValue<S> ->
assertEquals(17, it.useContents { x })
})
assertEquals(66, makeS(66, 111).useContents { x })
assertEquals(E.ONE, makeE(1))
getVarargs(
0,
true,
2.toByte(),
Short.MIN_VALUE,
42,
Long.MAX_VALUE,
3.14f,
2.71,
0x1234ABCDL.toCPointer<COpaque>(),
UByte.MAX_VALUE,
22.toUShort(),
111u,
ULong.MAX_VALUE,
E.TWO,
cValue<S> { x = 15 }
).useContents {
assertEquals(1, a1)
assertEquals(2.toByte(), a2)
assertEquals(Short.MIN_VALUE, a3)
assertEquals(42, a4)
assertEquals(Long.MAX_VALUE, a5)
assertEquals(3.14f, a6)
assertEquals(2.71, a7)
assertEquals(0x1234ABCDL, a8.toLong())
assertEquals(UByte.MAX_VALUE, a9)
assertEquals(22.toUShort(), a10)
assertEquals(111u, a11)
assertEquals(ULong.MAX_VALUE, a12)
assertEquals(E.TWO, a13)
assertEquals(15, a14.x)
}
}
@@ -0,0 +1,72 @@
---
#include <stdarg.h>
struct S {
int x;
};
static int getX(struct S (*callback)(void)) {
return callback().x;
}
static void applyCallback(struct S s, void (*callback)(struct S)) {
callback(s);
}
static struct S makeS(int x, ...) {
return (struct S){ x };
}
enum E {
ZERO, ONE, TWO
};
static enum E makeE(int ordinal, ...) {
return ordinal;
}
struct Args {
char a1;
char a2;
short a3;
int a4;
long long a5;
float a6;
double a7;
void* a8;
unsigned char a9;
unsigned short a10;
unsigned int a11;
unsigned long long a12;
enum E a13;
struct S a14;
};
static struct Args getVarargs(int ignore, ...) {
va_list args;
va_start(args, ignore);
struct Args result = {
va_arg(args, char),
va_arg(args, char),
va_arg(args, short),
va_arg(args, int),
va_arg(args, long long),
va_arg(args, double),
va_arg(args, double),
va_arg(args, void*),
va_arg(args, unsigned char),
va_arg(args, unsigned short),
va_arg(args, unsigned int),
va_arg(args, unsigned long long),
va_arg(args, enum E),
va_arg(args, struct S)
};
va_end(args);
return result;
}
+16 -24
View File
@@ -36,10 +36,10 @@ llvmVersion = 6.0.1
# Mac OS X. # Mac OS X.
llvmHome.macos_x64 = clang-llvm-6.0.1-darwin-macos llvmHome.macos_x64 = clang-llvm-6.0.1-darwin-macos
targetToolchain.macos_x64 = target-toolchain-8-macos_x64 targetToolchain.macos_x64 = target-toolchain-8-macos_x64
libffiDir.macos_x64 = libffi-3.2.1-3-darwin-macos
arch.macos_x64 = x86_64 arch.macos_x64 = x86_64
targetSysRoot.macos_x64 = target-sysroot-8-macos_x64 targetSysRoot.macos_x64 = target-sysroot-8-macos_x64
libffiDir.macos_x64 = libffi-3.2.1-3-darwin-macos
llvmLtoFlags.macos_x64 = llvmLtoFlags.macos_x64 =
llvmLtoOptFlags.macos_x64 = -O3 -function-sections llvmLtoOptFlags.macos_x64 = -O3 -function-sections
llvmLtoNooptFlags.macos_x64 = -O1 llvmLtoNooptFlags.macos_x64 = -O1
@@ -65,7 +65,7 @@ target-toolchain-8-macos_x64.default = \
# Apple's 32-bit iOS. # Apple's 32-bit iOS.
targetToolchain.macos_x64-ios_arm32 = target-toolchain-8-macos_x64 targetToolchain.macos_x64-ios_arm32 = target-toolchain-8-macos_x64
dependencies.macos_x64-ios_arm32 = \ dependencies.macos_x64-ios_arm32 = \
libffi-3.2.1-3-darwin-ios \ libffi-3.2.1-3-darwin-macos \
clang-llvm-6.0.1-darwin-macos clang-llvm-6.0.1-darwin-macos
target-sysroot-8-ios_arm32.default = \ target-sysroot-8-ios_arm32.default = \
@@ -75,7 +75,6 @@ arch.ios_arm32 = armv7
entrySelector.ios_arm32 = -alias _Konan_main _main entrySelector.ios_arm32 = -alias _Konan_main _main
# Shared with 64-bit version. # Shared with 64-bit version.
targetSysRoot.ios_arm32 = target-sysroot-8-ios_arm64 targetSysRoot.ios_arm32 = target-sysroot-8-ios_arm64
libffiDir.ios_arm32 = libffi-3.2.1-3-darwin-ios
llvmLtoFlags.ios_arm32 = llvmLtoFlags.ios_arm32 =
llvmLtoOptFlags.ios_arm32 = -O3 -function-sections llvmLtoOptFlags.ios_arm32 = -O3 -function-sections
linkerNoDebugFlags.ios_arm32 = -S linkerNoDebugFlags.ios_arm32 = -S
@@ -91,7 +90,7 @@ osVersionMin.ios_arm32 = 9.0
# Apple's 64-bit iOS. # Apple's 64-bit iOS.
targetToolchain.macos_x64-ios_arm64 = target-toolchain-8-macos_x64 targetToolchain.macos_x64-ios_arm64 = target-toolchain-8-macos_x64
dependencies.macos_x64-ios_arm64 = \ dependencies.macos_x64-ios_arm64 = \
libffi-3.2.1-3-darwin-ios \ libffi-3.2.1-3-darwin-macos \
clang-llvm-6.0.1-darwin-macos clang-llvm-6.0.1-darwin-macos
target-sysroot-8-ios_arm64.default = \ target-sysroot-8-ios_arm64.default = \
@@ -100,7 +99,6 @@ target-sysroot-8-ios_arm64.default = \
arch.ios_arm64 = arm64 arch.ios_arm64 = arm64
entrySelector.ios_arm64 = -alias _Konan_main _main entrySelector.ios_arm64 = -alias _Konan_main _main
targetSysRoot.ios_arm64 = target-sysroot-8-ios_arm64 targetSysRoot.ios_arm64 = target-sysroot-8-ios_arm64
libffiDir.ios_arm64 = libffi-3.2.1-3-darwin-ios
llvmLtoFlags.ios_arm64 = llvmLtoFlags.ios_arm64 =
llvmLtoOptFlags.ios_arm64 = -O3 -function-sections llvmLtoOptFlags.ios_arm64 = -O3 -function-sections
linkerNoDebugFlags.ios_arm64 = -S linkerNoDebugFlags.ios_arm64 = -S
@@ -116,7 +114,7 @@ osVersionMin.ios_arm64 = 9.0
# Apple's iOS simulator. # Apple's iOS simulator.
targetToolchain.macos_x64-ios_x64 = target-toolchain-8-macos_x64 targetToolchain.macos_x64-ios_x64 = target-toolchain-8-macos_x64
dependencies.macos_x64-ios_x64 = \ dependencies.macos_x64-ios_x64 = \
libffi-3.2.1-2-darwin-ios_sim \ libffi-3.2.1-3-darwin-macos \
clang-llvm-6.0.1-darwin-macos clang-llvm-6.0.1-darwin-macos
target-sysroot-8-ios_x64.default = \ target-sysroot-8-ios_x64.default = \
@@ -125,7 +123,6 @@ target-sysroot-8-ios_x64.default = \
arch.ios_x64 = x86_64 arch.ios_x64 = x86_64
entrySelector.ios_x64 = -alias _Konan_main _main entrySelector.ios_x64 = -alias _Konan_main _main
targetSysRoot.ios_x64 = target-sysroot-8-ios_x64 targetSysRoot.ios_x64 = target-sysroot-8-ios_x64
libffiDir.ios_x64 = libffi-3.2.1-2-darwin-ios_sim
llvmLtoFlags.ios_x64 = llvmLtoFlags.ios_x64 =
llvmLtoOptFlags.ios_x64 = -O3 -function-sections llvmLtoOptFlags.ios_x64 = -O3 -function-sections
llvmLtoNooptFlags.ios_x64 = -O1 llvmLtoNooptFlags.ios_x64 = -O1
@@ -142,10 +139,10 @@ osVersionMin.ios_x64 = 9.0
llvmHome.linux_x64 = clang-llvm-6.0.1-linux-x86-64 llvmHome.linux_x64 = clang-llvm-6.0.1-linux-x86-64
gccToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64 gccToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64
targetToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu targetToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu
libffiDir.linux_x64 = libffi-3.2.1-2-linux-x86-64
quadruple.linux_x64 = x86_64-unknown-linux-gnu quadruple.linux_x64 = x86_64-unknown-linux-gnu
targetSysRoot.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot targetSysRoot.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
libffiDir.linux_x64 = libffi-3.2.1-2-linux-x86-64
# targetSysroot-relative. # targetSysroot-relative.
libGcc.linux_x64 = ../../lib/gcc/x86_64-unknown-linux-gnu/4.8.5 libGcc.linux_x64 = ../../lib/gcc/x86_64-unknown-linux-gnu/4.8.5
llvmLtoFlags.linux_x64 = llvmLtoFlags.linux_x64 =
@@ -174,8 +171,7 @@ dependencies.linux_x64-linux_arm32_hfp = \
clang-llvm-6.0.1-linux-x86-64 \ clang-llvm-6.0.1-linux-x86-64 \
target-gcc-toolchain-3-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \
target-sysroot-1-raspberrypi \ target-sysroot-1-raspberrypi \
libffi-3.2.1-2-linux-x86-64 \ libffi-3.2.1-2-linux-x86-64
libffi-3.2.1-2-raspberrypi
quadruple.linux_arm32_hfp = armv6-unknown-linux-gnueabihf quadruple.linux_arm32_hfp = armv6-unknown-linux-gnueabihf
entrySelector.linux_arm32_hfp = --defsym main=Konan_main entrySelector.linux_arm32_hfp = --defsym main=Konan_main
@@ -184,7 +180,6 @@ linkerDynamicFlags.linux_arm32_hfp = -shared
linkerOptimizationFlags.linux_arm32_hfp = --gc-sections linkerOptimizationFlags.linux_arm32_hfp = --gc-sections
targetSysRoot.linux_arm32_hfp = target-sysroot-1-raspberrypi targetSysRoot.linux_arm32_hfp = target-sysroot-1-raspberrypi
# We could reuse host toolchain here. # We could reuse host toolchain here.
libffiDir.linux_arm32_hfp = libffi-3.2.1-2-raspberrypi
linkerKonanFlags.linux_arm32_hfp = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \ linkerKonanFlags.linux_arm32_hfp = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
--defsym __cxa_demangle=Konan_cxa_demangle --defsym __cxa_demangle=Konan_cxa_demangle
# targetSysroot-relative. # targetSysroot-relative.
@@ -207,8 +202,7 @@ dependencies.linux_x64-linux_mips32 = \
target-gcc-toolchain-2-linux-mips \ target-gcc-toolchain-2-linux-mips \
target-gcc-toolchain-3-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \
target-sysroot-2-mips \ target-sysroot-2-mips \
libffi-3.2.1-2-linux-x86-64 \ libffi-3.2.1-2-linux-x86-64
libffi-3.2.1-2-linux-mips
quadruple.linux_mips32 = mips-unknown-linux-gnu quadruple.linux_mips32 = mips-unknown-linux-gnu
entrySelector.linux_mips32 = --defsym main=Konan_main entrySelector.linux_mips32 = --defsym main=Konan_main
@@ -216,7 +210,6 @@ linkerOptimizationFlags.linux_mips32 = --gc-sections
linkerDynamicFlags.linux_mips32 = -shared linkerDynamicFlags.linux_mips32 = -shared
targetSysRoot.linux_mips32 = target-sysroot-2-mips targetSysRoot.linux_mips32 = target-sysroot-2-mips
# We could reuse host toolchain here. # We could reuse host toolchain here.
libffiDir.linux_mips32 = libffi-3.2.1-2-linux-mips
linkerKonanFlags.linux_mips32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \ linkerKonanFlags.linux_mips32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
--defsym __cxa_demangle=Konan_cxa_demangle -z notext --defsym __cxa_demangle=Konan_cxa_demangle -z notext
# targetSysroot-relative. # targetSysroot-relative.
@@ -236,15 +229,13 @@ dependencies.linux_x64-linux_mipsel32 = \
target-gcc-toolchain-2-linux-mips \ target-gcc-toolchain-2-linux-mips \
target-gcc-toolchain-3-linux-x86-64 \ target-gcc-toolchain-3-linux-x86-64 \
target-sysroot-2-mipsel \ target-sysroot-2-mipsel \
libffi-3.2.1-2-linux-x86-64 \ libffi-3.2.1-2-linux-x86-64
libffi-3.2.1-2-linux-mipsel
quadruple.linux_mipsel32 = mipsel-unknown-linux-gnu quadruple.linux_mipsel32 = mipsel-unknown-linux-gnu
entrySelector.linux_mipsel32 = --defsym main=Konan_main entrySelector.linux_mipsel32 = --defsym main=Konan_main
linkerOptimizationFlags.linux_mipsel32 = --gc-sections linkerOptimizationFlags.linux_mipsel32 = --gc-sections
linkerDynamicFlags.linux_mipsel32 = -shared linkerDynamicFlags.linux_mipsel32 = -shared
targetSysRoot.linux_mipsel32 = target-sysroot-2-mipsel targetSysRoot.linux_mipsel32 = target-sysroot-2-mipsel
# We could reuse host toolchain here. # We could reuse host toolchain here.
libffiDir.linux_mipsel32 = libffi-3.2.1-2-linux-mipsel
linkerKonanFlags.linux_mipsel32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \ linkerKonanFlags.linux_mipsel32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
--defsym __cxa_demangle=Konan_cxa_demangle -z notext --defsym __cxa_demangle=Konan_cxa_demangle -z notext
# targetSysroot-relative. # targetSysroot-relative.
@@ -264,13 +255,13 @@ dependencies.macos_x64-android_arm32 = \
clang-llvm-6.0.1-darwin-macos \ clang-llvm-6.0.1-darwin-macos \
target-sysroot-21-android_arm32 \ target-sysroot-21-android_arm32 \
target-toolchain-21-osx-android_arm32 \ target-toolchain-21-osx-android_arm32 \
libffi-3.2.1-2-android_arm32 libffi-3.2.1-3-darwin-macos
targetToolchain.linux_x64-android_arm32 = target-toolchain-21-linux-android_arm32 targetToolchain.linux_x64-android_arm32 = target-toolchain-21-linux-android_arm32
dependencies.linux_x64-android_arm32 = \ dependencies.linux_x64-android_arm32 = \
clang-llvm-6.0.1-linux-x86-64 \ clang-llvm-6.0.1-linux-x86-64 \
target-sysroot-21-android_arm32 \ target-sysroot-21-android_arm32 \
target-toolchain-21-linux-android_arm32 \ target-toolchain-21-linux-android_arm32 \
libffi-3.2.1-2-android_arm32 libffi-3.2.1-2-linux-x86-64
quadruple.android_arm32 = arm-linux-androideabi quadruple.android_arm32 = arm-linux-androideabi
entrySelector.android_arm32 = -Wl,--defsym -Wl,main=Konan_main entrySelector.android_arm32 = -Wl,--defsym -Wl,main=Konan_main
@@ -279,7 +270,6 @@ llvmLtoOptFlags.android_arm32 = -O3 -function-sections
linkerNoDebugFlags.android_arm32 = -Wl,-S linkerNoDebugFlags.android_arm32 = -Wl,-S
llvmLtoNooptFlags.android_arm32 = -O1 llvmLtoNooptFlags.android_arm32 = -O1
targetSysRoot.android_arm32 = target-sysroot-21-android_arm32 targetSysRoot.android_arm32 = target-sysroot-21-android_arm32
libffiDir.android_arm32 = libffi-3.2.1-2-android_arm32
linkerKonanFlags.android_arm32 = -lm -latomic -lstdc++ -landroid linkerKonanFlags.android_arm32 = -lm -latomic -lstdc++ -landroid
# Android ARM64, based on NDK for android-21. # Android ARM64, based on NDK for android-21.
@@ -289,29 +279,28 @@ dependencies.macos_x64-android_arm64 = \
clang-llvm-6.0.1-darwin-macos \ clang-llvm-6.0.1-darwin-macos \
target-sysroot-21-android_arm64 \ target-sysroot-21-android_arm64 \
target-toolchain-21-osx-android_arm64 \ target-toolchain-21-osx-android_arm64 \
libffi-3.2.1-2-android_arm64 libffi-3.2.1-3-darwin-macos
targetToolchain.linux_x64-android_arm64 = target-toolchain-21-linux-android_arm64 targetToolchain.linux_x64-android_arm64 = target-toolchain-21-linux-android_arm64
dependencies.linux_x64-android_arm64 = \ dependencies.linux_x64-android_arm64 = \
clang-llvm-6.0.1-linux-x86-64 \ clang-llvm-6.0.1-linux-x86-64 \
target-sysroot-21-android_arm64 \ target-sysroot-21-android_arm64 \
target-toolchain-21-linux-android_arm64 \ target-toolchain-21-linux-android_arm64 \
libffi-3.2.1-2-android_arm64 libffi-3.2.1-2-linux-x86-64
quadruple.android_arm64 = aarch64-linux-android quadruple.android_arm64 = aarch64-linux-android
entrySelector.android_arm64 = -Wl,--defsym -Wl,main=Konan_main entrySelector.android_arm64 = -Wl,--defsym -Wl,main=Konan_main
llvmLtoFlags.android_arm64 = -emulated-tls -relocation-model=pic llvmLtoFlags.android_arm64 = -emulated-tls -relocation-model=pic
targetSysRoot.android_arm64 = target-sysroot-21-android_arm64 targetSysRoot.android_arm64 = target-sysroot-21-android_arm64
libffiDir.android_arm64 = libffi-3.2.1-2-android_arm64
linkerKonanFlags.android_arm64 = -lm -latomic -lstdc++ -landroid linkerKonanFlags.android_arm64 = -lm -latomic -lstdc++ -landroid
linkerNoDebugFlags.android_arm64 = -Wl,-S linkerNoDebugFlags.android_arm64 = -Wl,-S
# Windows x86-64, based on mingw-w64. # Windows x86-64, based on mingw-w64.
llvmHome.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 llvmHome.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
targetToolchain.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 targetToolchain.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64
quadruple.mingw_x64 = x86_64-w64-mingw32 quadruple.mingw_x64 = x86_64-w64-mingw32
targetSysRoot.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 targetSysRoot.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64
# For using with Universal Windows Platform (UWP) we need to use this slower option. # For using with Universal Windows Platform (UWP) we need to use this slower option.
# See https://youtrack.jetbrains.com/issue/KT-27654. # See https://youtrack.jetbrains.com/issue/KT-27654.
# TODO: remove, once fixed in mingw, check with the bug testcase. # TODO: remove, once fixed in mingw, check with the bug testcase.
@@ -333,18 +322,21 @@ dependencies.mingw_x64 = \
# WebAssembly 32-bit. # WebAssembly 32-bit.
targetToolchain.macos_x64-wasm32 = target-toolchain-3-macos-wasm targetToolchain.macos_x64-wasm32 = target-toolchain-3-macos-wasm
dependencies.macos_x64-wasm32 = \ dependencies.macos_x64-wasm32 = \
libffi-3.2.1-3-darwin-macos \
clang-llvm-6.0.1-darwin-macos \ clang-llvm-6.0.1-darwin-macos \
target-sysroot-2-wasm \ target-sysroot-2-wasm \
target-toolchain-3-macos-wasm target-toolchain-3-macos-wasm
targetToolchain.linux_x64-wasm32 = target-toolchain-2-linux-wasm targetToolchain.linux_x64-wasm32 = target-toolchain-2-linux-wasm
dependencies.linux_x64-wasm32 = \ dependencies.linux_x64-wasm32 = \
libffi-3.2.1-2-linux-x86-64 \
clang-llvm-6.0.1-linux-x86-64 \ clang-llvm-6.0.1-linux-x86-64 \
target-sysroot-2-wasm \ target-sysroot-2-wasm \
target-toolchain-2-linux-wasm target-toolchain-2-linux-wasm
targetToolchain.mingw_x64-wasm32 = target-toolchain-2-mingw-wasm targetToolchain.mingw_x64-wasm32 = target-toolchain-2-mingw-wasm
dependencies.mingw_x64-wasm32 = \ dependencies.mingw_x64-wasm32 = \
libffi-3.2.1-mingw-w64-x86-64 \
msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \ msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \
target-sysroot-2-wasm \ target-sysroot-2-wasm \
target-toolchain-2-mingw-wasm target-toolchain-2-mingw-wasm
+3
View File
@@ -25,6 +25,7 @@ boardSpecificClangFlags.zephyr_stm32f4_disco = -mabi=aapcs -mthumb -mcpu=cortex-
targetToolchain.linux_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-linux/arm-none-eabi targetToolchain.linux_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-linux/arm-none-eabi
dependencies.linux_x64-zephyr_stm32f4_disco = \ dependencies.linux_x64-zephyr_stm32f4_disco = \
libffi-3.2.1-2-linux-x86-64 \
clang-llvm-6.0.1-linux-x86-64 \ clang-llvm-6.0.1-linux-x86-64 \
gcc-arm-none-eabi-7-2017-q4-major-linux \ gcc-arm-none-eabi-7-2017-q4-major-linux \
target-sysroot-2-wasm target-sysroot-2-wasm
@@ -32,11 +33,13 @@ dependencies.linux_x64-zephyr_stm32f4_disco = \
targetToolchain.macos_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-mac/arm-none-eabi targetToolchain.macos_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-mac/arm-none-eabi
dependencies.macos_x64-zephyr_stm32f4_disco = \ dependencies.macos_x64-zephyr_stm32f4_disco = \
gcc-arm-none-eabi-7-2017-q4-major-mac \ gcc-arm-none-eabi-7-2017-q4-major-mac \
libffi-3.2.1-3-darwin-macos \
clang-llvm-6.0.1-darwin-macos \ clang-llvm-6.0.1-darwin-macos \
target-sysroot-2-wasm target-sysroot-2-wasm
targetToolchain.mingw_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-win32/arm-none-eabi targetToolchain.mingw_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-win32/arm-none-eabi
dependencies.mingw_x64-zephyr_stm32f4_disco = \ dependencies.mingw_x64-zephyr_stm32f4_disco = \
libffi-3.2.1-mingw-w64-x86-64 \
msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \ msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \
gcc-arm-none-eabi-7-2017-q4-major-win32 \ gcc-arm-none-eabi-7-2017-q4-major-win32 \
target-sysroot-2-wasm target-sysroot-2-wasm
-2
View File
@@ -23,8 +23,6 @@ targetList.each { targetName ->
if (!isWindows()) if (!isWindows())
compilerArgs '-fPIC' compilerArgs '-fPIC'
includeRuntime(delegate) includeRuntime(delegate)
if (rootProject.hasProperty("${targetName}LibffiDir"))
compilerArgs '-I' + project.file(rootProject.ext.get("${targetName}LibffiDir") + "/include")
linkerArgs project.file("../common/build/$targetName/hash.bc").path linkerArgs project.file("../common/build/$targetName/hash.bc").path
} }
-74
View File
@@ -18,85 +18,11 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#ifndef KONAN_NO_FFI
#include <ffi.h>
#endif
#include "Memory.h" #include "Memory.h"
#include "Types.h" #include "Types.h"
namespace {
typedef int FfiTypeKind;
#ifndef KONAN_NO_FFI
// Also declared in Varargs.kt
const FfiTypeKind FFI_TYPE_KIND_VOID = 0;
const FfiTypeKind FFI_TYPE_KIND_SINT8 = 1;
const FfiTypeKind FFI_TYPE_KIND_SINT16 = 2;
const FfiTypeKind FFI_TYPE_KIND_SINT32 = 3;
const FfiTypeKind FFI_TYPE_KIND_SINT64 = 4;
const FfiTypeKind FFI_TYPE_KIND_FLOAT = 5;
const FfiTypeKind FFI_TYPE_KIND_DOUBLE = 6;
const FfiTypeKind FFI_TYPE_KIND_POINTER = 7;
const FfiTypeKind FFI_TYPE_KIND_UINT8 = 8;
const FfiTypeKind FFI_TYPE_KIND_UINT16 = 9;
const FfiTypeKind FFI_TYPE_KIND_UINT32 = 10;
const FfiTypeKind FFI_TYPE_KIND_UINT64 = 11;
ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) {
switch (typeKind) {
case FFI_TYPE_KIND_VOID: return &ffi_type_void;
case FFI_TYPE_KIND_SINT8: return &ffi_type_sint8;
case FFI_TYPE_KIND_SINT16: return &ffi_type_sint16;
case FFI_TYPE_KIND_SINT32: return &ffi_type_sint32;
case FFI_TYPE_KIND_SINT64: return &ffi_type_sint64;
case FFI_TYPE_KIND_FLOAT: return &ffi_type_float;
case FFI_TYPE_KIND_DOUBLE: return &ffi_type_double;
case FFI_TYPE_KIND_POINTER: return &ffi_type_pointer;
case FFI_TYPE_KIND_UINT8: return &ffi_type_uint8;
case FFI_TYPE_KIND_UINT16: return &ffi_type_uint16;
case FFI_TYPE_KIND_UINT32: return &ffi_type_uint32;
case FFI_TYPE_KIND_UINT64: return &ffi_type_uint64;
default: assert(false); return nullptr;
}
}
#endif // KONAN_NO_FFI
} // namespace
extern "C" { extern "C" {
void Kotlin_Interop_callFunctionPointer(void* codePtr, void* returnValuePtr, FfiTypeKind returnTypeKind,
void** arguments, intptr_t* argumentTypeKinds,
int totalArgumentsNumber, int variadicArgumentsNumber) {
#ifdef KONAN_NO_FFI
RuntimeAssert(false, "Vararg calls are not supported on this platform");
#else
ffi_type** argumentTypes = (ffi_type**)argumentTypeKinds;
// In-place convertion:
for (int i = 0; i < totalArgumentsNumber; ++i) {
argumentTypes[i] = convertFfiTypeKindToType((FfiTypeKind) argumentTypeKinds[i]);
}
ffi_type* returnType = convertFfiTypeKindToType(returnTypeKind);
ffi_cif cif;
if (variadicArgumentsNumber < 0) {
// Non-variadic.
ffi_prep_cif(&cif, FFI_DEFAULT_ABI, totalArgumentsNumber, returnType, argumentTypes);
} else {
int fixedArgumentsNumber = totalArgumentsNumber - variadicArgumentsNumber;
ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI,
fixedArgumentsNumber, totalArgumentsNumber,
returnType, argumentTypes);
}
ffi_call(&cif, (void (*)())codePtr, returnValuePtr, arguments);
#endif
}
KNativePtr Kotlin_Interop_createStablePointer(KRef any) { KNativePtr Kotlin_Interop_createStablePointer(KRef any) {
return CreateStablePointer(any); return CreateStablePointer(any);
} }
@@ -69,7 +69,10 @@ open class Command(initialCommand: List<String>) {
/** /**
* If withErrors is true then output from error stream will be added * If withErrors is true then output from error stream will be added
*/ */
fun getOutputLines(withErrors: Boolean = false): List<String> { fun getOutputLines(withErrors: Boolean = false): List<String> =
getResult(withErrors, handleError = true).outputLines
fun getResult(withErrors: Boolean, handleError: Boolean = false): Result {
log() log()
val outputFile = createTempFile() val outputFile = createTempFile()
@@ -80,7 +83,7 @@ open class Command(initialCommand: List<String>) {
builder.redirectInput(Redirect.INHERIT) builder.redirectInput(Redirect.INHERIT)
builder.redirectError(Redirect.INHERIT) builder.redirectError(Redirect.INHERIT)
builder.redirectOutput(ProcessBuilder.Redirect.to(outputFile)) builder.redirectOutput(Redirect.to(outputFile))
.redirectErrorStream(withErrors) .redirectErrorStream(withErrors)
// Note: getting process output could be done without redirecting to temporary file, // Note: getting process output could be done without redirecting to temporary file,
// however this would require managing a thread to read `process.inputStream` because // however this would require managing a thread to read `process.inputStream` because
@@ -88,14 +91,16 @@ open class Command(initialCommand: List<String>) {
val process = builder.start() val process = builder.start()
val code = process.waitFor() val code = process.waitFor()
handleExitCode(code) if (handleError) handleExitCode(code)
return outputFile.readLines() return Result(code, outputFile.readLines())
} finally { } finally {
outputFile.delete() outputFile.delete()
} }
} }
class Result(val exitCode: Int, val outputLines: List<String>)
private fun handleExitCode(code: Int) { private fun handleExitCode(code: Int) {
if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.", command[0]) if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.", command[0])
} }
@@ -24,6 +24,7 @@ interface Configurables : TargetableExternalStorage {
val llvmHome get() = hostString("llvmHome") val llvmHome get() = hostString("llvmHome")
val llvmVersion get() = hostString("llvmVersion") val llvmVersion get() = hostString("llvmVersion")
val libffiDir get() = hostString("libffiDir")
// TODO: Delegate to a map? // TODO: Delegate to a map?
val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags") val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags")
@@ -37,7 +38,6 @@ interface Configurables : TargetableExternalStorage {
val linkerDynamicFlags get() = targetList("linkerDynamicFlags") val linkerDynamicFlags get() = targetList("linkerDynamicFlags")
val llvmDebugOptFlags get() = targetList("llvmDebugOptFlags") val llvmDebugOptFlags get() = targetList("llvmDebugOptFlags")
val targetSysRoot get() = targetString("targetSysRoot") val targetSysRoot get() = targetString("targetSysRoot")
val libffiDir get() = targetString("libffiDir")
// Notice: these ones are host-target. // Notice: these ones are host-target.
val targetToolchain get() = hostTargetString("targetToolchain") val targetToolchain get() = hostTargetString("targetToolchain")
@@ -45,7 +45,6 @@ interface Configurables : TargetableExternalStorage {
val absoluteTargetSysRoot get() = absolute(targetSysRoot) val absoluteTargetSysRoot get() = absolute(targetSysRoot)
val absoluteTargetToolchain get() = absolute(targetToolchain) val absoluteTargetToolchain get() = absolute(targetToolchain)
val absoluteLlvmHome get() = absolute(llvmHome) val absoluteLlvmHome get() = absolute(llvmHome)
val absoluteLibffiDir get() = absolute(libffiDir)
} }
interface NonAppleConfigurables : Configurables { interface NonAppleConfigurables : Configurables {
@@ -44,9 +44,6 @@ abstract class LinkerFlags(val configurables: Configurables)
else -> error("Don't know libLTO location for this platform.") else -> error("Don't know libLTO location for this platform.")
} }
val targetLibffi = configurables.libffiDir?.let { listOf("${configurables.absoluteLibffiDir}/lib/libffi.a") }
?: emptyList()
open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor. open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor.
abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile, abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,