[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 exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
||||
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 objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||
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.isULong() = this.isUnsigned(UnsignedType.ULONG)
|
||||
|
||||
private fun IrType.isCEnumType(): Boolean {
|
||||
internal 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
|
||||
|
||||
+3
@@ -198,6 +198,9 @@ internal class KonanSymbols(
|
||||
|
||||
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
|
||||
|
||||
val readBits = interopFunction("readBits")
|
||||
val writeBits = interopFunction("writeBits")
|
||||
|
||||
val objCExportTrapOnUndeclaredException =
|
||||
symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions(
|
||||
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.jvm.ir.propertyIfAccessor
|
||||
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.ir.KonanSymbols
|
||||
import org.jetbrains.kotlin.backend.konan.ir.superClasses
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irGetObject
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.isPropertyAccessor
|
||||
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.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
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(
|
||||
val symbols: KonanSymbols,
|
||||
val builder: IrBuilderWithScope
|
||||
)
|
||||
) {
|
||||
fun IrType.isCPointer(): Boolean = this.classOrNull == symbols.interopCPointer
|
||||
|
||||
fun IrType.isNativePointed(): Boolean = isSubtypeOfClass(symbols.nativePointed)
|
||||
}
|
||||
|
||||
private inline fun <T> generateInteropCall(
|
||||
symbols: KonanSymbols,
|
||||
@@ -154,40 +162,192 @@ private fun InteropCallContext.convertIntegralToEnum(value: IrExpression, enumTy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if the given call-site is not an accessor for T.value, T : CEnumVar.
|
||||
* Otherwise, generates:
|
||||
*
|
||||
* get() => byValue(this.reinterpret().value)
|
||||
*
|
||||
* set(value) => this.reinterpret().value = value.value
|
||||
private fun IrType.getCEnumPrimitiveType(): IrType {
|
||||
assert(this.isCEnumType())
|
||||
val enumClass = this.getClass()!!
|
||||
return enumClass.properties.single { it.name.asString() == "value" }
|
||||
.getter!!.returnType
|
||||
}
|
||||
|
||||
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,
|
||||
symbols: KonanSymbols,
|
||||
builder: IrBuilderWithScope
|
||||
): IrExpression? = if (isEnumVarValueAccessor(callSite.symbol.owner, symbols))
|
||||
generateInteropCall(symbols, builder) { generateEnumVarValueAccess(callSite) }
|
||||
else null
|
||||
): IrExpression? = when {
|
||||
isEnumVarValueAccessor(callSite.symbol.owner, symbols) ->
|
||||
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 {
|
||||
val accessor = callSite.symbol.owner
|
||||
val nativePtr = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
|
||||
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 {
|
||||
accessor.isGetter -> {
|
||||
val enumClass = accessor.returnType.getClass()!!
|
||||
val enumPrimitiveType = enumClass.properties.single { it.name.asString() == "value" }
|
||||
.getter!!.returnType
|
||||
val readMemory = readPrimitiveFromMemory(nativePtr, enumPrimitiveType)
|
||||
return convertIntegralToEnum(readMemory, accessor.returnType)
|
||||
val type = accessor.returnType
|
||||
when {
|
||||
type.isCEnumType() -> readEnumValueFromMemory(fieldPointer, type)
|
||||
type.isPrimitiveType() -> readPrimitiveFromMemory(fieldPointer, type)
|
||||
type.isCPointer() -> readPointerFromMemory(fieldPointer)
|
||||
type.isNativePointed() -> readPointed(fieldPointer)
|
||||
else -> error("Cannot get field type: ${type.getClass()?.name}")
|
||||
}
|
||||
}
|
||||
accessor.isSetter -> {
|
||||
val arg = callSite.getValueArgument(0)!!
|
||||
val valueToWrite = convertEnumToIntegral(arg)
|
||||
writePrimitiveToMemory(nativePtr, valueToWrite)
|
||||
val value = callSite.getValueArgument(0)!!
|
||||
val type = accessor.valueParameters[0].type
|
||||
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("")
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
tryGenerateEnumVarValueAccess(expression, symbols, builder)?.let { return it }
|
||||
tryGenerateInteropMemberAccess(expression, symbols, builder)?.let { return it }
|
||||
|
||||
val intrinsicType = tryGetIntrinsicType(expression)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user