Implement lowering of some builtin operators. (#154)
* EQEQ * THROW_NPE Add test for (un)boxing `==` operands.
This commit is contained in:
committed by
GitHub
parent
0632a30eca
commit
b6561b13a4
+4
@@ -17,6 +17,10 @@ internal class KonanLower(val context: Context) {
|
|||||||
fun lower(irFile: IrFile) {
|
fun lower(irFile: IrFile) {
|
||||||
val phaser = PhaseManager(context)
|
val phaser = PhaseManager(context)
|
||||||
|
|
||||||
|
phaser.phase("Lower_builtin_operators") {
|
||||||
|
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
||||||
|
}
|
||||||
|
|
||||||
phaser.phase("Lower_shared_variables") {
|
phaser.phase("Lower_shared_variables") {
|
||||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ object KonanPhases {
|
|||||||
val phases = mapOf<String, KonanPhase> (
|
val phases = mapOf<String, KonanPhase> (
|
||||||
"Backend" to KonanPhase("All backend"),
|
"Backend" to KonanPhase("All backend"),
|
||||||
"Optimizer" to KonanPhase("IR Optimizer"),
|
"Optimizer" to KonanPhase("IR Optimizer"),
|
||||||
|
"Lower_builtin_operators" to KonanPhase("BuiltIn Operators Lowering"),
|
||||||
"Lower_shared_variables" to KonanPhase("Shared Variable Lowering"),
|
"Lower_shared_variables" to KonanPhase("Shared Variable Lowering"),
|
||||||
"Lower_local_functions" to KonanPhase("Local Function Lowering"),
|
"Lower_local_functions" to KonanPhase("Local Function Lowering"),
|
||||||
"Lower_callables" to KonanPhase("Callable references Lowering"),
|
"Lower_callables" to KonanPhase("Callable references Lowering"),
|
||||||
|
|||||||
+6
@@ -43,6 +43,12 @@ internal val FunctionDescriptor.implementation: FunctionDescriptor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||||
|
|
||||||
|
// TODO: check it is external?
|
||||||
|
internal val FunctionDescriptor.isIntrinsic: Boolean
|
||||||
|
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
||||||
|
|
||||||
private val intrinsicTypes = setOf(
|
private val intrinsicTypes = setOf(
|
||||||
"kotlin.Unit",
|
"kotlin.Unit",
|
||||||
"kotlin.Boolean", "kotlin.Char",
|
"kotlin.Boolean", "kotlin.Char",
|
||||||
|
|||||||
+43
-107
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
@@ -1723,6 +1724,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
return evaluateFunctionInvoke(descriptor, args)
|
return evaluateFunctionInvoke(descriptor, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.isIntrinsic) {
|
||||||
|
return evaluateIntrinsicCall(callee, args)
|
||||||
|
}
|
||||||
|
|
||||||
when (descriptor) {
|
when (descriptor) {
|
||||||
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
|
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
|
||||||
is ClassConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
is ClassConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
||||||
@@ -1808,14 +1813,31 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
private val kEqeq = Name.identifier("EQEQ")
|
|
||||||
private val ktEqeqeq = Name.identifier("EQEQEQ")
|
private fun evaluateIntrinsicCall(callee: IrCall, args: List<LLVMValueRef?>): LLVMValueRef {
|
||||||
private val kGt0 = Name.identifier("GT0")
|
val descriptor = callee.descriptor
|
||||||
private val kGteq0 = Name.identifier("GTEQ0")
|
val name = descriptor.fqNameUnsafe.asString()
|
||||||
private val kLt0 = Name.identifier("LT0")
|
|
||||||
private val kLteq0 = Name.identifier("LTEQ0")
|
return when (name) {
|
||||||
private val kNot = Name.identifier("NOT")
|
"konan.internal.areEqualByValue" -> {
|
||||||
private val kThrowNpe = Name.identifier("THROW_NPE")
|
val arg0 = args[0]!!
|
||||||
|
val arg1 = args[1]!!
|
||||||
|
assert (arg0.type == arg1.type)
|
||||||
|
|
||||||
|
when (LLVMGetTypeKind(arg0.type)) {
|
||||||
|
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind ->
|
||||||
|
codegen.fcmpEq(arg0, arg1)
|
||||||
|
|
||||||
|
else ->
|
||||||
|
codegen.icmpEq(arg0, arg1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> TODO(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!!
|
private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!!
|
||||||
private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!!
|
private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!!
|
||||||
private val kTrue = LLVMConstInt(LLVMInt1Type(), 1, 1)!!
|
private val kTrue = LLVMConstInt(LLVMInt1Type(), 1, 1)!!
|
||||||
@@ -1825,16 +1847,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
private fun evaluateOperatorCall(callee: IrCall, args: List<LLVMValueRef?>): LLVMValueRef {
|
private fun evaluateOperatorCall(callee: IrCall, args: List<LLVMValueRef?>): LLVMValueRef {
|
||||||
context.log("evaluateCall : origin:${ir2string(callee)}")
|
context.log("evaluateCall : origin:${ir2string(callee)}")
|
||||||
val descriptor = callee.descriptor
|
val descriptor = callee.descriptor
|
||||||
when (descriptor.name) {
|
val ib = context.irModule!!.irBuiltins
|
||||||
kEqeq -> return evaluateOperatorEqeq (callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!)
|
when (descriptor) {
|
||||||
ktEqeqeq -> return evaluateOperatorEqeqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!)
|
ib.eqeqeq -> return codegen.icmpEq(args[0]!!, args[1]!!)
|
||||||
kGt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
|
ib.gt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
|
||||||
kGteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
|
ib.gteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
|
||||||
kLt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
|
ib.lt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
|
||||||
kLteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
|
ib.lteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
|
||||||
kNot -> return codegen.icmpNe(args[0]!!, kTrue)
|
ib.booleanNot -> return codegen.icmpNe(args[0]!!, kTrue)
|
||||||
// TODO: reconsider.
|
|
||||||
kThrowNpe -> return evaluateOperatorThrowNpe()
|
|
||||||
else -> {
|
else -> {
|
||||||
TODO(descriptor.name.toString())
|
TODO(descriptor.name.toString())
|
||||||
}
|
}
|
||||||
@@ -1843,19 +1863,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun getEquals(type: KotlinType) : SimpleFunctionDescriptor {
|
|
||||||
|
|
||||||
val name = Name.identifier("equals")
|
|
||||||
val descriptors = type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).filter {
|
|
||||||
it.valueParameters.size == 1 && KotlinBuiltIns.isAnyOrNullableAny(it.valueParameters[0].type)
|
|
||||||
}
|
|
||||||
assert(descriptors.size == 1)
|
|
||||||
val descriptor = descriptors.first()
|
|
||||||
return descriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun getToString(type: KotlinType): SimpleFunctionDescriptor {
|
private fun getToString(type: KotlinType): SimpleFunctionDescriptor {
|
||||||
val descriptor = type.memberScope.getContributedFunctions(kNameToString, NoLookupLocation.FROM_BACKEND).first()
|
val descriptor = type.memberScope.getContributedFunctions(kNameToString, NoLookupLocation.FROM_BACKEND).first()
|
||||||
return descriptor
|
return descriptor
|
||||||
@@ -1863,81 +1870,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun getThrowNpe(): SimpleFunctionDescriptor {
|
|
||||||
val name = Name.identifier("ThrowNullPointerException")
|
|
||||||
val moduleDescriptor = KonanPlatform.builtIns.builtInsModule
|
|
||||||
val packageDescriptor = moduleDescriptor.getPackage(FqName("konan.internal"))
|
|
||||||
val descriptor = packageDescriptor.memberScope.getContributedFunctions(
|
|
||||||
name, NoLookupLocation.FROM_BACKEND).first()
|
|
||||||
return descriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun evaluateOperatorEqeq(callee: IrBinaryPrimitiveImpl, arg0: LLVMValueRef, arg1: LLVMValueRef): LLVMValueRef {
|
|
||||||
|
|
||||||
val arg0Type = callee.argument0.type
|
|
||||||
val isFp = KotlinBuiltIns.isDouble(arg0Type) || KotlinBuiltIns.isFloat(arg0Type)
|
|
||||||
val isInt = KotlinBuiltIns.isPrimitiveType(arg0Type) && !isFp
|
|
||||||
when {
|
|
||||||
isFp -> return codegen.fcmpEq(arg0, arg1)
|
|
||||||
isInt -> return codegen.icmpEq(arg0, arg1)
|
|
||||||
else -> return generateEqeqForObjects(callee, arg0, arg1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun generateEqeqForObjects(callee: IrBinaryPrimitiveImpl, arg0: LLVMValueRef, arg1: LLVMValueRef): LLVMValueRef {
|
|
||||||
val arg0Type = callee.argument0.type
|
|
||||||
val descriptor = getEquals(arg0Type) // Get descriptor for "arg0.equals".
|
|
||||||
if (arg0Type.isMarkedNullable) { // If arg0 is nullable.
|
|
||||||
val bbEq2 = codegen.basicBlock() // Block to process "eqeq".
|
|
||||||
val bbEq3 = codegen.basicBlock() // Block to process "eqeqeq".
|
|
||||||
val bbExit = codegen.basicBlock() // Exit block for feather generation.
|
|
||||||
val result = codegen.alloca(callee.type)
|
|
||||||
|
|
||||||
val condition = codegen.icmpEq(arg0, codegen.kNullObjHeaderPtr) // Compare arg0 with "null".
|
|
||||||
codegen.condBr(condition, bbEq3, bbEq2) // If (arg0 == null) bbEq3 else bbEq2.
|
|
||||||
|
|
||||||
codegen.positionAtEnd(bbEq3) // Get generation to bbEq3.
|
|
||||||
val resultIdentityEq = evaluateOperatorEqeqeq(callee, arg0, arg1)
|
|
||||||
codegen.store(resultIdentityEq, result) // Store "eqeq" result in "result".
|
|
||||||
codegen.br(bbExit) //
|
|
||||||
|
|
||||||
codegen.positionAtEnd(bbEq2) // Get generation to bbEq2.
|
|
||||||
val resultEquals = evaluateSimpleFunctionCall(descriptor, listOf(arg0, arg1))
|
|
||||||
codegen.store(resultEquals, result) // Store "eqeqeq" result in "result".
|
|
||||||
codegen.br(bbExit) //
|
|
||||||
|
|
||||||
codegen.positionAtEnd(bbExit) // Get generation to bbExit.
|
|
||||||
return codegen.load(result) // Load result.
|
|
||||||
} else {
|
|
||||||
return evaluateSimpleFunctionCall(descriptor, listOf(arg0, arg1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun evaluateOperatorEqeqeq(callee: IrBinaryPrimitiveImpl,
|
|
||||||
arg0: LLVMValueRef, arg1: LLVMValueRef): LLVMValueRef {
|
|
||||||
|
|
||||||
val arg0Type = callee.argument0.type
|
|
||||||
|
|
||||||
return when {
|
|
||||||
KotlinBuiltIns.isPrimitiveType(arg0Type) -> TODO("${ir2string(callee)}")
|
|
||||||
else -> codegen.icmpEq(arg0, arg1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun evaluateOperatorThrowNpe(): LLVMValueRef {
|
|
||||||
val result = evaluateSimpleFunctionCall(getThrowNpe(), listOf())
|
|
||||||
codegen.unreachable()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: LLVMValueRef?, branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) {
|
private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: LLVMValueRef?, branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) {
|
||||||
val neitherUnitNorNothing = !isNothing && !isUnit // If branches doesn't end with 'return' either result hasn't got 'unit' type.
|
val neitherUnitNorNothing = !isNothing && !isUnit // If branches doesn't end with 'return' either result hasn't got 'unit' type.
|
||||||
val branchResult = branch.result
|
val branchResult = branch.result
|
||||||
@@ -2001,7 +1933,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
// In such case it would be possible to check that all args are available and in the correct order.
|
// In such case it would be possible to check that all args are available and in the correct order.
|
||||||
// However, it currently requires some refactoring to be performed.
|
// However, it currently requires some refactoring to be performed.
|
||||||
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
||||||
return currentCodeContext.genCall(function, args)
|
val result = currentCodeContext.genCall(function, args)
|
||||||
|
if (descriptor.returnType?.isNothing() == true) {
|
||||||
|
codegen.unreachable()
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|||||||
+2
-10
@@ -109,15 +109,6 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
|
|
||||||
return if (parameter.containingDeclaration == irBuiltins.eqeq) {
|
|
||||||
this // Do not box arguments of `==` because codegen expects them to be unboxed.
|
|
||||||
// TODO: implement `==` lowering at IR level and remove this hack.
|
|
||||||
} else {
|
|
||||||
this.useAsValue(parameter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the element of [primitiveTypes] if given type is represented as primitive type in generated code,
|
* @return the element of [primitiveTypes] if given type is represented as primitive type in generated code,
|
||||||
* or `null` if represented as object reference.
|
* or `null` if represented as object reference.
|
||||||
@@ -168,7 +159,8 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
|||||||
|
|
||||||
private fun IrExpression.box(primitiveType: KotlinType): IrExpression {
|
private fun IrExpression.box(primitiveType: KotlinType): IrExpression {
|
||||||
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
val primitiveTypeClass = TypeUtils.getClassDescriptor(primitiveType)!!
|
||||||
val boxFunction = context.builtIns.getKonanInternalFunctions("box${primitiveTypeClass.name}").single()
|
val boxFunction = context.builtIns.getKonanInternalFunctions("box${primitiveTypeClass.name}").singleOrNull() ?:
|
||||||
|
TODO(primitiveType.toString())
|
||||||
|
|
||||||
return IrCallImpl(startOffset, endOffset, boxFunction).apply {
|
return IrCallImpl(startOffset, endOffset, boxFunction).apply {
|
||||||
putValueArgument(0, this@box)
|
putValueArgument(0, this@box)
|
||||||
|
|||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||||
|
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.IrTry
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrBinaryPrimitiveImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
|
||||||
|
private val builtIns = context.builtIns
|
||||||
|
private val irBuiltins = context.irModule!!.irBuiltins
|
||||||
|
|
||||||
|
override fun visitTry(aTry: IrTry): IrExpression {
|
||||||
|
// Workaround for the bug in IrTryImpl.transformChildren: transform `finallyExpression` too.
|
||||||
|
// TODO: fix the bug and remove it.
|
||||||
|
val transformer = this
|
||||||
|
return (aTry as IrTryImpl).apply {
|
||||||
|
tryResult = tryResult.transform(transformer, null)
|
||||||
|
catches.forEachIndexed { i, irCatch ->
|
||||||
|
catches[i] = irCatch.transform(transformer, null)
|
||||||
|
}
|
||||||
|
finallyExpression = finallyExpression?.transform(transformer, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
if (expression.descriptor is IrBuiltinOperatorDescriptor) {
|
||||||
|
return transformBuiltinOperator(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformBuiltinOperator(expression: IrCall): IrExpression {
|
||||||
|
val descriptor = expression.descriptor
|
||||||
|
|
||||||
|
return when (descriptor) {
|
||||||
|
irBuiltins.eqeq -> {
|
||||||
|
val binary = expression as IrBinaryPrimitiveImpl
|
||||||
|
lowerEqeq(binary.argument0, binary.argument1, expression.startOffset, expression.endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
irBuiltins.throwNpe -> IrCallImpl(expression.startOffset, expression.endOffset,
|
||||||
|
builtIns.getKonanInternalFunctions("ThrowNullPointerException").single())
|
||||||
|
|
||||||
|
else -> expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lowerEqeq(lhs: IrExpression, rhs: IrExpression, startOffset: Int, endOffset: Int): IrExpression {
|
||||||
|
// TODO: optimize boxing?
|
||||||
|
|
||||||
|
// TODO: areEqualByValue 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.areEqualByValue` intrinsic:
|
||||||
|
val equals = builtIns.getKonanInternalFunctions("areEqualByValue").firstOrNull {
|
||||||
|
lhs.type.isSubtypeOf(it.valueParameters[0].type) && rhs.type.isSubtypeOf(it.valueParameters[1].type)
|
||||||
|
} ?: builtIns.getKonanInternalFunctions("areEqual").single() // or use the general implementation.
|
||||||
|
|
||||||
|
return IrCallImpl(startOffset, endOffset, equals).apply {
|
||||||
|
putValueArgument(0, lhs)
|
||||||
|
putValueArgument(1, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -528,6 +528,11 @@ task boxing12(type: RunKonanTest) {
|
|||||||
source = "codegen/boxing/boxing12.kt"
|
source = "codegen/boxing/boxing12.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task boxing13(type: RunKonanTest) {
|
||||||
|
goldValue = "false\nfalse\ntrue\ntrue\nfalse\nfalse\n"
|
||||||
|
source = "codegen/boxing/boxing13.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task interface0(type: RunKonanTest) {
|
task interface0(type: RunKonanTest) {
|
||||||
goldValue = "PASSED\n"
|
goldValue = "PASSED\n"
|
||||||
source = "runtime/basic/interface0.kt"
|
source = "runtime/basic/interface0.kt"
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fun is42(x: Any?) {
|
||||||
|
println(x == 42)
|
||||||
|
println(42 == x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
is42(16)
|
||||||
|
is42(42)
|
||||||
|
is42("42")
|
||||||
|
}
|
||||||
@@ -16,3 +16,9 @@ annotation class ExportForCppRuntime(val name: String = "")
|
|||||||
// in the absence of IR.
|
// in the absence of IR.
|
||||||
annotation class HasBackingField
|
annotation class HasBackingField
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation denotes that the element is intrinsic and its usages require special handling in compiler.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Intrinsic
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package konan.internal
|
||||||
|
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Boolean, second: Boolean): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Char, second: Char): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Byte, second: Byte): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Short, second: Short): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Int, second: Int): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Long, second: Long): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Float, second: Float): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Double, second: Double): Boolean
|
||||||
|
|
||||||
|
// For comparing with `null`:
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Nothing?, second: Any?): Boolean
|
||||||
|
@Intrinsic external fun areEqualByValue(first: Any?, second: Nothing?): Boolean
|
||||||
|
|
||||||
|
inline fun areEqual(first: Any?, second: Any?): Boolean {
|
||||||
|
return if (first == null) second == null else first.equals(second)
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
package konan.internal
|
package konan.internal
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun ThrowNullPointerException() {
|
internal fun ThrowNullPointerException(): Nothing {
|
||||||
throw NullPointerException()
|
throw NullPointerException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun ThrowArrayIndexOutOfBoundsException() {
|
internal fun ThrowArrayIndexOutOfBoundsException(): Nothing {
|
||||||
throw IndexOutOfBoundsException()
|
throw IndexOutOfBoundsException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun ThrowClassCastException() {
|
internal fun ThrowClassCastException(): Nothing {
|
||||||
throw ClassCastException()
|
throw ClassCastException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user