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)
|
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
||||||
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
BuiltinOperatorLowering(context).lower(irFile)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
||||||
InnerClassLowering(context).runOnFilePostfix(irFile)
|
InnerClassLowering(context).runOnFilePostfix(irFile)
|
||||||
|
|||||||
+4
-4
@@ -185,11 +185,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
|||||||
symbolTable.referenceSimpleFunction(it)
|
symbolTable.referenceSimpleFunction(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ieee754NullableEquals = context.getInternalFunctions("ieee754NullableEquals").map {
|
val equals = context.builtIns.any.unsubstitutedMemberScope
|
||||||
symbolTable.referenceSimpleFunction(it)
|
.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(
|
override val ThrowNullPointerException = symbolTable.referenceSimpleFunction(
|
||||||
context.getInternalFunctions("ThrowNullPointerException").single())
|
context.getInternalFunctions("ThrowNullPointerException").single())
|
||||||
|
|||||||
+103
-59
@@ -16,44 +16,43 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.lower
|
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.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.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
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.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptor
|
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.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
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.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.isNullable
|
import org.jetbrains.kotlin.types.isNullable
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This lowering pass lowers some calls to [IrBuiltinOperatorDescriptor]s.
|
* This lowering pass lowers some calls to [IrBuiltinOperatorDescriptor]s.
|
||||||
*/
|
*/
|
||||||
internal class BuiltinOperatorLowering(val context: Context) : BodyLoweringPass {
|
internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, IrBuildingTransformer(context) {
|
||||||
|
|
||||||
private val transformer = BuiltinOperatorTransformer(context)
|
|
||||||
|
|
||||||
override fun lower(irBody: IrBody) {
|
|
||||||
irBody.transformChildrenVoid(transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BuiltinOperatorTransformer(val context: Context) : IrElementTransformerVoid() {
|
|
||||||
|
|
||||||
private val builtIns = context.builtIns
|
private val builtIns = context.builtIns
|
||||||
private val irBuiltins = context.irModule!!.irBuiltins
|
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 {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
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 {
|
private fun lowerEqeq(expression: IrCall): IrExpression {
|
||||||
// TODO: optimize boxing?
|
// 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 {
|
val nullableNothingType = builtIns.nullableNothingType
|
||||||
putValueArgument(0, expression.getValueArgument(0)!!)
|
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
|
||||||
putValueArgument(1, expression.getValueArgument(1)!!)
|
// 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 {
|
private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType, allowNullable: Boolean) =
|
||||||
val lhs = expression.getValueArgument(0)!!
|
from.atMostOne {
|
||||||
val rhs = expression.getValueArgument(1)!!
|
val leftParamType = it.owner.valueParameters[0].type
|
||||||
|
val rightParamType = it.owner.valueParameters[1].type
|
||||||
val nullableNothingType = builtIns.nullableNothingType
|
(lhsType.isSubtypeOf(leftParamType) || (allowNullable && lhsType.isSubtypeOf(leftParamType.makeNullable())))
|
||||||
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
|
&& (rhsType.isSubtypeOf(rightParamType) || (allowNullable && rhsType.isSubtypeOf(rightParamType.makeNullable())))
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,21 +21,9 @@ fun inline_assert() {
|
|||||||
//assert(true)
|
//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() {
|
@Test fun runTest() {
|
||||||
inline_todo()
|
inline_todo()
|
||||||
inline_assert()
|
inline_assert()
|
||||||
inline_maxof()
|
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: Float, second: Float): Boolean
|
||||||
@Intrinsic external fun ieee754Equals(first: Double, second: Double): 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: NativePtr, second: NativePtr): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
|
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): 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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user