Support unsigned range and progression values in range intrinsics

This commit is contained in:
Dmitry Petrov
2018-12-28 15:00:39 +03:00
parent 68e2d8dcd9
commit 54cba32426
14 changed files with 321 additions and 60 deletions
@@ -36,47 +36,46 @@ private val PROGRESSION_TO_ELEMENT_TYPE: Map<FqName, PrimitiveType> =
}
fun isPrimitiveRange(rangeType: KotlinType) =
!rangeType.isMarkedNullable && getPrimitiveRangeElementType(rangeType) != null
isClassTypeWithFqn(rangeType, PRIMITIVE_RANGE_FQNS)
fun isUnsignedRange(rangeType: KotlinType): Boolean =
isClassTypeWithFqn(rangeType, UNSIGNED_RANGE_FQNS)
fun isPrimitiveProgression(rangeType: KotlinType) =
!rangeType.isMarkedNullable && getPrimitiveProgressionElementType(rangeType) != null
isClassTypeWithFqn(rangeType, PRIMITIVE_PROGRESSION_FQNS)
fun getPrimitiveRangeElementType(rangeType: KotlinType): PrimitiveType? =
getPrimitiveRangeOrProgressionElementType(
rangeType,
RANGE_TO_ELEMENT_TYPE
)
fun isUnsignedProgression(rangeType: KotlinType) =
isClassTypeWithFqn(rangeType, UNSIGNED_PROGRESSION_FQNS)
private fun getPrimitiveProgressionElementType(rangeType: KotlinType) =
getPrimitiveRangeOrProgressionElementType(
rangeType,
PROGRESSION_TO_ELEMENT_TYPE
)
private fun getPrimitiveRangeOrProgressionElementType(
rangeOrProgression: KotlinType,
map: Map<FqName, PrimitiveType>
): PrimitiveType? {
val declarationDescriptor = rangeOrProgression.constructor.declarationDescriptor ?: return null
val fqName = DescriptorUtils.getFqName(declarationDescriptor).takeIf { it.isSafe } ?: return null
return map[fqName.toSafe()]
private fun isClassTypeWithFqn(kotlinType: KotlinType, fqns: Set<String>): Boolean {
val declarationDescriptor = kotlinType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
val fqName = DescriptorUtils.getFqName(declarationDescriptor).takeIf { it.isSafe } ?: return false
return fqName.asString() in fqns
}
private const val CHAR_RANGE_FQN = "kotlin.ranges.CharRange"
private const val INT_RANGE_FQN = "kotlin.ranges.IntRange"
private const val LONG_RANGE_FQN = "kotlin.ranges.LongRange"
private val PRIMITIVE_RANGE_FQNS = setOf(CHAR_RANGE_FQN, INT_RANGE_FQN, LONG_RANGE_FQN)
private const val CHAR_PROGRESSION_FQN = "kotlin.ranges.CharProgression"
private const val INT_PROGRESSION_FQN = "kotlin.ranges.IntProgression"
private const val LONG_PROGRESSION_FQN = "kotlin.ranges.LongProgression"
private val PRIMITIVE_PROGRESSION_FQNS = setOf(CHAR_PROGRESSION_FQN, INT_PROGRESSION_FQN, LONG_PROGRESSION_FQN)
private const val CLOSED_FLOAT_RANGE_FQN = "kotlin.ranges.ClosedFloatRange"
private const val CLOSED_DOUBLE_RANGE_FQN = "kotlin.ranges.ClosedDoubleRange"
private const val CLOSED_RANGE_FQN = "kotlin.ranges.ClosedRange"
private const val CLOSED_FLOATING_POINT_RANGE_FQN = "kotlin.ranges.ClosedFloatingPointRange"
private const val COMPARABLE_RANGE_FQN = "kotlin.ranges.ComparableRange"
private const val UINT_RANGE_FQN = "kotlin.ranges.UIntRange"
private const val ULONG_RANGE_FQN = "kotlin.ranges.ULongRange"
private val UNSIGNED_RANGE_FQNS = setOf(UINT_RANGE_FQN, ULONG_RANGE_FQN)
private const val UINT_PROGRESSION_FQN = "kotlin.ranges.UIntProgression"
private const val ULONG_PROGRESSION_FQN = "kotlin.ranges.ULongProgression"
private val UNSIGNED_PROGRESSION_FQNS = setOf(UINT_PROGRESSION_FQN, ULONG_PROGRESSION_FQN)
fun getRangeOrProgressionElementType(rangeType: KotlinType): KotlinType? {
val rangeClassDescriptor = rangeType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
@@ -62,9 +62,9 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
)
}
isPrimitiveRange(rangeType) ->
isPrimitiveRange(rangeType) || isUnsignedRange(rangeType) ->
PrimitiveRangeRangeValue(rangeExpression)
isPrimitiveProgression(rangeType) ->
isPrimitiveProgression(rangeType) || isUnsignedProgression(rangeType) ->
PrimitiveProgressionRangeValue(rangeExpression)
isSubtypeOfString(rangeType, builtIns) && isCharSequenceIteratorCall(loopRangeIteratorResolvedCall) ->
CharSequenceRangeValue(true, AsmTypes.JAVA_STRING_TYPE)
@@ -75,6 +75,7 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
}
}
@Suppress("DEPRECATION")
fun isLocalVarReference(rangeExpression: KtExpression, bindingContext: BindingContext): Boolean {
if (rangeExpression !is KtSimpleNameExpression) return false
val resultingDescriptor = rangeExpression.getResolvedCall(bindingContext)?.resultingDescriptor ?: return false
@@ -35,6 +35,10 @@ interface ComparisonGenerator {
fun jumpIfLess(v: InstructionAdapter, label: Label)
}
interface SignedIntegerComparisonGenerator : ComparisonGenerator {
fun jumpIfLessThanZero(v: InstructionAdapter, label: Label)
}
fun getComparisonGeneratorForKotlinType(kotlinType: KotlinType): ComparisonGenerator =
when {
KotlinBuiltIns.isChar(kotlinType) ->
@@ -20,7 +20,7 @@ import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class IntegerComparisonGenerator(override val comparedType: Type) : ComparisonGenerator {
class IntegerComparisonGenerator(override val comparedType: Type) : SignedIntegerComparisonGenerator {
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
v.ificmpge(label)
}
@@ -36,6 +36,10 @@ class IntegerComparisonGenerator(override val comparedType: Type) : ComparisonGe
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
v.ificmplt(label)
}
override fun jumpIfLessThanZero(v: InstructionAdapter, label: Label) {
v.iflt(label)
}
}
val IntComparisonGenerator = IntegerComparisonGenerator(Type.INT_TYPE)
@@ -20,7 +20,7 @@ import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
object LongComparisonGenerator : ComparisonGenerator {
object LongComparisonGenerator : SignedIntegerComparisonGenerator {
override val comparedType: Type = Type.LONG_TYPE
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
@@ -42,4 +42,10 @@ object LongComparisonGenerator : ComparisonGenerator {
v.lcmp()
v.iflt(label)
}
override fun jumpIfLessThanZero(v: InstructionAdapter, label: Label) {
v.lconst(0L)
v.lcmp()
v.iflt(label)
}
}
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.codegen.range.forLoop
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.range.comparison.SignedIntegerComparisonGenerator
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForKotlinType
import org.jetbrains.kotlin.codegen.range.getRangeOrProgressionElementType
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtForExpression
@@ -25,22 +28,34 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
abstract class AbstractForInProgressionLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) :
AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression) {
abstract class AbstractForInProgressionLoopGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression
) : AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression) {
protected var incrementVar: Int = -1
protected val asmLoopRangeType: Type
protected val kotlinLoopRangeType: KotlinType
protected val rangeKotlinType = bindingContext.getType(forExpression.loopRange!!)!!
private val rangeElementKotlinType = getRangeOrProgressionElementType(rangeKotlinType)
?: throw AssertionError("Unexpected loop range type: $rangeKotlinType")
private val incrementKotlinType: KotlinType
protected val incrementType: Type
init {
kotlinLoopRangeType = bindingContext.getType(forExpression.loopRange!!)!!
asmLoopRangeType = codegen.asmType(kotlinLoopRangeType)
asmLoopRangeType = codegen.asmType(rangeKotlinType)
val incrementProp = kotlinLoopRangeType.memberScope.getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND)
assert(incrementProp.size == 1) { kotlinLoopRangeType.toString() + " " + incrementProp.size }
incrementType = codegen.asmType(incrementProp.iterator().next().type)
val incrementProp = rangeKotlinType.memberScope.getContributedVariables(Name.identifier("step"), NoLookupLocation.FROM_BACKEND)
assert(incrementProp.size == 1) { rangeKotlinType.toString() + " " + incrementProp.size }
incrementKotlinType = incrementProp.single().type
incrementType = codegen.asmType(incrementKotlinType)
}
private val incrementComparisonGenerator =
getComparisonGeneratorForKotlinType(incrementKotlinType) as? SignedIntegerComparisonGenerator
?: throw AssertionError("Unexpected increment type: $incrementKotlinType")
private val elementComparisonGenerator = getComparisonGeneratorForKotlinType(rangeElementKotlinType)
override fun beforeLoop() {
super.beforeLoop()
@@ -59,33 +74,12 @@ abstract class AbstractForInProgressionLoopGenerator(codegen: ExpressionCodegen,
val negativeIncrement = Label()
val afterIf = Label()
if (asmElementType.sort == Type.LONG) {
v.lconst(0L)
v.lcmp()
v.ifle(negativeIncrement) // if increment < 0, jump
// increment > 0
v.lcmp()
v.ifgt(loopExit)
v.goTo(afterIf)
// increment < 0
v.mark(negativeIncrement)
v.lcmp()
v.iflt(loopExit)
v.mark(afterIf)
} else {
v.ifle(negativeIncrement) // if increment < 0, jump
// increment > 0
v.ificmpgt(loopExit)
v.goTo(afterIf)
// increment < 0
v.mark(negativeIncrement)
v.ificmplt(loopExit)
v.mark(afterIf)
}
incrementComparisonGenerator.jumpIfLessThanZero(v, negativeIncrement)
elementComparisonGenerator.jumpIfGreater(v, loopExit)
v.goTo(afterIf)
v.mark(negativeIncrement)
elementComparisonGenerator.jumpIfLess(v, loopExit)
v.mark(afterIf)
}
override fun assignToLoopParameter() {}
@@ -25,8 +25,9 @@ class ForInProgressionExpressionLoopGenerator(
forExpression: KtForExpression,
private val rangeExpression: KtExpression
) : AbstractForInProgressionLoopGenerator(codegen, forExpression) {
override fun storeProgressionParametersToLocalVars() {
codegen.gen(rangeExpression, asmLoopRangeType, kotlinLoopRangeType)
codegen.gen(rangeExpression, asmLoopRangeType, rangeKotlinType)
v.dup()
v.dup()
@@ -0,0 +1,101 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
const val MaxUI = UInt.MAX_VALUE
const val MinUI = UInt.MIN_VALUE
const val MaxUL = ULong.MAX_VALUE
const val MinUL = ULong.MIN_VALUE
val M = MaxUI.toULong()
val p1 = 6u downTo 1u
fun testSimpleUIntLoop() {
var s = 0
for (i in p1) {
s = s*10 + i.toInt()
}
if (s != 654321) throw AssertionError("$s")
}
val p2 = 1u downTo 6u
fun testEmptyUIntLoop() {
var s = 0
for (i in p2) {
s = s*10 + i.toInt()
}
if (s != 0) throw AssertionError("$s")
}
val p3 = 6UL downTo 1UL
fun testSimpleULongLoop() {
var s = 0
for (i in p3) {
s = s*10 + i.toInt()
}
if (s != 654321) throw AssertionError("$s")
}
val p4 = 1UL downTo 6UL
fun testEmptyULongLoop() {
var s = 0
for (i in p4) {
s = s*10 + i.toInt()
}
if (s != 0) throw AssertionError("$s")
}
val p5 = M + 6UL downTo M + 1UL
fun testULongLoop() {
var s = 0
for (i in p5) {
s = s*10 + (i-M).toInt()
}
if (s != 654321) throw AssertionError("$s")
}
val p6 = M + 1UL downTo M + 6UL
fun testEmptyULongLoop2() {
var s = 0
for (i in p6) {
s = s*10 + (i-M).toInt()
}
if (s != 0) throw AssertionError("$s")
}
val p7 = MinUI downTo MaxUI
fun testMaxUIdownToMinUI() {
val xs = ArrayList<UInt>()
for (i in p7) {
xs.add(i)
if (xs.size > 23) break
}
if (xs.size > 0) {
throw AssertionError("Wrong elements for MaxUI..MinUI: $xs")
}
}
val p8 = MinUL downTo MaxUL
fun testMaxULdownToMinUL() {
val xs = ArrayList<ULong>()
for (i in p8) {
xs.add(i)
if (xs.size > 23) break
}
if (xs.size > 0) {
throw AssertionError("Wrong elements for MaxUI..MinUI: $xs")
}
}
fun box(): String {
testSimpleUIntLoop()
testEmptyUIntLoop()
testSimpleULongLoop()
testEmptyULongLoop()
testULongLoop()
testEmptyULongLoop2()
testMaxUIdownToMinUI()
testMaxULdownToMinUL()
return "OK"
}
@@ -0,0 +1,101 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
const val MaxUI = UInt.MAX_VALUE
const val MinUI = UInt.MIN_VALUE
const val MaxUL = ULong.MAX_VALUE
const val MinUL = ULong.MIN_VALUE
val M = MaxUI.toULong()
val range1 = 1u .. 6u
fun testSimpleUIntLoop() {
var s = 0
for (i in range1) {
s = s*10 + i.toInt()
}
if (s != 123456) throw AssertionError("$s")
}
val range2 = 6u .. 1u
fun testEmptyUIntLoop() {
var s = 0
for (i in range2) {
s = s*10 + i.toInt()
}
if (s != 0) throw AssertionError("$s")
}
val range3 = 1UL .. 6UL
fun testSimpleULongLoop() {
var s = 0
for (i in range3) {
s = s*10 + i.toInt()
}
if (s != 123456) throw AssertionError("$s")
}
val range4 = 6UL .. 1UL
fun testEmptyULongLoop() {
var s = 0
for (i in range4) {
s = s*10 + i.toInt()
}
if (s != 0) throw AssertionError("$s")
}
val range5 = M+1UL..M+6UL
fun testULongLoop() {
var s = 0
for (i in range5) {
s = s*10 + (i-M).toInt()
}
if (s != 123456) throw AssertionError("$s")
}
val range6 = M+6UL..M+1UL
fun testEmptyULongLoop2() {
var s = 0
for (i in range6) {
s = s*10 + (i-M).toInt()
}
if (s != 0) throw AssertionError("$s")
}
val range7 = MaxUI..MinUI
fun testMaxUItoMinUI() {
val xs = ArrayList<UInt>()
for (i in range7) {
xs.add(i)
if (xs.size > 23) break
}
if (xs.size > 0) {
throw AssertionError("Wrong elements for MaxUI..MinUI: $xs")
}
}
val range8 = MaxUL..MinUL
fun testMaxULtoMinUL() {
val xs = ArrayList<ULong>()
for (i in range8) {
xs.add(i)
if (xs.size > 23) break
}
if (xs.size > 0) {
throw AssertionError("Wrong elements for MaxUI..MinUI: $xs")
}
}
fun box(): String {
testSimpleUIntLoop()
testEmptyUIntLoop()
testSimpleULongLoop()
testEmptyULongLoop()
testULongLoop()
testEmptyULongLoop2()
testMaxUItoMinUI()
testMaxULtoMinUL()
return "OK"
}
@@ -24198,6 +24198,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedDownTo.kt");
}
@TestMetadata("forInUnsignedProgression.kt")
public void testForInUnsignedProgression() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedProgression.kt");
}
@TestMetadata("forInUnsignedRange.kt")
public void testForInUnsignedRange() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRange.kt");
}
@TestMetadata("forInUnsignedRangeLiteral.kt")
public void testForInUnsignedRangeLiteral() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRangeLiteral.kt");
@@ -24198,6 +24198,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedDownTo.kt");
}
@TestMetadata("forInUnsignedProgression.kt")
public void testForInUnsignedProgression() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedProgression.kt");
}
@TestMetadata("forInUnsignedRange.kt")
public void testForInUnsignedRange() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRange.kt");
}
@TestMetadata("forInUnsignedRangeLiteral.kt")
public void testForInUnsignedRangeLiteral() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRangeLiteral.kt");
@@ -24203,6 +24203,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedDownTo.kt");
}
@TestMetadata("forInUnsignedProgression.kt")
public void testForInUnsignedProgression() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedProgression.kt");
}
@TestMetadata("forInUnsignedRange.kt")
public void testForInUnsignedRange() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRange.kt");
}
@TestMetadata("forInUnsignedRangeLiteral.kt")
public void testForInUnsignedRangeLiteral() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRangeLiteral.kt");
@@ -18648,6 +18648,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedDownTo.kt");
}
@TestMetadata("forInUnsignedProgression.kt")
public void testForInUnsignedProgression() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedProgression.kt");
}
@TestMetadata("forInUnsignedRange.kt")
public void testForInUnsignedRange() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRange.kt");
}
@TestMetadata("forInUnsignedRangeLiteral.kt")
public void testForInUnsignedRangeLiteral() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRangeLiteral.kt");
@@ -19698,6 +19698,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedDownTo.kt");
}
@TestMetadata("forInUnsignedProgression.kt")
public void testForInUnsignedProgression() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedProgression.kt");
}
@TestMetadata("forInUnsignedRange.kt")
public void testForInUnsignedRange() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRange.kt");
}
@TestMetadata("forInUnsignedRangeLiteral.kt")
public void testForInUnsignedRangeLiteral() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/forInUnsignedRangeLiteral.kt");