From 180424f0b034272371df750eb704461368a2365d Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 23 Jun 2020 12:50:33 +0500 Subject: [PATCH] [IR] Refactored bridges building As a side effect fixes KT-27534 and KT-30284 --- .../jetbrains/kotlin/backend/konan/Context.kt | 61 +++---- .../konan/descriptors/DescriptorUtils.kt | 157 ++++++++++++------ .../kotlin/backend/konan/llvm/DataLayout.kt | 4 +- backend.native/tests/build.gradle | 4 + .../tests/codegen/bridges/nativePointed.kt | 23 +++ 5 files changed, 163 insertions(+), 86 deletions(-) create mode 100644 backend.native/tests/codegen/bridges/nativePointed.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 05f13e7ab84..35a5e97a986 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -24,10 +24,6 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl @@ -47,6 +43,9 @@ import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.konan.library.KonanLibraryLayout import org.jetbrains.kotlin.library.SerializedIrModule @@ -59,10 +58,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal internal class SpecialDeclarationsFactory(val context: Context) { private val enumSpecialDeclarationsFactory by lazy { EnumSpecialDeclarationsFactory(context) } private val outerThisFields = mutableMapOf() - private val bridgesDescriptors = mutableMapOf, IrSimpleFunction>() private val loweredEnums = mutableMapOf() private val ordinals = mutableMapOf>() + private data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections) + + private val bridges = mutableMapOf() + val loweredInlineFunctions = mutableSetOf() object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS : @@ -119,21 +121,20 @@ internal class SpecialDeclarationsFactory(val context: Context) { assert(overriddenFunction.needBridge) { "Function ${irFunction.descriptor} is not needed in a bridge to call overridden function ${overriddenFunction.overriddenFunction.descriptor}" } - val bridgeDirections = overriddenFunction.bridgeDirections - return bridgesDescriptors.getOrPut(irFunction to bridgeDirections) { - createBridge(irFunction, bridgeDirections) - } + val key = BridgeKey(irFunction, overriddenFunction.bridgeDirections) + return bridges.getOrPut(key) { createBridge(key) } } - private fun createBridge(function: IrSimpleFunction, - bridgeDirections: BridgeDirections): IrSimpleFunction = WrappedSimpleFunctionDescriptor().let { descriptor -> + private fun createBridge(key: BridgeKey): IrSimpleFunction = WrappedSimpleFunctionDescriptor().let { descriptor -> + val (function, bridgeDirections) = key val startOffset = function.startOffset val endOffset = function.endOffset - val returnType = when (bridgeDirections.array[0]) { - BridgeDirection.TO_VALUE_TYPE, - BridgeDirection.NOT_NEEDED -> function.returnType - BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyNType - } + + fun BridgeDirection.type() = + if (this.kind == BridgeDirectionKind.NONE) + null + else this.irClass?.defaultType ?: context.irBuiltIns.anyNType + IrFunctionImpl( startOffset, endOffset, DECLARATION_ORIGIN_BRIDGE_METHOD(function), @@ -145,7 +146,7 @@ internal class SpecialDeclarationsFactory(val context: Context) { isExternal = false, isTailrec = false, isSuspend = function.isSuspend, - returnType = returnType, + returnType = bridgeDirections.returnDirection.type() ?: function.returnType, isExpect = false, isFakeOverride = false, isOperator = false, @@ -155,30 +156,16 @@ internal class SpecialDeclarationsFactory(val context: Context) { descriptor.bind(bridge) parent = function.parent - val dispatchReceiver = when (bridgeDirections.array[1]) { - BridgeDirection.TO_VALUE_TYPE -> function.dispatchReceiverParameter!! - BridgeDirection.NOT_NEEDED -> function.dispatchReceiverParameter - BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyClass.owner.thisReceiver!! + dispatchReceiverParameter = function.dispatchReceiverParameter?.let { + it.copyTo(bridge, type = bridgeDirections.dispatchReceiverDirection.type() ?: it.type) } - - val extensionReceiver = when (bridgeDirections.array[2]) { - BridgeDirection.TO_VALUE_TYPE -> function.extensionReceiverParameter!! - BridgeDirection.NOT_NEEDED -> function.extensionReceiverParameter - BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyClass.owner.thisReceiver!! + extensionReceiverParameter = function.extensionReceiverParameter?.let { + it.copyTo(bridge, type = bridgeDirections.extensionReceiverDirection.type() ?: it.type) } - - val valueParameterTypes = function.valueParameters.mapIndexed { index, valueParameter -> - when (bridgeDirections.array[index + 3]) { - BridgeDirection.TO_VALUE_TYPE -> valueParameter.type - BridgeDirection.NOT_NEEDED -> valueParameter.type - BridgeDirection.FROM_VALUE_TYPE -> context.irBuiltIns.anyNType - } + valueParameters += function.valueParameters.map { + it.copyTo(bridge, type = bridgeDirections.parameterDirectionAt(it.index).type() ?: it.type) } - dispatchReceiverParameter = dispatchReceiver?.copyTo(bridge) - extensionReceiverParameter = extensionReceiver?.copyTo(bridge) - valueParameters += function.valueParameters.map { it.copyTo(bridge, type = valueParameterTypes[it.index]) } - typeParameters += function.typeParameters.map { parameter -> parameter.copyToWithoutSuperTypes(bridge).also { it.superTypes += parameter.superTypes } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 789d6dc450a..8e86cd3e360 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.ir.* +import org.jetbrains.kotlin.backend.konan.llvm.isVoidAsReturnType import org.jetbrains.kotlin.backend.konan.llvm.longName import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -15,10 +16,8 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.isUnit +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.constants.StringValue @@ -75,55 +74,117 @@ internal val IrClass.isArrayWithFixedSizeItems: Boolean fun IrClass.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT -internal fun IrFunction.hasValueTypeAt(index: Int): Boolean { - when (index) { - 0 -> return !isSuspend && returnType.let { (it.isInlinedNative() || it.isUnit()) } - 1 -> return dispatchReceiverParameter.let { it != null && it.type.isInlinedNative() } - 2 -> return extensionReceiverParameter.let { it != null && it.type.isInlinedNative() } - else -> return this.valueParameters[index - 3].type.isInlinedNative() +private enum class TypeKind { + ABSENT, + VOID, + VALUE_TYPE, + REFERENCE +} + +private data class TypeWithKind(val irType: IrType?, val kind: TypeKind) { + companion object { + fun fromType(irType: IrType?) = when { + irType == null -> TypeWithKind(null, TypeKind.ABSENT) + irType.isInlinedNative() -> TypeWithKind(irType, TypeKind.VALUE_TYPE) + else -> TypeWithKind(irType, TypeKind.REFERENCE) + } } } -internal fun IrFunction.hasReferenceAt(index: Int): Boolean { - when (index) { - 0 -> return isSuspend || returnType.let { !it.isInlinedNative() && !it.isUnit() } - 1 -> return dispatchReceiverParameter.let { it != null && !it.type.isInlinedNative() } - 2 -> return extensionReceiverParameter.let { it != null && !it.type.isInlinedNative() } - else -> return !this.valueParameters[index - 3].type.isInlinedNative() +private fun IrFunction.typeWithKindAt(index: ParameterIndex) = when (index) { + ParameterIndex.RETURN_INDEX -> when { + isSuspend -> TypeWithKind(null, TypeKind.REFERENCE) + returnType.isVoidAsReturnType() -> TypeWithKind(returnType, TypeKind.VOID) + else -> TypeWithKind.fromType(returnType) + } + ParameterIndex.DISPATCH_RECEIVER_INDEX -> TypeWithKind.fromType(dispatchReceiverParameter?.type) + ParameterIndex.EXTENSION_RECEIVER_INDEX -> TypeWithKind.fromType(extensionReceiverParameter?.type) + else -> TypeWithKind.fromType(this.valueParameters[index.unmap()].type) +} + +private fun IrFunction.needBridgeToAt(target: IrFunction, index: ParameterIndex) + = bridgeDirectionToAt(target, index).kind != BridgeDirectionKind.NONE + +@Suppress("EXPERIMENTAL_FEATURE_WARNING") +private inline class ParameterIndex(val index: Int) { + companion object { + val RETURN_INDEX = ParameterIndex(0) + val DISPATCH_RECEIVER_INDEX = ParameterIndex(1) + val EXTENSION_RECEIVER_INDEX = ParameterIndex(2) + + fun map(index: Int) = ParameterIndex(index + 3) + + fun allParametersCount(irFunction: IrFunction) = irFunction.valueParameters.size + 3 + + inline fun forEachIndex(irFunction: IrFunction, block: (ParameterIndex) -> Unit) = + (0 until allParametersCount(irFunction)).forEach { block(ParameterIndex(it)) } + } + + fun unmap() = index - 3 +} + +internal fun IrFunction.needBridgeTo(target: IrFunction): Boolean { + ParameterIndex.forEachIndex(this) { + if (needBridgeToAt(target, it)) return true + } + return false +} + +internal enum class BridgeDirectionKind { + NONE, + BOX, + UNBOX +} + +internal data class BridgeDirection(val irClass: IrClass?, val kind: BridgeDirectionKind) { + companion object { + val NONE = BridgeDirection(null, BridgeDirectionKind.NONE) } } -private fun IrFunction.needBridgeToAt(target: IrFunction, index: Int) - = hasValueTypeAt(index) xor target.hasValueTypeAt(index) - -internal fun IrFunction.needBridgeTo(target: IrFunction) - = (0..this.valueParameters.size + 2).any { needBridgeToAt(target, it) } - -internal enum class BridgeDirection { - NOT_NEEDED, - FROM_VALUE_TYPE, - TO_VALUE_TYPE +private fun IrFunction.bridgeDirectionToAt(overriddenFunction: IrFunction, index: ParameterIndex): BridgeDirection { + val kind = typeWithKindAt(index).kind + val (irClass, otherKind) = overriddenFunction.typeWithKindAt(index) + return if (otherKind == kind) + BridgeDirection.NONE + else when (kind) { + TypeKind.VOID, TypeKind.REFERENCE -> BridgeDirection(irClass?.erasure(), BridgeDirectionKind.UNBOX) + TypeKind.VALUE_TYPE -> BridgeDirection( + irClass?.erasure().takeIf { otherKind == TypeKind.VOID } /* Otherwise erase to [Any?] */, + BridgeDirectionKind.BOX) + TypeKind.ABSENT -> error("TypeKind.ABSENT should be on both sides") + } } -private fun IrFunction.bridgeDirectionToAt(target: IrFunction, index: Int) - = when { - hasValueTypeAt(index) && target.hasReferenceAt(index) -> BridgeDirection.FROM_VALUE_TYPE - hasReferenceAt(index) && target.hasValueTypeAt(index) -> BridgeDirection.TO_VALUE_TYPE - else -> BridgeDirection.NOT_NEEDED +private tailrec fun IrType.erasure(): IrClass = + when (val classifier = classifierOrFail) { + is IrClassSymbol -> classifier.owner + is IrTypeParameterSymbol -> classifier.owner.superTypes.first().erasure() + else -> error(classifier) } -internal class BridgeDirections(val array: Array) { - constructor(parametersCount: Int): this(Array(parametersCount + 3, { BridgeDirection.NOT_NEEDED })) +internal class BridgeDirections(private val array: Array) { + constructor(irFunction: IrSimpleFunction, overriddenFunction: IrSimpleFunction) + : this(Array(ParameterIndex.allParametersCount(irFunction)) { + irFunction.bridgeDirectionToAt(overriddenFunction, ParameterIndex(it)) + }) - fun allNotNeeded(): Boolean = array.all { it == BridgeDirection.NOT_NEEDED } + fun allNotNeeded(): Boolean = array.all { it.kind == BridgeDirectionKind.NONE } + + private fun getDirectionAt(index: ParameterIndex) = array[index.index] + + val returnDirection get() = getDirectionAt(ParameterIndex.RETURN_INDEX) + val dispatchReceiverDirection get() = getDirectionAt(ParameterIndex.DISPATCH_RECEIVER_INDEX) + val extensionReceiverDirection get() = getDirectionAt(ParameterIndex.EXTENSION_RECEIVER_INDEX) + fun parameterDirectionAt(index: Int) = getDirectionAt(ParameterIndex.map(index)) override fun toString(): String { val result = StringBuilder() array.forEach { - result.append(when (it) { - BridgeDirection.FROM_VALUE_TYPE -> 'U' // unbox - BridgeDirection.TO_VALUE_TYPE -> 'B' // box - BridgeDirection.NOT_NEEDED -> 'N' // none + result.append(when (it.kind) { + BridgeDirectionKind.BOX -> 'B' + BridgeDirectionKind.UNBOX -> 'U' + BridgeDirectionKind.NONE -> 'N' }) } return result.toString() @@ -139,9 +200,13 @@ internal class BridgeDirections(val array: Array) { override fun hashCode(): Int { var result = 0 - array.forEach { result = result * 31 + it.ordinal } + array.forEach { result = result * 31 + it.hashCode() } return result } + + companion object { + fun none(irFunction: IrSimpleFunction) = BridgeDirections(irFunction, irFunction) + } } val IrSimpleFunction.allOverriddenFunctions: Set @@ -159,19 +224,15 @@ val IrSimpleFunction.allOverriddenFunctions: Set return result } -internal fun IrSimpleFunction.bridgeDirectionsTo( - overriddenDescriptor: IrSimpleFunction -): BridgeDirections { - val ourDirections = BridgeDirections(this.valueParameters.size) - for (index in ourDirections.array.indices) - ourDirections.array[index] = this.bridgeDirectionToAt(overriddenDescriptor, index) +internal fun IrSimpleFunction.bridgeDirectionsTo(overriddenFunction: IrSimpleFunction): BridgeDirections { + val ourDirections = BridgeDirections(this, overriddenFunction) val target = this.target if (!this.isReal && modality != Modality.ABSTRACT - && target.overrides(overriddenDescriptor) - && ourDirections == target.bridgeDirectionsTo(overriddenDescriptor)) { + && target.overrides(overriddenFunction) + && ourDirections == target.bridgeDirectionsTo(overriddenFunction)) { // Bridge is inherited from superclass. - return BridgeDirections(this.valueParameters.size) + return BridgeDirections.none(this) } return ourDirections diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt index b9da8cafac9..0481efd64b0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt @@ -33,9 +33,11 @@ internal fun RuntimeAware.getLLVMType(type: IrType): LLVMTypeRef = internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) = getLlvmType(type.primitiveBinaryType) +internal fun IrType.isVoidAsReturnType() = isUnit() || isNothing() + internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef { return when { - type.isUnit() || type.isNothing() -> voidType + type.isVoidAsReturnType() -> voidType else -> getLLVMType(type) } } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 1bdaf4bcb81..bd2c277e2d1 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -1459,6 +1459,10 @@ task bridges_special(type: KonanLocalTest) { source = "codegen/bridges/special.kt" } +task bridges_nativePointed(type: KonanLocalTest) { + source = "codegen/bridges/nativePointed.kt" +} + task returnTypeSignature(type: KonanLocalTest) { source = "codegen/bridges/returnTypeSignature.kt" } diff --git a/backend.native/tests/codegen/bridges/nativePointed.kt b/backend.native/tests/codegen/bridges/nativePointed.kt new file mode 100644 index 00000000000..00151feb2a4 --- /dev/null +++ b/backend.native/tests/codegen/bridges/nativePointed.kt @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package codegen.bridges.nativePointed + +import kotlinx.cinterop.* +import kotlin.test.* + +abstract class C { + abstract fun foo(x: Int): CPointer<*>? +} + +class CImpl : C() { + override fun foo(x: Int) = null +} + +@Test fun runTest() { + val c: C = CImpl() + assertNull(c.foo(42)) +} +