diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index 922283287cb..fc2f3c73d05 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -51,5 +51,9 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_INLINE) { //FunctionInlining(context).inline(irFile) } + + phaser.phase(KonanPhase.LOWER_INTEROP) { + InteropLowering(context).runOnFilePostfix(irFile) + } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index f5ba9e52da0..8ab4548e3b7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -18,6 +18,7 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering"), /* ... ... */ LOWER_CALLABLES("Callable references Lowering"), /* ... ... */ LOWER_INLINE("Functions inlining"), + /* ... ... */ LOWER_INTEROP("Interop lowering"), /* ... ... */ AUTOBOX("Autoboxing of primitive types"), /* ... ... */ LOWER_ENUMS("Enum classes lowering"), /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt new file mode 100644 index 00000000000..ee6bd0a9091 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt @@ -0,0 +1,201 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FunctionLoweringPass +import org.jetbrains.kotlin.backend.common.lower.at +import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.ValueType +import org.jetbrains.kotlin.backend.konan.isRepresentedAs +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.builders.IrBuilder +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall +import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf + +/** + * Lowers some interop intrinsic calls. + */ +internal class InteropLowering(val context: Context) : FunctionLoweringPass { + override fun lower(irFunction: IrFunction) { + val transformer = InteropTransformer(context, irFunction.descriptor) + irFunction.transformChildrenVoid(transformer) + } +} + +private class InteropTransformer(val context: Context, val function: FunctionDescriptor) : IrElementTransformerVoid() { + + val builder = context.createFunctionIrBuilder(function) + val interop = context.interopBuiltIns + + private fun MemberScope.getSingleContributedFunction(name: String, + predicate: (SimpleFunctionDescriptor) -> Boolean) = + this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).single(predicate) + + private fun IrBuilder.irGetObject(descriptor: ClassDescriptor): IrExpression { + return IrGetObjectValueImpl(startOffset, endOffset, descriptor.defaultType, descriptor) + } + + private fun IrBuilder.typeOf(descriptor: ClassDescriptor): IrExpression { + val companionObject = descriptor.companionObjectDescriptor ?: + error("native variable class $descriptor must have the companion object") + // TODO: add more checks and produce the compile error instead of exception. + + return irGetObject(companionObject) + } + + private fun IrBuilder.typeOf(type: KotlinType): IrExpression? { + val descriptor = TypeUtils.getClassDescriptor(type) ?: return null + return typeOf(descriptor) + } + + private fun KotlinType.findOverride(property: PropertyDescriptor): PropertyDescriptor { + val result = this.memberScope.getContributedVariables(property.name, NoLookupLocation.FROM_BACKEND).single() + assert (OverridingUtil.overrides(result, property)) + return result + } + + private fun IrBuilderWithScope.sizeOf(typeObject: IrExpression): IrExpression { + val sizeProperty = typeObject.type.findOverride(interop.variableTypeSize) + return irGet(typeObject, sizeProperty) + } + + private fun IrBuilderWithScope.sizeOf(type: KotlinType): IrExpression? { + val typeObject = typeOf(type) ?: return null + return sizeOf(typeObject) + } + + private fun IrBuilderWithScope.alignOf(typeObject: IrExpression): IrExpression { + val alignProperty = typeObject.type.findOverride(interop.variableTypeAlign) + return irGet(typeObject, alignProperty) + } + + private fun IrBuilderWithScope.alignOf(type: KotlinType): IrExpression? { + val typeObject = typeOf(type) ?: return null + return alignOf(typeObject) + } + + private fun IrBuilderWithScope.arrayGet(array: IrExpression, index: IrExpression): IrExpression? { + val elementSize = sizeOf(array.type.arguments.single().type) ?: return null + + val offset = times(elementSize, index) + + return irCall(interop.memberAt).apply { + extensionReceiver = array + putValueArgument(0, offset) + } + } + + private fun IrBuilderWithScope.times(left: IrExpression, right: IrExpression): IrCall { + val times = left.type.memberScope.getSingleContributedFunction("times") { + right.type.isSubtypeOf(it.valueParameters.single().type) + } + + return irCall(times).apply { + dispatchReceiver = left + putValueArgument(0, right) + } + } + + private fun IrBuilderWithScope.alloc(placement: IrExpression, size: IrExpression, align: IrExpression): IrExpression { + val alloc = placement.type.memberScope.getSingleContributedFunction("alloc") { + size.type.isSubtypeOf(it.valueParameters[0]!!.type) && + align.type.isSubtypeOf(it.valueParameters[1]!!.type) + } + + return irCall(alloc).apply { + dispatchReceiver = placement + putValueArgument(0, size) + putValueArgument(1, align) + } + } + + private fun IrBuilderWithScope.allocArray(placement: IrExpression, + elementType: KotlinType, + length: IrExpression + ): IrExpression? { + + val elementSize = sizeOf(elementType) ?: return null + val size = times(elementSize, length) + val align = alignOf(elementType) ?: return null + + return alloc(placement, size, align) + } + + private fun IrBuilderWithScope.alloc(placement: IrExpression, type: KotlinType): IrExpression? { + val size = sizeOf(type) ?: return null + val align = alignOf(type) ?: return null + + return alloc(placement, size, align) + } + + override fun visitCall(expression: IrCall): IrExpression { + + expression.transformChildrenVoid(this) + builder.at(expression) + val descriptor = expression.descriptor.original + + if (descriptor is ClassConstructorDescriptor) { + val type = descriptor.constructedClass.defaultType + if (type.isRepresentedAs(ValueType.C_POINTER) || type.isRepresentedAs(ValueType.NATIVE_POINTED)) { + return expression.getValueArgument(0)!! + } + } + + if (descriptor == interop.nativePointedRawPtrGetter || + OverridingUtil.overrides(descriptor, interop.nativePointedRawPtrGetter)) { + + return expression.dispatchReceiver!! + } + + return when (descriptor) { + interop.cPointerRawValue.getter -> expression.dispatchReceiver!! + + interop.interpretPointed -> expression.getValueArgument(0)!! + + interop.arrayGetByIntIndex, interop.arrayGetByLongIndex -> { + val array = expression.extensionReceiver!! + val index = expression.getValueArgument(0)!! + builder.arrayGet(array, index) ?: expression + } + + interop.allocUninitializedArrayWithIntLength, interop.allocUninitializedArrayWithLongLength -> { + val placement = expression.extensionReceiver!! + val elementType = expression.type.arguments.single().type + val length = expression.getValueArgument(0)!! + builder.allocArray(placement, elementType, length) ?: expression + } + + interop.allocVariable -> { + val placement = expression.extensionReceiver!! + val type = expression.getSingleTypeArgument() + builder.alloc(placement, type) ?: expression + } + + interop.typeOf -> { + val type = expression.getSingleTypeArgument() + builder.typeOf(type) ?: expression + } + + else -> expression + } + } + + private fun IrCall.getSingleTypeArgument(): KotlinType { + val typeParameter = descriptor.original.typeParameters.single() + return getTypeArgument(typeParameter)!! + } +} \ No newline at end of file