Merge branch 'master' of https://github.com/JetBrains/kotlin-native
This commit is contained in:
+68
@@ -0,0 +1,68 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
/**
|
||||
* Value type is a kind of Kotlin types represented as plain value in generated code, not as an object reference.
|
||||
* Such types may require autoboxing.
|
||||
*
|
||||
* The value type nearly corresponds to `[classFqName]` or `[classFqName]?` Kotlin type (depending on [isNullable]).
|
||||
*
|
||||
* @property classFqName name of the base value type class
|
||||
* @property isNullable whether `null` is included into this value type
|
||||
*/
|
||||
enum class ValueType(val classFqName: FqNameUnsafe, val isNullable: Boolean = false) {
|
||||
BOOLEAN(KotlinBuiltIns.FQ_NAMES._boolean),
|
||||
CHAR(KotlinBuiltIns.FQ_NAMES._char),
|
||||
|
||||
BYTE(KotlinBuiltIns.FQ_NAMES._byte),
|
||||
SHORT(KotlinBuiltIns.FQ_NAMES._short),
|
||||
INT(KotlinBuiltIns.FQ_NAMES._int),
|
||||
LONG(KotlinBuiltIns.FQ_NAMES._long),
|
||||
FLOAT(KotlinBuiltIns.FQ_NAMES._float),
|
||||
DOUBLE(KotlinBuiltIns.FQ_NAMES._double),
|
||||
|
||||
UNBOUND_CALLABLE_REFERENCE(FqNameUnsafe("konan.internal.UnboundCallableReference"))
|
||||
}
|
||||
|
||||
private fun KotlinType.isConstructedFromGivenClass(fqName: FqNameUnsafe) =
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, fqName)
|
||||
|
||||
/**
|
||||
* @return `true` if this type must be represented as given value type in generated code.
|
||||
*/
|
||||
tailrec fun KotlinType.isRepresentedAs(valueType: ValueType): Boolean {
|
||||
if (this.isMarkedNullable && !valueType.isNullable) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Supertypes should be checked even for "final" value types (e.g. Int)
|
||||
// to treat type parameter `T` with `Int` upper bound as value type.
|
||||
// This behavior is observed on Kotlin JVM and used in interop implementation.
|
||||
//
|
||||
// However to optimize this method only first supertype is checked
|
||||
// (it is supposed to be enough in all sane cases).
|
||||
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
|
||||
return firstSupertype.isRepresentedAs(valueType)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` if this type without `null` value must be represented as given value type in generated code.
|
||||
*
|
||||
* TODO: this method can be considered as a hack; rework its usages.
|
||||
*/
|
||||
tailrec fun KotlinType.notNullableIsRepresentedAs(valueType: ValueType): Boolean {
|
||||
if (this.isConstructedFromGivenClass(valueType.classFqName)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// See comment in [isRepresentedAs].
|
||||
val firstSupertype = this.constructor.supertypes.firstOrNull() ?: return false
|
||||
return firstSupertype.notNullableIsRepresentedAs(valueType)
|
||||
}
|
||||
+3
-14
@@ -1,6 +1,8 @@
|
||||
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.llvm.functionName
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
@@ -118,20 +120,7 @@ internal fun KonanBuiltIns.getKonanInternalFunctions(name: String): List<Functio
|
||||
return konanInternal.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).toList()
|
||||
}
|
||||
|
||||
private val UNBOUND_CALLABLE_REFERENCE = "UnboundCallableReference"
|
||||
|
||||
/**
|
||||
* `konan.internal.UnboundCallableReference` type or `null` if it is not available.
|
||||
*/
|
||||
internal val KonanBuiltIns.unboundCallableReferenceTypeOrNull: KotlinType?
|
||||
get() = this.getKonanInternalClassOrNull(UNBOUND_CALLABLE_REFERENCE)?.defaultType
|
||||
|
||||
internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
||||
val classDescriptor = TypeUtils.getClassDescriptor(this) ?: return false
|
||||
|
||||
return !this.isMarkedNullable &&
|
||||
classDescriptor.fqNameUnsafe.asString() == "konan.internal.$UNBOUND_CALLABLE_REFERENCE"
|
||||
}
|
||||
internal fun KotlinType.isUnboundCallableReference() = this.isRepresentedAs(ValueType.UNBOUND_CALLABLE_REFERENCE)
|
||||
|
||||
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
||||
get() {
|
||||
|
||||
+22
-16
@@ -1,28 +1,34 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isUnboundCallableReference
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
return when {
|
||||
// Nullable types must be represented as objects for boxing.
|
||||
type.isMarkedNullable -> this.kObjHeaderPtr
|
||||
type.isUnboundCallableReference() -> int8TypePtr
|
||||
KotlinBuiltIns.isBoolean(type) -> LLVMInt1Type()
|
||||
KotlinBuiltIns.isByte(type) -> LLVMInt8Type()
|
||||
KotlinBuiltIns.isShort(type) || KotlinBuiltIns.isChar(type) -> LLVMInt16Type()
|
||||
KotlinBuiltIns.isInt(type) -> LLVMInt32Type()
|
||||
KotlinBuiltIns.isLong(type) -> LLVMInt64Type()
|
||||
KotlinBuiltIns.isFloat(type) -> LLVMFloatType()
|
||||
KotlinBuiltIns.isDouble(type) -> LLVMDoubleType()
|
||||
!KotlinBuiltIns.isPrimitiveType(type) -> this.kObjHeaderPtr
|
||||
else -> throw NotImplementedError(type.toString() + " is not supported")
|
||||
private val valueTypes = ValueType.values().associate {
|
||||
it to when (it) {
|
||||
ValueType.BOOLEAN -> LLVMInt1Type()
|
||||
ValueType.BYTE -> LLVMInt8Type()
|
||||
ValueType.SHORT, ValueType.CHAR -> LLVMInt16Type()
|
||||
ValueType.INT -> LLVMInt32Type()
|
||||
ValueType.LONG -> LLVMInt64Type()
|
||||
ValueType.FLOAT -> LLVMFloatType()
|
||||
ValueType.DOUBLE -> LLVMDoubleType()
|
||||
ValueType.UNBOUND_CALLABLE_REFERENCE -> int8TypePtr
|
||||
}!!
|
||||
}
|
||||
|
||||
internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
for ((valueType, llvmType) in valueTypes) {
|
||||
if (type.isRepresentedAs(valueType)) {
|
||||
return llvmType
|
||||
}
|
||||
}
|
||||
|
||||
return this.kObjHeaderPtr
|
||||
}
|
||||
|
||||
internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
|
||||
return when {
|
||||
type.isUnit() -> LLVMVoidType()!!
|
||||
|
||||
+29
-44
@@ -3,9 +3,11 @@ package org.jetbrains.kotlin.backend.konan.lower
|
||||
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.unboundCallableReferenceTypeOrNull
|
||||
import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -15,14 +17,10 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
/**
|
||||
* Boxes and unboxes values of primitive types when necessary.
|
||||
* Boxes and unboxes values of value types when necessary.
|
||||
*/
|
||||
internal class Autoboxing(val context: Context) : FileLoweringPass {
|
||||
|
||||
@@ -40,27 +38,13 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
// is not equal to e.g. called function return type?
|
||||
|
||||
|
||||
private val irBuiltins = context.irModule!!.irBuiltins
|
||||
|
||||
/**
|
||||
* The list of primitive types to box and unbox.
|
||||
*/
|
||||
private val primitiveTypes = with(context.builtIns) {
|
||||
listOf(booleanType, byteType, shortType, charType, intType, longType, floatType, doubleType) +
|
||||
unboundCallableReferenceTypeOrNull.singletonOrEmptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type to use for runtime type checks instead of given one (e.g. `IntBox` instead of `Int`)
|
||||
*/
|
||||
private fun getRuntimeReferenceType(type: KotlinType): KotlinType {
|
||||
if (type.isSubtypeOf(builtIns.nullableNothingType)) return type
|
||||
|
||||
primitiveTypes.forEach {
|
||||
listOf(it, it.makeNullable()).forEach { superType ->
|
||||
if (type.isSubtypeOf(superType)) {
|
||||
return getBoxType(superType).makeNullableAsSpecified(superType.isMarkedNullable)
|
||||
}
|
||||
ValueType.values().forEach {
|
||||
if (type.notNullableIsRepresentedAs(it)) {
|
||||
return getBoxType(it).makeNullableAsSpecified(TypeUtils.isNullableType(type))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,13 +93,13 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the element of [primitiveTypes] if given type is represented as primitive type in generated code,
|
||||
* @return the [ValueType] given type represented in generated code as,
|
||||
* or `null` if represented as object reference.
|
||||
*/
|
||||
private fun getCustomRepresentation(type: KotlinType): KotlinType? {
|
||||
if (type.isSubtypeOf(builtIns.nothingType)) return null
|
||||
|
||||
return primitiveTypes.firstOrNull { type.isSubtypeOf(it) }
|
||||
private fun getValueType(type: KotlinType): ValueType? {
|
||||
return ValueType.values().firstOrNull {
|
||||
type.isRepresentedAs(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrExpression.useAs(type: KotlinType): IrExpression {
|
||||
@@ -147,21 +131,21 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
|
||||
private fun IrExpression.adaptIfNecessary(actualType: KotlinType, expectedType: KotlinType): IrExpression {
|
||||
val actualRepresentation = getCustomRepresentation(actualType)
|
||||
val expectedRepresentation = getCustomRepresentation(expectedType)
|
||||
val actualValueType = getValueType(actualType)
|
||||
val expectedValueType = getValueType(expectedType)
|
||||
|
||||
return when {
|
||||
actualRepresentation == expectedRepresentation -> this
|
||||
actualValueType == expectedValueType -> this
|
||||
|
||||
actualRepresentation == null && expectedRepresentation != null -> {
|
||||
actualValueType == null && expectedValueType != null -> {
|
||||
// This may happen in the following cases:
|
||||
// 1. `actualType` is `Nothing`;
|
||||
// 2. `actualType` is incompatible.
|
||||
|
||||
this.unbox(expectedRepresentation)
|
||||
this.unbox(expectedValueType)
|
||||
}
|
||||
|
||||
actualRepresentation != null && expectedRepresentation == null -> this.box(actualRepresentation)
|
||||
actualValueType != null && expectedValueType == null -> this.box(actualValueType)
|
||||
|
||||
else -> throw IllegalArgumentException("actual type is $actualType, expected $expectedType")
|
||||
}
|
||||
@@ -175,23 +159,24 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getBoxType(primitiveType: KotlinType): SimpleType {
|
||||
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
||||
return context.builtIns.getKonanInternalClass("${primitiveTypeClass.name}Box").defaultType
|
||||
}
|
||||
private val ValueType.shortName
|
||||
get() = this.classFqName.shortName()
|
||||
|
||||
private fun IrExpression.box(primitiveType: KotlinType): IrExpression {
|
||||
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
||||
val boxFunction = context.builtIns.getKonanInternalFunctions("box${primitiveTypeClass.name}").singleOrNull() ?:
|
||||
TODO(primitiveType.toString())
|
||||
private fun getBoxType(valueType: ValueType) =
|
||||
context.builtIns.getKonanInternalClass("${valueType.shortName}Box").defaultType
|
||||
|
||||
private fun IrExpression.box(valueType: ValueType): IrExpression {
|
||||
val boxFunctionName = "box${valueType.shortName}"
|
||||
val boxFunction = context.builtIns.getKonanInternalFunctions(boxFunctionName).singleOrNull() ?:
|
||||
TODO(valueType.toString())
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, boxFunction).apply {
|
||||
putValueArgument(0, this@box)
|
||||
}.uncheckedCast(this.type) // Try not to bring new type incompatibilities.
|
||||
}
|
||||
|
||||
private fun IrExpression.unbox(primitiveType: KotlinType): IrExpression {
|
||||
val boxGetter = getBoxType(primitiveType)
|
||||
private fun IrExpression.unbox(valueType: ValueType): IrExpression {
|
||||
val boxGetter = getBoxType(valueType)
|
||||
.memberScope.getContributedDescriptors()
|
||||
.filterIsInstance<PropertyDescriptor>()
|
||||
.single { it.name.asString() == "value" }
|
||||
|
||||
@@ -581,7 +581,6 @@ task boxing14(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task boxing15(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "17\n"
|
||||
source = "codegen/boxing/boxing15.kt"
|
||||
}
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ class PlatformInfo {
|
||||
}
|
||||
|
||||
task dist_compiler(type: Copy) {
|
||||
dependsOn ':backend.native:jars', 'dist_runtime'
|
||||
dependsOn ':backend.native:jars'
|
||||
|
||||
destinationDir file('dist')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user