KT-29229 Intrinsify 'in' operator for unsigned integer ranges

Support mixed type case, e.g., '[UByte] in [UIntRange]'.
This commit is contained in:
Dmitry Petrov
2019-05-29 17:12:01 +03:00
parent 5426071102
commit 0531bd4fe6
14 changed files with 388 additions and 93 deletions
@@ -17,12 +17,11 @@
package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForKotlinType
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
import org.jetbrains.kotlin.codegen.range.comparison.getRangeContainsTypeInfo
import org.jetbrains.kotlin.codegen.range.forLoop.ForInDefinitelySafeSimpleProgressionLoopGenerator
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Type
@@ -60,22 +58,20 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
operatorReference: KtSimpleNameExpression,
resolvedCall: ResolvedCall<out CallableDescriptor>
): InExpressionGenerator {
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
val comparedType = comparisonGenerator?.comparedType
val rangeContainsTypeInfo = getRangeContainsTypeInfo(resolvedCall)
?: return CallBasedInExpressionGenerator(codegen, operatorReference)
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, rangeContainsTypeInfo)
?: return CallBasedInExpressionGenerator(codegen, operatorReference)
return when {
comparisonGenerator == null -> CallBasedInExpressionGenerator(codegen, operatorReference)
comparedType == Type.DOUBLE_TYPE || comparedType == Type.FLOAT_TYPE -> {
return when (comparisonGenerator.comparedType) {
Type.DOUBLE_TYPE, Type.FLOAT_TYPE -> {
val rangeLiteral = getBoundedValue(codegen) as? BoundedValue
?: throw AssertionError("Floating point intrinsic range value should be a range literal")
InFloatingPointRangeLiteralExpressionGenerator(operatorReference, rangeLiteral, comparisonGenerator, codegen.frameMap)
}
else ->
InIntegralContinuousRangeExpressionGenerator(
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
)
else -> InIntegralContinuousRangeExpressionGenerator(
operatorReference, rangeContainsTypeInfo, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
)
}
}
@@ -94,71 +90,6 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
}
}
private val StackValue.unsignedType: UnsignedType?
get() = kotlinType?.let { UnsignedTypes.toUnsignedType(it) }
private fun coerceUnsignedToUInt(stackValue: StackValue, uIntKotlinType: KotlinType): StackValue {
val valueKotlinType = stackValue.kotlinType
val valueUnsignedType = stackValue.unsignedType
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
if (valueUnsignedType == UnsignedType.UINT) return stackValue
return StackValue.operation(Type.INT_TYPE, uIntKotlinType) { v ->
stackValue.put(stackValue.type, valueKotlinType, v)
when (valueUnsignedType) {
UnsignedType.UBYTE -> {
v.iconst(0xFF)
v.and(Type.INT_TYPE)
}
UnsignedType.USHORT -> {
v.iconst(0xFFFF)
v.and(Type.INT_TYPE)
}
UnsignedType.ULONG -> {
v.cast(Type.LONG_TYPE, Type.INT_TYPE)
}
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
}
}
}
private fun coerceUnsignedToULong(stackValue: StackValue, uLongKotlinType: KotlinType): StackValue {
val valueKotlinType = stackValue.kotlinType
val valueUnsignedType = stackValue.unsignedType
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
if (valueUnsignedType == UnsignedType.ULONG) return stackValue
return StackValue.operation(Type.LONG_TYPE, uLongKotlinType) { v ->
stackValue.put(stackValue.type, valueKotlinType, v)
when (valueUnsignedType) {
UnsignedType.UBYTE -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFF)
v.and(Type.LONG_TYPE)
}
UnsignedType.USHORT -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFFFF)
v.and(Type.LONG_TYPE)
}
UnsignedType.UINT -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFFFF_FFFFL)
v.and(Type.LONG_TYPE)
}
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
}
}
}
protected fun createConstBoundedForLoopGeneratorOrNull(
codegen: ExpressionCodegen,
forExpression: KtForExpression,
@@ -251,7 +182,7 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
)
private fun isProhibitedCharConstEndValue(step: Int, endValue: Char) =
endValue == if (step == 1) java.lang.Character.MAX_VALUE else java.lang.Character.MIN_VALUE
endValue == if (step == 1) Char.MAX_VALUE else Char.MIN_VALUE
private fun isProhibitedIntConstEndValue(step: Int, endValue: Int) =
endValue == if (step == 1) Int.MAX_VALUE else Int.MIN_VALUE
@@ -209,10 +209,12 @@ fun isPrimitiveRangeContains(descriptor: CallableDescriptor): Boolean {
fun isUnsignedIntegerRangeContains(descriptor: CallableDescriptor): Boolean {
if (descriptor.name.asString() != "contains") return false
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false
if (!isUnsignedRange(dispatchReceiverType)) return false
return true
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
return (dispatchReceiverType != null && isUnsignedRange(dispatchReceiverType)) ||
(extensionReceiverType != null && isUnsignedRange(extensionReceiverType))
}
fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableDescriptor): Boolean {
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Type
val StackValue.unsignedType: UnsignedType?
get() = kotlinType?.let { UnsignedTypes.toUnsignedType(it) }
fun coerceUnsignedToUInt(stackValue: StackValue, uIntKotlinType: KotlinType): StackValue =
coerceUnsignedToUInt(stackValue, stackValue.kotlinType, uIntKotlinType)
fun coerceUnsignedToUInt(
stackValue: StackValue,
valueKotlinType: KotlinType?,
uIntKotlinType: KotlinType
): StackValue {
val valueUnsignedType = stackValue.unsignedType
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
if (valueUnsignedType == UnsignedType.UINT) return stackValue
return StackValue.operation(Type.INT_TYPE, uIntKotlinType) { v ->
stackValue.put(stackValue.type, valueKotlinType, v)
when (valueUnsignedType) {
UnsignedType.UBYTE -> {
v.iconst(0xFF)
v.and(Type.INT_TYPE)
}
UnsignedType.USHORT -> {
v.iconst(0xFFFF)
v.and(Type.INT_TYPE)
}
UnsignedType.ULONG -> {
v.cast(Type.LONG_TYPE, Type.INT_TYPE)
}
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
}
}
}
fun coerceUnsignedToULong(stackValue: StackValue, uLongKotlinType: KotlinType): StackValue =
coerceUnsignedToULong(stackValue, stackValue.kotlinType, uLongKotlinType)
fun coerceUnsignedToULong(
stackValue: StackValue,
valueKotlinType: KotlinType?,
uLongKotlinType: KotlinType
): StackValue {
val valueUnsignedType = stackValue.unsignedType
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
if (valueUnsignedType == UnsignedType.ULONG) return stackValue
return StackValue.operation(Type.LONG_TYPE, uLongKotlinType) { v ->
stackValue.put(stackValue.type, valueKotlinType, v)
when (valueUnsignedType) {
UnsignedType.UBYTE -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFF)
v.and(Type.LONG_TYPE)
}
UnsignedType.USHORT -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFFFF)
v.and(Type.LONG_TYPE)
}
UnsignedType.UINT -> {
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
v.lconst(0xFFFF_FFFFL)
v.and(Type.LONG_TYPE)
}
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
}
}
}
@@ -59,17 +59,25 @@ fun getComparisonGeneratorForKotlinType(kotlinType: KotlinType): ComparisonGener
throw UnsupportedOperationException("Unexpected element type: $kotlinType")
}
class RangeContainsTypeInfo(
val rangeElementType: KotlinType,
val valueParameterType: KotlinType
)
fun getRangeContainsTypeInfo(call: ResolvedCall<out CallableDescriptor>): RangeContainsTypeInfo? {
val descriptor = call.resultingDescriptor
val receiverType = descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type ?: return null
val elementType = getRangeOrProgressionElementType(receiverType) ?: return null
val valueParameterType = descriptor.valueParameters.singleOrNull()?.type ?: return null
return RangeContainsTypeInfo(elementType, valueParameterType)
}
fun getComparisonGeneratorForRangeContainsCall(
codegen: ExpressionCodegen,
call: ResolvedCall<out CallableDescriptor>
rangeContainsTypeInfo: RangeContainsTypeInfo
): ComparisonGenerator? {
val descriptor = call.resultingDescriptor
val receiverType = descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type ?: return null
val elementType = getRangeOrProgressionElementType(receiverType) ?: return null
val valueParameterType = descriptor.valueParameters.singleOrNull()?.type ?: return null
val elementType = rangeContainsTypeInfo.rangeElementType
val valueParameterType = rangeContainsTypeInfo.valueParameterType
val asmElementType = codegen.asmType(elementType)
val asmValueParameterType = codegen.asmType(valueParameterType)
@@ -78,6 +86,12 @@ fun getComparisonGeneratorForRangeContainsCall(
asmElementType == asmValueParameterType ->
getComparisonGeneratorForKotlinType(elementType)
KotlinBuiltIns.isUInt(elementType) ->
UIntComparisonGenerator
KotlinBuiltIns.isULong(elementType) ->
ULongComparisonGenerator
asmElementType.isPrimitiveIntOrCoercible() && asmValueParameterType.isPrimitiveIntOrCoercible() ->
IntComparisonGenerator
@@ -16,9 +16,13 @@
package org.jetbrains.kotlin.codegen.range.inExpression
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.range.BoundedValue
import org.jetbrains.kotlin.codegen.range.coerceUnsignedToUInt
import org.jetbrains.kotlin.codegen.range.coerceUnsignedToULong
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
import org.jetbrains.kotlin.codegen.range.comparison.RangeContainsTypeInfo
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.org.objectweb.asm.Label
@@ -27,6 +31,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class InIntegralContinuousRangeExpressionGenerator(
operatorReference: KtSimpleNameExpression,
private val rangeContainsTypeInfo: RangeContainsTypeInfo,
private val boundedValue: BoundedValue,
private val comparisonGenerator: ComparisonGenerator,
private val frameMap: FrameMap
@@ -38,6 +43,7 @@ class InIntegralContinuousRangeExpressionGenerator(
private fun gen(argument: StackValue): BranchedValue =
object : BranchedValue(argument, null, comparisonGenerator.comparedType, Opcodes.IFEQ) {
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
if (jumpIfFalse) {
genJumpIfFalse(v, jumpLabel)
@@ -54,7 +60,7 @@ class InIntegralContinuousRangeExpressionGenerator(
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
putCoercedArgumentOnStack(v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
@@ -95,7 +101,7 @@ class InIntegralContinuousRangeExpressionGenerator(
boundedValue.putHighLow(v, operandType)
arg1.put(operandType, v)
putCoercedArgumentOnStack(v)
v.store(arg1Var, operandType)
v.load(arg1Var, operandType)
@@ -127,5 +133,21 @@ class InIntegralContinuousRangeExpressionGenerator(
}
}
private fun putCoercedArgumentOnStack(v: InstructionAdapter) {
val argumentKotlinType = rangeContainsTypeInfo.valueParameterType
val rangeElementKotlinType = rangeContainsTypeInfo.rangeElementType
val coercedValue = when {
KotlinBuiltIns.isUInt(rangeElementKotlinType) ->
coerceUnsignedToUInt(arg1, argumentKotlinType, rangeElementKotlinType)
KotlinBuiltIns.isULong(rangeElementKotlinType) ->
coerceUnsignedToULong(arg1, argumentKotlinType, rangeElementKotlinType)
else ->
arg1
}
coercedValue.put(operandType, v)
}
}
}