* - bridges building
- bridges support in vtable and methods table

* FunctionDescriptor.signature fix + some assertions

* tests

* refactoring

* refactoring

* removed debug output

* refactoring

* review fixes

* implemented bridge construction for parameters

* tests

* refactoring

* removed custom box/unbox

* added bridge for extension receiver
This commit is contained in:
Igor Chevdar
2017-02-16 15:26:57 +05:00
committed by GitHub
parent 70cb8ced5a
commit c746da3ce4
29 changed files with 837 additions and 95 deletions
@@ -3,25 +3,24 @@ package org.jetbrains.kotlin.backend.konan
import llvm.LLVMDumpModule
import llvm.LLVMModuleRef
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.Ir
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import java.lang.System.out
internal class SpecialDescriptorsFactory {
internal class SpecialDescriptorsFactory(val context: Context) {
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
private val bridgesDescriptors = mutableMapOf<FunctionDescriptor, FunctionDescriptor>()
fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor =
if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor")
@@ -34,13 +33,75 @@ internal class SpecialDescriptorsFactory {
false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE,
false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver)
}
fun getBridgeDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor {
return bridgesDescriptors.getOrPut(descriptor) {
SimpleFunctionDescriptorImpl.create(
descriptor.containingDeclaration,
Annotations.EMPTY,
("<bridge-to>" + descriptor.functionName).synthesizedName,
CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE).apply {
initializeBridgeDescriptor(this, descriptor)
}
}
}
private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl, descriptor: FunctionDescriptor) {
val bridgeDirections = descriptor.bridgeDirections
if (bridgeDirections.allNotNeeded())
throw AssertionError("Function $descriptor is not needed in a bridge")
val returnType = when (bridgeDirections[0]) {
BridgeDirection.TO_VALUE_TYPE -> descriptor.returnType!!
BridgeDirection.NOT_NEEDED -> descriptor.returnType
BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType
}
val extensionReceiverType = when (bridgeDirections[1]) {
BridgeDirection.TO_VALUE_TYPE -> descriptor.extensionReceiverParameter!!.type
BridgeDirection.NOT_NEEDED -> descriptor.extensionReceiverParameter?.type
BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType
}
bridgeDescriptor.initialize(
extensionReceiverType,
descriptor.dispatchReceiverParameter,
descriptor.typeParameters,
descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor ->
when (bridgeDirections[index + 2]) {
BridgeDirection.TO_VALUE_TYPE -> valueParameterDescriptor
BridgeDirection.NOT_NEEDED -> valueParameterDescriptor
BridgeDirection.FROM_VALUE_TYPE -> ValueParameterDescriptorImpl(
valueParameterDescriptor.containingDeclaration,
null,
index,
Annotations.EMPTY,
valueParameterDescriptor.name,
context.builtIns.anyType,
valueParameterDescriptor.declaresDefaultValue(),
valueParameterDescriptor.isCrossinline,
valueParameterDescriptor.isNoinline,
valueParameterDescriptor.varargElementType,
SourceElement.NO_SOURCE)
}
},
returnType,
descriptor.modality,
Visibilities.PRIVATE)
}
}
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
var moduleDescriptor: ModuleDescriptor? = null
val specialDescriptorsFactory = SpecialDescriptorsFactory()
val specialDescriptorsFactory = SpecialDescriptorsFactory(this)
private val vtableBuilders = mutableMapOf<ClassDescriptor, ClassVtablesBuilder>()
fun getVtableBuilder(classDescriptor: ClassDescriptor) = vtableBuilders.getOrPut(classDescriptor) {
ClassVtablesBuilder(classDescriptor, this)
}
// TODO: make lateinit?
var irModule: IrModuleFragment? = null
@@ -55,6 +55,9 @@ internal class KonanLower(val context: Context) {
phaser.phase(KonanPhase.LOWER_CALLABLES) {
CallableReferenceLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.BRIDGES_BUILDING) {
BridgesBuilding(context).lower(irFile)
}
phaser.phase(KonanPhase.AUTOBOX) {
Autoboxing(context).lower(irFile)
}
@@ -24,6 +24,7 @@ enum class KonanPhase(val description: String,
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
/* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"),
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering"),
/* ... ... */ BRIDGES_BUILDING("Bridges building"),
/* ... */ BITCODE("LLVM BitCode Generation"),
/* ... ... */ RTTI("RTTI Generation"),
/* ... ... */ CODEGEN("Code Generation"),
@@ -1,7 +1,7 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
/**
@@ -69,4 +69,6 @@ tailrec fun KotlinType.notNullableIsRepresentedAs(valueType: ValueType): Boolean
// See comment in [isRepresentedAs].
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
return firstSupertype.notNullableIsRepresentedAs(valueType)
}
}
internal fun KotlinType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) }
@@ -0,0 +1,93 @@
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor) {
val overriddenDescriptor = overriddenDescriptor.original
val needBridge: Boolean
get() {
if (descriptor.modality == Modality.ABSTRACT) return false
return descriptor.needBridgeTo(overriddenDescriptor)
|| descriptor.target.needBridgeTo(overriddenDescriptor)
}
fun trivialOverride() = descriptor.original == overriddenDescriptor
override fun toString(): String {
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
}
}
internal class ClassVtablesBuilder(val classDescriptor: ClassDescriptor, val context: Context) {
val vtableEntries: List<OverriddenFunctionDescriptor> by lazy {
assert(!classDescriptor.isInterface)
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDescriptor)) {
emptyList()
} else {
context.getVtableBuilder(classDescriptor.getSuperClassOrAny()).vtableEntries
}
val methods = classDescriptor.contributedMethods
val newVtableSlots = mutableListOf<OverriddenFunctionDescriptor>()
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
val overridingMethod = methods.singleOrNull { OverridingUtil.overrides(it, superMethod.descriptor) }
if (overridingMethod == null) {
superMethod
} else {
if (!superMethod.trivialOverride())
newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, superMethod.descriptor))
newVtableSlots.add(OverriddenFunctionDescriptor(overridingMethod, overridingMethod))
OverriddenFunctionDescriptor(overridingMethod, superMethod.overriddenDescriptor)
}
}
methods.filterNot { method -> inheritedVtableSlots.any { it.descriptor == method } } // Find newly defined methods.
.mapTo(newVtableSlots) { OverriddenFunctionDescriptor(it, it) }
val list = inheritedVtableSlots + newVtableSlots.filter { it.descriptor.isOverridable }.sortedBy {
it.overriddenDescriptor.functionName.localHash.value
}
list
}
fun vtableIndex(function: FunctionDescriptor): Int {
val target = function.target
val index = vtableEntries.indexOfFirst { it.overriddenDescriptor == target }
if (index < 0) throw Error(function.toString() + " not in vtable of " + this.toString())
return index
}
private val ClassDescriptor.contributedMethodsWithOverridden: List<OverriddenFunctionDescriptor>
get() {
return contributedMethods.flatMap { method ->
method.allOverriddenDescriptors.map { OverriddenFunctionDescriptor(method, it) }
}.distinctBy {
Triple(it.overriddenDescriptor.functionName, it.descriptor, it.needBridge)
}.sortedBy {
it.overriddenDescriptor.functionName.localHash.value
}
}
val methodTableEntries: List<OverriddenFunctionDescriptor> by lazy {
assert(!classDescriptor.isAbstract())
classDescriptor.contributedMethodsWithOverridden.filter {
// We check that either method is open, or one of declarations it overrides
// is open.
it.overriddenDescriptor.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it.overriddenDescriptor).any { it.isOverridable }
}
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
}
@@ -3,27 +3,23 @@ package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitution
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -46,14 +42,14 @@ internal val ClassDescriptor.implementedInterfaces: List<ClassDescriptor>
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor {
internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(): T {
if (this.kind.isReal) {
return this
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
// TODO: is it correct to take first?
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
return filtered.first { it.modality != Modality.ABSTRACT } as T
}
}
@@ -157,7 +153,7 @@ internal val KotlinType.isKFunctionType: Boolean
internal val FunctionDescriptor.isFunctionInvoke: Boolean
get() {
val dispatchReceiver = dispatchReceiverParameter ?: return false
assert (!dispatchReceiver.type.isKFunctionType)
assert(!dispatchReceiver.type.isKFunctionType)
return dispatchReceiver.type.isFunctionType &&
this.isOperator && this.name == OperatorNameConventions.INVOKE
@@ -165,8 +161,19 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
get() {
val result = mutableListOf<T>()
fun traverse(descriptor: T) {
result.add(descriptor)
descriptor.overriddenDescriptors.forEach { traverse(it as T) }
}
traverse(this)
return result
}
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
get () {
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
// (includes declarations from supers)
@@ -177,49 +184,85 @@ internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
val setters = properties.mapNotNull { it.setter }
val allMethods = (functions + getters + setters).sortedBy {
// TODO: use local hash instead, but it needs major refactoring.
it.functionName.hashCode()
it.functionName.localHash.value
}
return allMethods
}
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|| this.kind == ClassKind.ENUM_CLASS
// TODO: optimize
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
get() {
assert(!this.isInterface)
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) {
emptyList()
} else {
this.getSuperClassOrAny().vtableEntries
}
val methods = this.contributedMethods
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
methods.singleOrNull { OverridingUtil.overrides(it, superMethod) } ?: superMethod
}
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
internal fun FunctionDescriptor.hasValueTypeAt(index: Int): Boolean {
when (index) {
0 -> return returnType.let { it != null && it.isValueType() }
1 -> return extensionReceiverParameter.let { it != null && it.type.isValueType() }
else -> return this.valueParameters[index - 2].type.isValueType()
}
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
if (functionDescriptor == function.original) return index
}
throw Error(function.toString() + " not in vtable of " + this.toString())
}
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
internal fun FunctionDescriptor.hasReferenceAt(index: Int): Boolean {
when (index) {
0 -> return returnType.let { it != null && !it.isValueType() }
1 -> return extensionReceiverParameter.let { it != null && !it.type.isValueType() }
else -> return !this.valueParameters[index - 2].type.isValueType()
}
}
private fun FunctionDescriptor.overridesFunWithReferenceAt(index: Int)
= allOverriddenDescriptors.any { it.original.hasReferenceAt(index) }
private fun FunctionDescriptor.overridesFunWithValueTypeAt(index: Int)
= allOverriddenDescriptors.any { it.original.hasValueTypeAt(index) }
private fun FunctionDescriptor.needBridgeToAt(target: FunctionDescriptor, index: Int)
= hasValueTypeAt(index) xor target.hasValueTypeAt(index)
internal fun FunctionDescriptor.needBridgeTo(target: FunctionDescriptor)
= (0..this.valueParameters.size + 1).any { needBridgeToAt(target, it) }
internal val FunctionDescriptor.target: FunctionDescriptor
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
internal enum class BridgeDirection {
NOT_NEEDED,
FROM_VALUE_TYPE,
TO_VALUE_TYPE
}
private fun FunctionDescriptor.bridgeDirectionAt(index: Int) : BridgeDirection {
if (kind.isReal) {
if (hasValueTypeAt(index) && overridesFunWithReferenceAt(index))
return BridgeDirection.FROM_VALUE_TYPE
return BridgeDirection.NOT_NEEDED
}
val target = this.target
return when {
hasValueTypeAt(index) && target.hasValueTypeAt(index) && overridesFunWithReferenceAt(index) -> BridgeDirection.FROM_VALUE_TYPE
hasValueTypeAt(index) && target.hasReferenceAt(index) && overridesFunWithValueTypeAt(index) -> BridgeDirection.TO_VALUE_TYPE
else -> BridgeDirection.NOT_NEEDED
}
}
private fun bridgesEqual(first: Array<BridgeDirection>, second: Array<BridgeDirection>)
= first.indices.none { first[it] != second[it] }
internal fun Array<BridgeDirection>.allNotNeeded() = this.all { it == BridgeDirection.NOT_NEEDED }
internal val FunctionDescriptor.bridgeDirections: Array<BridgeDirection>
get() {
assert(!this.isAbstract())
return this.contributedMethods.filter {
// We check that either method is open, or one of declarations it overrides
// is open.
it.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it).any { it.isOverridable }
val ourDirections = Array<BridgeDirection>(this.valueParameters.size + 2, { BridgeDirection.NOT_NEEDED })
if (modality == Modality.ABSTRACT)
return ourDirections
for (index in ourDirections.indices)
ourDirections[index] = this.bridgeDirectionAt(index)
if (!kind.isReal && bridgesEqual(ourDirections, this.target.bridgeDirections)) {
// Bridge is inherited from supers
for (index in ourDirections.indices)
ourDirections[index] = BridgeDirection.NOT_NEEDED
}
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
return ourDirections
}
@@ -3,6 +3,8 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.konan.descriptors.allValueParameters
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
@@ -81,15 +83,20 @@ private fun typeToHashString(type: KotlinType): String {
private val FunctionDescriptor.signature: String
get() {
val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: ""
val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this
val argsPart = actualDescriptor.valueParameters.map {
val argsPart = this.valueParameters.map {
typeToHashString(it.type)
}.joinToString(";")
// TODO: add return type
// (it is not simple because return type can be changed when overriding)
return "$extensionReceiverPart($argsPart)"
// Just distinguish value types and references - it's needed for calling virtual methods through bridges.
val returnTypePart =
when {
returnType.let { it != null && it.isValueType() } -> "ValueType"
returnType.let { it != null && !KotlinBuiltIns.isUnitOrNullableUnit(it) } -> "Reference"
else -> ""
}
return "$extensionReceiverPart($argsPart)$returnTypePart"
}
// TODO: rename to indicate that it has signature included
@@ -151,16 +151,6 @@ internal interface ContextUtils : RuntimeAware {
return base64Encode(globalHashBytes)
}
/**
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
*
* TODO: share this implementation
*/
private fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
val String.localHash: LocalHash
get() = LocalHash(localHash(stringAsBytes(this)))
val String.globalHash: ConstValue
get() = memScoped {
val hashBytes = this@globalHash.globalHashBytes
@@ -170,13 +160,22 @@ internal interface ContextUtils : RuntimeAware {
val FqName.globalHash: ConstValue
get() = this.toString().globalHash
val Name.localHash: LocalHash
get() = this.toString().localHash
val FqName.localHash: LocalHash
get() = this.toString().localHash
}
/**
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
*/
internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
internal val String.localHash: LocalHash
get() = LocalHash(localHash(stringAsBytes(this)))
internal val Name.localHash: LocalHash
get() = this.toString().localHash
internal val FqName.localHash: LocalHash
get() = this.toString().localHash
internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
@@ -1654,7 +1654,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
"konan.internal.areEqualByValue" -> {
val arg0 = args[0]
val arg1 = args[1]
assert (arg0.type == arg1.type)
assert (arg0.type == arg1.type, { "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
return when (LLVMGetTypeKind(arg0.type)) {
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind ->
@@ -1749,7 +1749,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val realDescriptor = descriptor.resolveFakeOverride().original
val realDescriptor = descriptor.target
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
return call(descriptor, llvmFunction, args, resultLifetime)
}
@@ -1766,7 +1766,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val owner = descriptor.containingDeclaration as ClassDescriptor
val llvmMethod = if (!owner.isInterface) {
// If this is a virtual method of the class - we can call via vtable.
val index = owner.vtableIndex(descriptor)
val index = context.getVtableBuilder(owner).vtableIndex(descriptor)
val vtablePlace = codegen.gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
val vtable = codegen.bitcast(kInt8PtrPtr, vtablePlace)
@@ -232,7 +232,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
val typeInfoWithVtableType = structType(
runtime.typeInfoType,
LLVMArrayType(int8TypePtr, descriptor.vtableEntries.size)!!
LLVMArrayType(int8TypePtr, context.getVtableBuilder(descriptor).vtableEntries.size)!!
)
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
@@ -121,15 +121,21 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
runtime.fieldTableRecordType, fields)
val methods = if (!classDesc.isAbstract()) {
classDesc.methodTableEntries.map {
val nameSignature = it.functionName.localHash
val methods = if (classDesc.isAbstract()) {
emptyList()
} else {
val functionNames = mutableMapOf<Long, OverriddenFunctionDescriptor>()
context.getVtableBuilder(classDesc).methodTableEntries.map {
val functionName = it.overriddenDescriptor.functionName
val nameSignature = functionName.localHash
val previous = functionNames.putIfAbsent(nameSignature.value, it)
if (previous != null)
throw AssertionError("Duplicate method table entry: functionName = '$functionName', hash = '${nameSignature.value}', entry1 = $previous, entry2 = $it")
// TODO: compile-time resolution limits binary compatibility
val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress
val methodEntryPoint = it.implementation.entryPointAddress
MethodTableRecord(nameSignature, methodEntryPoint)
}.sortedBy { it.nameSignature.value }
} else {
emptyList()
}
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
@@ -148,7 +154,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
typeInfo
} else {
// TODO: compile-time resolution limits binary compatibility
val vtableEntries = classDesc.vtableEntries.map { it.resolveFakeOverride().original.entryPointAddress }
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map { it.implementation.entryPointAddress }
val vtable = ConstArray(int8TypePtr, vtableEntries)
Struct(typeInfo, vtable)
}
@@ -159,4 +165,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
}
internal val OverriddenFunctionDescriptor.implementation: FunctionDescriptor
get() {
val target = descriptor.target
if (!needBridge) return target
val bridgeOwner = if (descriptor.bridgeDirections.allNotNeeded()) target else descriptor
return context.specialDescriptorsFactory.getBridgeDescriptor(bridgeOwner)
}
}
@@ -4,15 +4,12 @@ import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.isNullConst
import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
import org.jetbrains.kotlin.backend.konan.util.atMostOne
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -94,6 +91,38 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
}
}
override fun visitCall(expression: IrCall): IrExpression {
val irCall = super.visitCall(expression) as IrCall
val descriptor = irCall.descriptor as? FunctionDescriptor ?: return irCall
if (descriptor.modality == Modality.ABSTRACT || (irCall.superQualifier == null && descriptor.isOverridable))
return irCall // A virtual call. box/unbox will be in the corresponding bridge.
val target = descriptor.target
if (!descriptor.original.needBridgeTo(target)) return irCall
val overriddenCall = IrCallImpl(irCall.startOffset, irCall.endOffset,
target, remapTypeArguments(irCall, target)).apply {
dispatchReceiver = irCall.dispatchReceiver
extensionReceiver = irCall.extensionReceiver
mapValueParameters { irCall.getValueArgument(it)!! }
}
return super.visitCall(overriddenCall)
}
private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor): Map<TypeParameterDescriptor, KotlinType>? {
val oldCallee = oldExpression.descriptor
return if (oldCallee.typeParameters.isEmpty())
null
else oldCallee.typeParameters.associateBy(
{ newCallee.typeParameters[it.index] },
{ oldExpression.getTypeArgument(it)!! }
)
}
/**
* @return the [ValueType] given type represented in generated code as,
* or `null` if represented as object reference.
@@ -0,0 +1,79 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal class BridgesBuilding(val context: Context) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrStatement {
val irClass = super.visitClass(declaration) as IrClass
val functions = mutableSetOf<FunctionDescriptor?>()
irClass.declarations.forEach {
when (it) {
is IrFunction -> functions.add(it.descriptor)
is IrProperty -> {
functions.add(it.getter?.descriptor)
functions.add(it.setter?.descriptor)
}
}
}
irClass.descriptor.contributedMethods.forEach { functions.add(it) }
functions.forEach { buildBridge(it, irClass) }
return irClass
}
})
}
private object DECLARATION_ORIGIN_BRIDGE_METHOD :
IrDeclarationOriginImpl("BRIDGE_METHOD")
private fun buildBridge(descriptor: FunctionDescriptor?, irClass: IrClass) {
if (descriptor == null) return
if (descriptor.bridgeDirections.allNotNeeded())
return
val bridgeDescriptor = context.specialDescriptorsFactory.getBridgeDescriptor(descriptor)
val delegatingCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, descriptor.target,
superQualifier = descriptor.target.containingDeclaration as ClassDescriptor /* Call non-virtually */).apply {
val dispatchReceiverParameter = bridgeDescriptor.dispatchReceiverParameter
if (dispatchReceiverParameter != null)
dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter)
val extensionReceiverParameter = bridgeDescriptor.extensionReceiverParameter
if (extensionReceiverParameter != null)
extensionReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, extensionReceiverParameter)
bridgeDescriptor.valueParameters.forEach {
this.putValueArgument(it.index, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it))
}
}
val bridgeBody = if (bridgeDescriptor.returnType.let { it != null && !KotlinBuiltIns.isUnitOrNullableUnit(it) })
IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridgeDescriptor, delegatingCall)
else
delegatingCall
irClass.declarations.add(IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_BRIDGE_METHOD,
bridgeDescriptor, IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(bridgeBody))))
}
}
@@ -4,7 +4,6 @@ import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
+70
View File
@@ -441,6 +441,76 @@ task initializers_correctOrder2(type: RunKonanTest) {
source = "codegen/initializers/correctOrder2.kt"
}
task bridges_test0(type: RunKonanTest) {
goldValue = "42\n42\n"
source = "codegen/bridges/test0.kt"
}
task bridges_test1(type: RunKonanTest) {
goldValue = "1042\n"
source = "codegen/bridges/test1.kt"
}
task bridges_test2(type: RunKonanTest) {
goldValue = "42\n42\n"
source = "codegen/bridges/test2.kt"
}
task bridges_test3(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/bridges/test3.kt"
}
task bridges_test4(type: RunKonanTest) {
goldValue = "42\n42\n"
source = "codegen/bridges/test4.kt"
}
task bridges_test5(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/bridges/test5.kt"
}
task bridges_test6(type: RunKonanTest) {
goldValue = "42\n42\n42\n42\n42\n"
source = "codegen/bridges/test6.kt"
}
task bridges_test7(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/bridges/test7.kt"
}
task bridges_test8(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/bridges/test8.kt"
}
task bridges_test9(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/bridges/test9.kt"
}
task bridges_test10(type: RunKonanTest) {
goldValue = "42\n42\n"
source = "codegen/bridges/test10.kt"
}
task bridges_test11(type: RunKonanTest) {
goldValue = "42\n42\n42\n"
source = "codegen/bridges/test11.kt"
}
task bridges_test12(type: RunKonanTest) {
goldValue = "B: 42\nC: 42\n"
source = "codegen/bridges/test12.kt"
}
task bridges_test13(type: RunKonanTest) {
goldValue = "42\n42\n"
source = "codegen/bridges/test13.kt"
}
task array0(type: RunKonanTest) {
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
source = "runtime/collections/array0.kt"
@@ -0,0 +1,15 @@
// vtable call
open class A {
open fun foo(): Any = "A"
}
open class C : A() {
override fun foo(): Int = 42
}
fun main(args: Array<String>) {
val c = C()
val a: A = c
println(c.foo().toString())
println(a.foo().toString())
}
@@ -0,0 +1,18 @@
// interface call, bridge overridden
interface Z1 {
fun foo(x: Int) : Any
}
open class A : Z1 {
override fun foo(x: Int) : Int = 5
}
open class B : A() {
override fun foo(x: Int) : Int = 42
}
fun main(args: Array<String>) {
val z1: A = B()
println((z1.foo(1) + 1000).toString())
}
@@ -0,0 +1,28 @@
open class A<T> {
open fun foo(x: T) {
println(x.toString())
}
}
interface I {
fun foo(x: Int)
}
class B : A<Int>(), I {
var z: Int = 5
override fun foo(x: Int) {
z = x
}
}
fun zzz(a: A<Int>) {
a.foo(42)
}
fun main(args: Array<String>) {
val b = B()
zzz(b)
val a = A<Int>()
zzz(a)
println(b.z)
}
@@ -0,0 +1,20 @@
interface I {
fun foo(x: Int)
}
abstract class A<T> {
abstract fun foo(x: T)
}
class B : A<Int>(), I {
override fun foo(x: Int) = println(x)
}
fun main(args: Array<String>) {
val b = B()
val a: A<Int> = b
val c: I = b
b.foo(42)
a.foo(42)
c.foo(42)
}
@@ -0,0 +1,24 @@
abstract class A<in T> {
abstract fun foo(x: T)
}
class B : A<Int>() {
override fun foo(x: Int) {
println("B: $x")
}
}
class C : A<Any>() {
override fun foo(x: Any) {
println("C: $x")
}
}
fun foo(arg: A<Int>) {
arg.foo(42)
}
fun main(args: Array<String>) {
foo(B())
foo(C())
}
@@ -0,0 +1,22 @@
open class A<T> {
open fun T.foo() {
println(this.toString())
}
fun bar(x: T) {
x.foo()
}
}
open class B: A<Int>() {
override fun Int.foo() {
println(this)
}
}
fun main(args : Array<String>) {
val b = B()
val a = A<Int>()
b.bar(42)
a.bar(42)
}
@@ -0,0 +1,17 @@
// vtable call, bridge inherited
open class A {
open fun foo(): Any = "A"
}
open class C : A() {
override fun foo(): Int = 42
}
open class D: C()
fun main(args: Array<String>) {
val c = D()
val a: A = c
println(c.foo().toString())
println(a.foo().toString())
}
@@ -0,0 +1,30 @@
// non-generic interface, generic impl, non-virtual call + interface call
open class A<T> {
var size: T = 56 as T
}
interface C {
var size: Int
}
class B : C, A<Int>()
fun box(): String {
val b = B()
if (b.size != 56) return "fail 1"
b.size = 55
if (b.size != 55) return "fail 2"
val c: C = b
if (c.size != 55) return "fail 3"
c.size = 57
if (c.size != 57) return "fail 4"
return "OK"
}
fun main(args: Array<String>) {
println(box())
}
@@ -0,0 +1,21 @@
// vtable call + interface call
interface Z {
fun foo(): Any
}
interface Y {
fun foo(): Int
}
open class A {
fun foo(): Int = 42
}
open class B: A(), Z, Y
fun main(args: Array<String>) {
val z: Z = B()
val y: Y = z as Y
println(z.foo().toString())
println(y.foo().toString())
}
@@ -0,0 +1,30 @@
// non-generic interface, generic impl, vtable call + interface call
open class A<T> {
open var size: T = 56 as T
}
interface C {
var size: Int
}
open class B : C, A<Int>()
fun box(): String {
val b = B()
if (b.size != 56) return "fail 1"
b.size = 55
if (b.size != 55) return "fail 2"
val c: C = b
if (c.size != 55) return "fail 3"
c.size = 57
if (c.size != 57) return "fail 4"
return "OK"
}
fun main(args: Array<String>) {
println(box())
}
@@ -0,0 +1,31 @@
// vtable call + interface call
interface Z {
fun foo(): Any
}
interface Y {
fun foo(): Int
}
open class A {
open fun foo(): Any = "A"
}
open class C : A() {
override fun foo(): Int = 42
}
open class D: C(), Y, Z
fun main(args: Array<String>) {
val d = D()
val y: Y = d
val z: Z = d
val c: C = d
val a: A = d
println(d.foo().toString())
println(y.foo().toString())
println(z.foo().toString())
println(c.foo().toString())
println(a.foo().toString())
}
@@ -0,0 +1,30 @@
// generic interface, non-generic impl, vtable call + interface call
open class A {
open var size: Int = 56
}
interface C<T> {
var size: T
}
open class B : C<Int>, A()
fun box(): String {
val b = B()
if (b.size != 56) return "fail 1"
b.size = 55
if (b.size != 55) return "fail 2"
val c: C<Int> = b
if (c.size != 55) return "fail 3"
c.size = 57
if (c.size != 57) return "fail 4"
return "OK"
}
fun main(args: Array<String>) {
println(box())
}
@@ -0,0 +1,30 @@
// generic interface, non-generic impl, non-virtual call + interface call
open class A {
var size: Int = 56
}
interface C<T> {
var size: T
}
class B : C<Int>, A()
fun box(): String {
val b = B()
if (b.size != 56) return "fail 1"
b.size = 55
if (b.size != 55) return "fail 2"
val c: C<Int> = b
if (c.size != 55) return "fail 3"
c.size = 57
if (c.size != 57) return "fail 4"
return "OK"
}
fun main(args: Array<String>) {
println(box())
}
@@ -0,0 +1,27 @@
// abstract class vtable call
abstract class A {
abstract fun foo(): String
}
abstract class B : A()
class Z : B() {
override fun foo() = "Z"
}
fun box(): String {
val z = Z()
val b: B = z
val a: A = z
return when {
z.foo() != "Z" -> "Fail #1"
b.foo() != "Z" -> "Fail #2"
a.foo() != "Z" -> "Fail #3"
else -> "OK"
}
}
fun main(args: Array<String>) {
println(box())
}