Skip to content

Commit

Permalink
Implemented equal in IR
Browse files Browse the repository at this point in the history
  • Loading branch information
homuroll committed Jun 6, 2018
1 parent eec6f39 commit 2faf790
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

return IrCallImpl(startOffset, endOffset, equals).apply {
putValueArgument(0, expression.getValueArgument(0)!!)
putValueArgument(1, expression.getValueArgument(1)!!)
builder.at(expression).run {
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 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())))
}
}
12 changes: 0 additions & 12 deletions backend.native/tests/serialization/deserialized_inline0.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

21 changes: 1 addition & 20 deletions runtime/src/main/kotlin/konan/internal/Intrinsics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 2faf790

Please sign in to comment.