Implemented equal in IR
This commit is contained in:
+1
-1
@@ -136,7 +136,7 @@ internal class KonanLower(val context: Context) {
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
||||
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
||||
BuiltinOperatorLowering(context).lower(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
||||
InnerClassLowering(context).runOnFilePostfix(irFile)
|
||||
|
||||
+4
-4
@@ -185,11 +185,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
||||
symbolTable.referenceSimpleFunction(it)
|
||||
}
|
||||
|
||||
val ieee754NullableEquals = context.getInternalFunctions("ieee754NullableEquals").map {
|
||||
symbolTable.referenceSimpleFunction(it)
|
||||
}
|
||||
val equals = context.builtIns.any.unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier("equals"), NoLookupLocation.FROM_BACKEND)
|
||||
.single().let { symbolTable.referenceSimpleFunction(it) }
|
||||
|
||||
override val areEqual = symbolTable.referenceSimpleFunction(context.getInternalFunctions("areEqual").single())
|
||||
override val areEqual get() = error("Must not be used")
|
||||
|
||||
override val ThrowNullPointerException = symbolTable.referenceSimpleFunction(
|
||||
context.getInternalFunctions("ThrowNullPointerException").single())
|
||||
|
||||
+103
-59
@@ -16,44 +16,43 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.atMostOne
|
||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
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.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
|
||||
/**
|
||||
* This lowering pass lowers some calls to [IrBuiltinOperatorDescriptor]s.
|
||||
*/
|
||||
internal class BuiltinOperatorLowering(val context: Context) : BodyLoweringPass {
|
||||
|
||||
private val transformer = BuiltinOperatorTransformer(context)
|
||||
|
||||
override fun lower(irBody: IrBody) {
|
||||
irBody.transformChildrenVoid(transformer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BuiltinOperatorTransformer(val context: Context) : IrElementTransformerVoid() {
|
||||
internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, IrBuildingTransformer(context) {
|
||||
|
||||
private val builtIns = context.builtIns
|
||||
private val irBuiltins = context.irModule!!.irBuiltins
|
||||
private val symbols = context.ir.symbols
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
@@ -104,56 +103,101 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.irLogicalAnd(lhs: IrExpression, rhs: IrExpression) = context.andand(lhs, rhs)
|
||||
private fun IrBuilderWithScope.irIsNull(exp: IrExpression) = irEqeqeq(exp, irNull())
|
||||
private fun IrBuilderWithScope.irIsNotNull(exp: IrExpression) = irNot(irEqeqeq(exp, irNull()))
|
||||
|
||||
private fun lowerEqeq(expression: IrCall): IrExpression {
|
||||
// TODO: optimize boxing?
|
||||
val startOffset = expression.startOffset
|
||||
val endOffset = expression.endOffset
|
||||
|
||||
val equals = selectEqualsFunction(expression)
|
||||
builder.at(expression).run {
|
||||
val lhs = expression.getValueArgument(0)!!
|
||||
val rhs = expression.getValueArgument(1)!!
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, equals).apply {
|
||||
putValueArgument(0, expression.getValueArgument(0)!!)
|
||||
putValueArgument(1, expression.getValueArgument(1)!!)
|
||||
val nullableNothingType = builtIns.nullableNothingType
|
||||
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
|
||||
// Compare by reference if each part is either `Nothing` or `Nothing?`:
|
||||
return irEqeqeq(lhs, rhs)
|
||||
}
|
||||
|
||||
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
|
||||
selectIntrinsic(symbols.areEqualByValue, lhs.type, rhs.type, false)?.let {
|
||||
return irCall(it).apply {
|
||||
putValueArgument(0, lhs)
|
||||
putValueArgument(1, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
if (lhs.isNullConst() || rhs.isNullConst()) {
|
||||
// or compare by reference if left or right part is `null`:
|
||||
return irEqeqeq(lhs, rhs)
|
||||
}
|
||||
|
||||
// TODO: areEqualByValue and ieee754Equals intrinsics are specially treated by code generator
|
||||
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
|
||||
fun callEquals(lhs: IrExpression, rhs: IrExpression) =
|
||||
if (expression.descriptor in ieee754EqualsDescriptors())
|
||||
// Find a type-compatible `konan.internal.ieee754Equals` intrinsic:
|
||||
irCall(selectIntrinsic(symbols.ieee754Equals, lhs.type, rhs.type, true)!!).apply {
|
||||
putValueArgument(0, lhs)
|
||||
putValueArgument(1, rhs)
|
||||
}
|
||||
else
|
||||
irCall(symbols.equals).apply {
|
||||
dispatchReceiver = lhs
|
||||
putValueArgument(0, rhs)
|
||||
}
|
||||
|
||||
val lhsIsNotNullable = !lhs.type.isNullable()
|
||||
val rhsIsNotNullable = !rhs.type.isNullable()
|
||||
|
||||
return if (expression.descriptor in ieee754EqualsDescriptors()) {
|
||||
if (lhsIsNotNullable && rhsIsNotNullable)
|
||||
callEquals(lhs, rhs)
|
||||
else irBlock {
|
||||
val lhsTemp = irTemporary(lhs)
|
||||
val rhsTemp = irTemporary(rhs)
|
||||
if (lhsIsNotNullable xor rhsIsNotNullable) { // Exactly one nullable.
|
||||
+irLogicalAnd(
|
||||
irIsNotNull(irGet((if (lhsIsNotNullable) rhsTemp else lhsTemp).symbol)),
|
||||
callEquals(irGet(lhsTemp.symbol), irGet(rhsTemp.symbol))
|
||||
)
|
||||
} else { // Both are nullable.
|
||||
+irIfThenElse(builtIns.booleanType, irIsNull(irGet(lhsTemp.symbol)),
|
||||
irIsNull(irGet(rhsTemp.symbol)),
|
||||
irLogicalAnd(
|
||||
irIsNotNull(irGet(rhsTemp.symbol)),
|
||||
callEquals(irGet(lhsTemp.symbol), irGet(rhsTemp.symbol))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lhsIsNotNullable)
|
||||
callEquals(lhs, rhs)
|
||||
else {
|
||||
irBlock {
|
||||
val lhsTemp = irTemporary(lhs)
|
||||
if (rhsIsNotNullable)
|
||||
+irLogicalAnd(irIsNotNull(irGet(lhsTemp.symbol)), callEquals(irGet(lhsTemp.symbol), rhs))
|
||||
else {
|
||||
val rhsTemp = irTemporary(rhs)
|
||||
+irIfThenElse(builtIns.booleanType, irIsNull(irGet(lhsTemp.symbol)),
|
||||
irIsNull(irGet(rhsTemp.symbol)),
|
||||
callEquals(irGet(lhsTemp.symbol), irGet(rhsTemp.symbol))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectEqualsFunction(expression: IrCall): IrSimpleFunctionSymbol {
|
||||
val lhs = expression.getValueArgument(0)!!
|
||||
val rhs = expression.getValueArgument(1)!!
|
||||
|
||||
val nullableNothingType = builtIns.nullableNothingType
|
||||
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
|
||||
// Compare by reference if each part is either `Nothing` or `Nothing?`:
|
||||
return irBuiltins.eqeqeqSymbol
|
||||
}
|
||||
|
||||
// TODO: areEqualByValue and ieee754Equals intrinsics are specially treated by code generator
|
||||
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
|
||||
|
||||
// Find a type-compatible `konan.internal.ieee754Equals` intrinsic:
|
||||
if (expression.descriptor in ieee754EqualsDescriptors()) {
|
||||
return if (lhs.type.isNullable() || rhs.type.isNullable())
|
||||
selectIntrinsic(context.ir.symbols.ieee754NullableEquals, lhs.type, rhs.type)!!
|
||||
else
|
||||
selectIntrinsic(context.ir.symbols.ieee754Equals, lhs.type, rhs.type)!!
|
||||
}
|
||||
|
||||
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
|
||||
selectIntrinsic(context.ir.symbols.areEqualByValue, lhs.type, rhs.type)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
return if (lhs.isNullConst() || rhs.isNullConst()) {
|
||||
// or compare by reference if left or right part is `null`:
|
||||
irBuiltins.eqeqeqSymbol
|
||||
} else {
|
||||
// or use the general implementation:
|
||||
context.ir.symbols.areEqual
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType):
|
||||
IrSimpleFunctionSymbol? = from.atMostOne {
|
||||
lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type)
|
||||
}
|
||||
private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType, allowNullable: Boolean) =
|
||||
from.atMostOne {
|
||||
val leftParamType = it.owner.valueParameters[0].type
|
||||
val rightParamType = it.owner.valueParameters[1].type
|
||||
(lhsType.isSubtypeOf(leftParamType) || (allowNullable && lhsType.isSubtypeOf(leftParamType.makeNullable())))
|
||||
&& (rhsType.isSubtypeOf(rightParamType) || (allowNullable && rhsType.isSubtypeOf(rightParamType.makeNullable())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,21 +21,9 @@ fun inline_assert() {
|
||||
//assert(true)
|
||||
}
|
||||
|
||||
fun inline_areEqual() {
|
||||
val a = 17
|
||||
val b = "some string"
|
||||
println(konan.internal.areEqual(a, 17))
|
||||
println(konan.internal.areEqual(a, a))
|
||||
println(konan.internal.areEqual(17, 17))
|
||||
println(konan.internal.areEqual(b, "some string"))
|
||||
println(konan.internal.areEqual("some string", b))
|
||||
println(konan.internal.areEqual(b, b))
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
inline_todo()
|
||||
inline_assert()
|
||||
inline_maxof()
|
||||
inline_areEqual()
|
||||
}
|
||||
|
||||
|
||||
@@ -30,25 +30,6 @@ import kotlinx.cinterop.NativePtr
|
||||
@Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean
|
||||
@Intrinsic external fun ieee754Equals(first: Double, second: Double): Boolean
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun ieee754NullableEquals(first: Float?, second: Float?): Boolean = when {
|
||||
first == null -> second == null
|
||||
second == null -> false
|
||||
else -> ieee754Equals(first, second)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun ieee754NullableEquals(first: Double?, second: Double?): Boolean = when {
|
||||
first == null -> second == null
|
||||
second == null -> false
|
||||
else -> ieee754Equals(first, second)
|
||||
}
|
||||
|
||||
@Intrinsic external fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun areEqual(first: Any?, second: Any?): Boolean {
|
||||
return if (first == null) second == null else first.equals(second)
|
||||
}
|
||||
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean
|
||||
Reference in New Issue
Block a user