[IR] Refactored bridges building

As a side effect fixes KT-27534 and KT-30284
This commit is contained in:
Igor Chevdar
2020-06-23 12:50:33 +05:00
parent 323ac4bf5b
commit 180424f0b0
5 changed files with 163 additions and 86 deletions
@@ -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<IrClass, IrField>()
private val bridgesDescriptors = mutableMapOf<Pair<IrSimpleFunction, BridgeDirections>, IrSimpleFunction>()
private val loweredEnums = mutableMapOf<IrClass, LoweredEnum>()
private val ordinals = mutableMapOf<ClassDescriptor, Map<ClassDescriptor, Int>>()
private data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
private val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
val loweredInlineFunctions = mutableSetOf<IrFunction>()
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 }
}
@@ -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<BridgeDirection>) {
constructor(parametersCount: Int): this(Array<BridgeDirection>(parametersCount + 3, { BridgeDirection.NOT_NEEDED }))
internal class BridgeDirections(private val array: Array<BridgeDirection>) {
constructor(irFunction: IrSimpleFunction, overriddenFunction: IrSimpleFunction)
: this(Array<BridgeDirection>(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<BridgeDirection>) {
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<IrSimpleFunction>
@@ -159,19 +224,15 @@ val IrSimpleFunction.allOverriddenFunctions: Set<IrSimpleFunction>
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
@@ -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)
}
}
+4
View File
@@ -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"
}
@@ -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))
}