[Interop][Lowering] Support metadata-based structs
This commit is contained in:
committed by
Sergey Bogolepov
parent
7d48c580ca
commit
2e594bb19c
+2
@@ -10,6 +10,8 @@ object RuntimeNames {
|
|||||||
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
||||||
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
||||||
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
||||||
|
val cStructMemberAt = FqName("kotlinx.cinterop.internal.CStruct.MemberAt")
|
||||||
|
val cStructBitField = FqName("kotlinx.cinterop.internal.CStruct.BitField")
|
||||||
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
||||||
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||||
val independent = FqName("kotlin.native.internal.Independent")
|
val independent = FqName("kotlin.native.internal.Independent")
|
||||||
|
|||||||
+1
-1
@@ -602,7 +602,7 @@ private fun IrType.isUShort() = this.isUnsigned(UnsignedType.USHORT)
|
|||||||
private fun IrType.isUInt() = this.isUnsigned(UnsignedType.UINT)
|
private fun IrType.isUInt() = this.isUnsigned(UnsignedType.UINT)
|
||||||
private fun IrType.isULong() = this.isUnsigned(UnsignedType.ULONG)
|
private fun IrType.isULong() = this.isUnsigned(UnsignedType.ULONG)
|
||||||
|
|
||||||
private fun IrType.isCEnumType(): Boolean {
|
internal fun IrType.isCEnumType(): Boolean {
|
||||||
val simpleType = this as? IrSimpleType ?: return false
|
val simpleType = this as? IrSimpleType ?: return false
|
||||||
if (simpleType.hasQuestionMark) return false
|
if (simpleType.hasQuestionMark) return false
|
||||||
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
|
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
|
||||||
|
|||||||
+3
@@ -198,6 +198,9 @@ internal class KonanSymbols(
|
|||||||
|
|
||||||
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
|
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
|
||||||
|
|
||||||
|
val readBits = interopFunction("readBits")
|
||||||
|
val writeBits = interopFunction("writeBits")
|
||||||
|
|
||||||
val objCExportTrapOnUndeclaredException =
|
val objCExportTrapOnUndeclaredException =
|
||||||
symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions(
|
symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions(
|
||||||
Name.identifier("trapOnUndeclaredException"),
|
Name.identifier("trapOnUndeclaredException"),
|
||||||
|
|||||||
+186
-26
@@ -7,22 +7,20 @@ package org.jetbrains.kotlin.backend.konan.lower
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
|
import org.jetbrains.kotlin.backend.konan.cgen.isCEnumType
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
|
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.superClasses
|
import org.jetbrains.kotlin.backend.konan.ir.superClasses
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
|
||||||
import org.jetbrains.kotlin.ir.builders.irGetObject
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
import org.jetbrains.kotlin.ir.declarations.isPropertyAccessor
|
import org.jetbrains.kotlin.ir.declarations.isPropertyAccessor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,10 +36,20 @@ private fun isEnumVarValueAccessor(function: IrFunction, symbols: KonanSymbols):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isMemberAtAccessor(function: IrFunction): Boolean =
|
||||||
|
function.hasAnnotation(RuntimeNames.cStructMemberAt)
|
||||||
|
|
||||||
|
private fun isBitFieldAccessor(function: IrFunction): Boolean =
|
||||||
|
function.hasAnnotation(RuntimeNames.cStructBitField)
|
||||||
|
|
||||||
private class InteropCallContext(
|
private class InteropCallContext(
|
||||||
val symbols: KonanSymbols,
|
val symbols: KonanSymbols,
|
||||||
val builder: IrBuilderWithScope
|
val builder: IrBuilderWithScope
|
||||||
)
|
) {
|
||||||
|
fun IrType.isCPointer(): Boolean = this.classOrNull == symbols.interopCPointer
|
||||||
|
|
||||||
|
fun IrType.isNativePointed(): Boolean = isSubtypeOfClass(symbols.nativePointed)
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <T> generateInteropCall(
|
private inline fun <T> generateInteropCall(
|
||||||
symbols: KonanSymbols,
|
symbols: KonanSymbols,
|
||||||
@@ -154,40 +162,192 @@ private fun InteropCallContext.convertIntegralToEnum(value: IrExpression, enumTy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun IrType.getCEnumPrimitiveType(): IrType {
|
||||||
* Returns null if the given call-site is not an accessor for T.value, T : CEnumVar.
|
assert(this.isCEnumType())
|
||||||
* Otherwise, generates:
|
val enumClass = this.getClass()!!
|
||||||
*
|
return enumClass.properties.single { it.name.asString() == "value" }
|
||||||
* get() => byValue(this.reinterpret().value)
|
.getter!!.returnType
|
||||||
*
|
}
|
||||||
* set(value) => this.reinterpret().value = value.value
|
|
||||||
|
private fun InteropCallContext.readEnumValueFromMemory(nativePtr: IrExpression, enumType: IrType): IrExpression {
|
||||||
|
val enumPrimitiveType = enumType.getCEnumPrimitiveType()
|
||||||
|
val readMemory = readPrimitiveFromMemory(nativePtr, enumPrimitiveType)
|
||||||
|
return convertIntegralToEnum(readMemory, enumType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.writeEnumValueToMemory(nativePtr: IrExpression, value: IrExpression): IrExpression {
|
||||||
|
val valueToWrite = convertEnumToIntegral(value)
|
||||||
|
return writePrimitiveToMemory(nativePtr, valueToWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.convertCPointerToNativePtr(cPointer: IrExpression): IrExpression {
|
||||||
|
return builder.irCall(symbols.interopCPointerGetRawValue).also {
|
||||||
|
it.extensionReceiver = cPointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun InteropCallContext.writePointerToMemory(nativePtr: IrExpression, value: IrExpression): IrExpression {
|
||||||
|
val valueToWrite = when {
|
||||||
|
value.type.isCPointer() -> convertCPointerToNativePtr(value)
|
||||||
|
else -> error("Unsupported pointer type")
|
||||||
|
}
|
||||||
|
return writePrimitiveToMemory(nativePtr, valueToWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.calculateFieldPointer(receiver: IrExpression, offset: Long): IrExpression {
|
||||||
|
val base = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
|
||||||
|
it.dispatchReceiver = receiver
|
||||||
|
}
|
||||||
|
val nativePtrPlusLong = symbols.nativePtrType.getClass()!!
|
||||||
|
.functions.single { it.name.identifier == "plus" }
|
||||||
|
return with (builder) {
|
||||||
|
irCall(nativePtrPlusLong).also {
|
||||||
|
it.dispatchReceiver = base
|
||||||
|
it.putValueArgument(0, irLong(offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.readPointerFromMemory(nativePtr: IrExpression): IrExpression {
|
||||||
|
val readMemory = readPrimitiveFromMemory(nativePtr, symbols.nativePtrType)
|
||||||
|
return builder.irCall(symbols.interopInterpretCPointer).also {
|
||||||
|
it.putValueArgument(0, readMemory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.readPointed(nativePtr: IrExpression): IrExpression {
|
||||||
|
return builder.irCall(symbols.interopInterpretNullablePointed).also {
|
||||||
|
it.putValueArgument(0, nativePtr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns non-null result if [callSite] is accessor to:
|
||||||
|
* 1. T.value, T : CEnumVar
|
||||||
|
* 2. T.<field-name>, T : CStructVar and accessor is annotated with
|
||||||
|
* [kotlinx.cinterop.internal.CStruct.MemberAt] or [kotlinx.cinterop.internal.CStruct.BitField]
|
||||||
*/
|
*/
|
||||||
internal fun tryGenerateEnumVarValueAccess(
|
internal fun tryGenerateInteropMemberAccess(
|
||||||
callSite: IrCall,
|
callSite: IrCall,
|
||||||
symbols: KonanSymbols,
|
symbols: KonanSymbols,
|
||||||
builder: IrBuilderWithScope
|
builder: IrBuilderWithScope
|
||||||
): IrExpression? = if (isEnumVarValueAccessor(callSite.symbol.owner, symbols))
|
): IrExpression? = when {
|
||||||
generateInteropCall(symbols, builder) { generateEnumVarValueAccess(callSite) }
|
isEnumVarValueAccessor(callSite.symbol.owner, symbols) ->
|
||||||
else null
|
generateInteropCall(symbols, builder) { generateEnumVarValueAccess(callSite) }
|
||||||
|
isMemberAtAccessor(callSite.symbol.owner) ->
|
||||||
|
generateInteropCall(symbols, builder) { generateMemberAtAccess(callSite) }
|
||||||
|
isBitFieldAccessor(callSite.symbol.owner) ->
|
||||||
|
generateInteropCall(symbols, builder) { generateBitFieldAccess(callSite) }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
private fun InteropCallContext.generateEnumVarValueAccess(callSite: IrCall): IrExpression {
|
private fun InteropCallContext.generateEnumVarValueAccess(callSite: IrCall): IrExpression {
|
||||||
val accessor = callSite.symbol.owner
|
val accessor = callSite.symbol.owner
|
||||||
val nativePtr = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
|
val nativePtr = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
|
||||||
it.dispatchReceiver = callSite.dispatchReceiver!!
|
it.dispatchReceiver = callSite.dispatchReceiver!!
|
||||||
}
|
}
|
||||||
|
return when {
|
||||||
|
accessor.isGetter -> readEnumValueFromMemory(nativePtr, accessor.returnType)
|
||||||
|
accessor.isSetter -> writeEnumValueToMemory(nativePtr, callSite.getValueArgument(0)!!)
|
||||||
|
else -> error("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpression {
|
||||||
|
val accessor = callSite.symbol.owner
|
||||||
|
val memberAt = accessor.getAnnotation(RuntimeNames.cStructMemberAt)!!
|
||||||
|
val offset = (memberAt.getValueArgument(0) as IrConst<Long>).value
|
||||||
|
val fieldPointer = calculateFieldPointer(callSite.dispatchReceiver!!, offset)
|
||||||
return when {
|
return when {
|
||||||
accessor.isGetter -> {
|
accessor.isGetter -> {
|
||||||
val enumClass = accessor.returnType.getClass()!!
|
val type = accessor.returnType
|
||||||
val enumPrimitiveType = enumClass.properties.single { it.name.asString() == "value" }
|
when {
|
||||||
.getter!!.returnType
|
type.isCEnumType() -> readEnumValueFromMemory(fieldPointer, type)
|
||||||
val readMemory = readPrimitiveFromMemory(nativePtr, enumPrimitiveType)
|
type.isPrimitiveType() -> readPrimitiveFromMemory(fieldPointer, type)
|
||||||
return convertIntegralToEnum(readMemory, accessor.returnType)
|
type.isCPointer() -> readPointerFromMemory(fieldPointer)
|
||||||
|
type.isNativePointed() -> readPointed(fieldPointer)
|
||||||
|
else -> error("Cannot get field type: ${type.getClass()?.name}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
accessor.isSetter -> {
|
accessor.isSetter -> {
|
||||||
val arg = callSite.getValueArgument(0)!!
|
val value = callSite.getValueArgument(0)!!
|
||||||
val valueToWrite = convertEnumToIntegral(arg)
|
val type = accessor.valueParameters[0].type
|
||||||
writePrimitiveToMemory(nativePtr, valueToWrite)
|
when {
|
||||||
|
type.isCEnumType() -> writeEnumValueToMemory(fieldPointer, value)
|
||||||
|
type.isPrimitiveType() -> writePrimitiveToMemory(fieldPointer, value)
|
||||||
|
type.isCPointer() -> writePointerToMemory(fieldPointer, value)
|
||||||
|
else -> error("Cannot set field of type ${type.getClass()?.name}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> error("")
|
else -> error("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.writeBits(
|
||||||
|
base: IrExpression,
|
||||||
|
offset: Long,
|
||||||
|
size: Int,
|
||||||
|
value: IrExpression
|
||||||
|
): IrExpression {
|
||||||
|
val integralValue = when {
|
||||||
|
value.type.isCEnumType() -> convertEnumToIntegral(value)
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
val targetType = symbols.writeBits.owner.valueParameters.last().type
|
||||||
|
val valueToWrite = castPrimitiveIfNeeded(integralValue, targetType)
|
||||||
|
return with(builder) {
|
||||||
|
irCall(symbols.writeBits).also {
|
||||||
|
it.putValueArgument(0, base)
|
||||||
|
it.putValueArgument(1, irLong(offset))
|
||||||
|
it.putValueArgument(2, irInt(size))
|
||||||
|
it.putValueArgument(3, valueToWrite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.readBits(
|
||||||
|
base: IrExpression,
|
||||||
|
offset: Long,
|
||||||
|
size: Int,
|
||||||
|
type: IrType
|
||||||
|
): IrExpression {
|
||||||
|
val isSigned = when {
|
||||||
|
type.isCEnumType() ->
|
||||||
|
!type.getCEnumPrimitiveType().isUnsigned()
|
||||||
|
else ->
|
||||||
|
!type.isUnsigned()
|
||||||
|
}
|
||||||
|
val integralValue = with (builder) {
|
||||||
|
irCall(symbols.readBits).also {
|
||||||
|
it.putValueArgument(0, base)
|
||||||
|
it.putValueArgument(1, irLong(offset))
|
||||||
|
it.putValueArgument(2, irInt(size))
|
||||||
|
it.putValueArgument(3, irBoolean(isSigned))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
type.isCEnumType() -> convertIntegralToEnum(integralValue, type)
|
||||||
|
else -> castPrimitiveIfNeeded(integralValue, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InteropCallContext.generateBitFieldAccess(callSite: IrCall): IrExpression {
|
||||||
|
val accessor = callSite.symbol.owner
|
||||||
|
val bitField = accessor.getAnnotation(RuntimeNames.cStructBitField)!!
|
||||||
|
val offset = (bitField.getValueArgument(0) as IrConst<Long>).value
|
||||||
|
val size = (bitField.getValueArgument(1) as IrConst<Int>).value
|
||||||
|
val base = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
|
||||||
|
it.dispatchReceiver = callSite.dispatchReceiver!!
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
accessor.isSetter -> {
|
||||||
|
val argument = callSite.getValueArgument(0)!!
|
||||||
|
writeBits(base, offset, size, argument)
|
||||||
|
}
|
||||||
|
accessor.isGetter -> {
|
||||||
|
val type = accessor.returnType
|
||||||
|
readBits(base, offset, size, type)
|
||||||
|
}
|
||||||
|
else -> error("Unexpected function: ${accessor.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -901,7 +901,7 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
|
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
tryGenerateEnumVarValueAccess(expression, symbols, builder)?.let { return it }
|
tryGenerateInteropMemberAccess(expression, symbols, builder)?.let { return it }
|
||||||
|
|
||||||
val intrinsicType = tryGetIntrinsicType(expression)
|
val intrinsicType = tryGetIntrinsicType(expression)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user