From 16ab11289c494c95d67ec0a89fa6d9b5d036dab8 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Thu, 3 Sep 2020 00:49:14 -0700 Subject: [PATCH] [JS_IR] Enable RangeContainsLowering optimizations. --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 ++++ .../box/ranges/contains/inComparableRange.kt | 27 ++++++------- .../box/ranges/contains/inExtensionRange.kt | 18 ++++----- .../codegen/box/ranges/contains/inIntRange.kt | 20 ++++------ .../contains/inOptimizableDoubleRange.kt | 38 +++++++++---------- .../contains/inOptimizableFloatRange.kt | 38 +++++++++---------- .../ranges/contains/inOptimizableIntRange.kt | 26 ++++++------- .../ranges/contains/inOptimizableLongRange.kt | 26 ++++++------- .../contains/inRangeWithCustomContains.kt | 10 ++--- .../contains/inRangeWithImplicitReceiver.kt | 14 +++---- .../inRangeWithNonmatchingArguments.kt | 18 ++++----- 11 files changed, 105 insertions(+), 137 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 113211a6120..68a7a970e8f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -335,6 +335,12 @@ private val returnableBlockLoweringPhase = makeBodyLoweringPhase( prerequisite = setOf(functionInliningPhase) ) +private val rangeContainsLoweringPhase = makeBodyLoweringPhase( + ::RangeContainsLowering, + name = "RangeContainsLowering", + description = "[Optimization] Optimizes calls to contains() for ClosedRanges" +) + private val forLoopsLoweringPhase = makeBodyLoweringPhase( ::ForLoopsLowering, name = "ForLoopsLowering", @@ -703,6 +709,7 @@ val loweringList = listOf( propertyReferenceLoweringPhase, interopCallableReferenceLoweringPhase, returnableBlockLoweringPhase, + rangeContainsLoweringPhase, forLoopsLoweringPhase, primitiveCompanionLoweringPhase, propertyAccessorInlinerLoweringPhase, diff --git a/compiler/testData/codegen/box/ranges/contains/inComparableRange.kt b/compiler/testData/codegen/box/ranges/contains/inComparableRange.kt index 8e4fdb3c09a..8aad5a2332d 100644 --- a/compiler/testData/codegen/box/ranges/contains/inComparableRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inComparableRange.kt @@ -1,9 +1,5 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* class ComparablePair>(val first: T, val second: T) : Comparable> { override fun compareTo(other: ComparablePair): Int { @@ -21,32 +17,33 @@ operator fun Float.rangeTo(other: Float) = object : ClosedFloatingPointRange= b } +// assert\((.*)\) \{\s*(".*")\s*} fun check(x: Double, left: Double, right: Double): Boolean { val result = x in left..right val range = left..right - assert(result == x in range) { "Failed: unoptimized === unoptimized for custom double $range" } + assertTrue(result == x in range, "Failed: unoptimized === unoptimized for custom double $range") return result } fun check(x: Float, left: Float, right: Float): Boolean { val result = x in left..right val range = left..right - assert(result == x in range) { "Failed: unoptimized === unoptimized for standard float $range" } + assertTrue(result == x in range, "Failed: unoptimized === unoptimized for standard float $range") return result } fun box(): String { - assert("a" !in "b".."c") - assert("b" in "a".."d") + assertTrue("a" !in "b".."c") + assertTrue("b" in "a".."d") - assert(ComparablePair(2, 2) !in ComparablePair(1, 10)..ComparablePair(2, 1)) - assert(ComparablePair(2, 2) in ComparablePair(2, 0)..ComparablePair(2, 10)) + assertTrue(ComparablePair(2, 2) !in ComparablePair(1, 10)..ComparablePair(2, 1)) + assertTrue(ComparablePair(2, 2) in ComparablePair(2, 0)..ComparablePair(2, 10)) - assert(!check(-0.0, 0.0, 0.0)) - assert(check(Double.NaN, Double.NaN, Double.NaN)) + assertTrue(!check(-0.0, 0.0, 0.0)) + assertTrue(check(Double.NaN, Double.NaN, Double.NaN)) - assert(check(-0.0f, 0.0f, 0.0f)) - assert(!check(Float.NaN, Float.NaN, Float.NaN)) + assertTrue(check(-0.0f, 0.0f, 0.0f)) + assertTrue(!check(Float.NaN, Float.NaN, Float.NaN)) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inExtensionRange.kt b/compiler/testData/codegen/box/ranges/contains/inExtensionRange.kt index 9e093a1ca2f..f6b5dd0e28e 100644 --- a/compiler/testData/codegen/box/ranges/contains/inExtensionRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inExtensionRange.kt @@ -1,23 +1,19 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* operator fun Int.rangeTo(right: String): ClosedRange = this..this + 1 operator fun Long.rangeTo(right: Double): ClosedRange = this..right.toLong() + 1 operator fun String.rangeTo(right: Int): ClosedRange = this..this fun box(): String { - assert(0 !in 1.."a") - assert(1 in 1.."a") + assertTrue(0 !in 1.."a") + assertTrue(1 in 1.."a") - assert(0L !in 1L..2.0) - assert(2L in 1L..3.0) + assertTrue(0L !in 1L..2.0) + assertTrue(2L in 1L..3.0) - assert("a" !in "b"..1) - assert("a" in "a"..1) + assertTrue("a" !in "b"..1) + assertTrue("a" in "a"..1) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inIntRange.kt b/compiler/testData/codegen/box/ranges/contains/inIntRange.kt index 82472b8910d..488c32c72d9 100644 --- a/compiler/testData/codegen/box/ranges/contains/inIntRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inIntRange.kt @@ -1,23 +1,19 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun box(): String { for (x in 1..10) { - assert(x in 1..10) - assert(x + 10 !in 1..10) + assertTrue(x in 1..10) + assertTrue(x + 10 !in 1..10) } var x = 0 - assert(0 !in 1..2) + assertTrue(0 !in 1..2) - assert(++x in 1..1) - assert(++x !in 1..1) + assertTrue(++x in 1..1) + assertTrue(++x !in 1..1) - assert(sideEffect(x) in 2..3) + assertTrue(sideEffect(x) in 2..3) return "OK" } @@ -25,6 +21,6 @@ fun box(): String { var invocationCounter = 0 fun sideEffect(x: Int): Int { ++invocationCounter - assert(invocationCounter == 1) + assertTrue(invocationCounter == 1) return x } \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt b/compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt index 4c549ed0ab8..7e2835d5809 100644 --- a/compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt @@ -1,16 +1,12 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun check(x: Double, left: Double, right: Double): Boolean { val result = x in left..right val manual = x >= left && x <= right val range = left..right - assert(result == manual) { "Failed: optimized === manual for $range" } - assert(result == checkUnoptimized(x, range)) { "Failed: optimized === unoptimized for $range" } + assertTrue(result == manual, "Failed: optimized === manual for $range") + assertTrue(result == checkUnoptimized(x, range), "Failed: optimized === unoptimized for $range") return result } @@ -19,23 +15,23 @@ fun checkUnoptimized(x: Double, range: ClosedRange): Boolean { } fun box(): String { - assert(check(1.0, 0.0, 2.0)) - assert(!check(1.0, -1.0, 0.0)) + assertTrue(check(1.0, 0.0, 2.0)) + assertTrue(!check(1.0, -1.0, 0.0)) - assert(check(Double.MIN_VALUE, 0.0, 1.0)) - assert(check(Double.MAX_VALUE, Double.MAX_VALUE - Double.MIN_VALUE, Double.MAX_VALUE)) - assert(!check(Double.NaN, Double.NaN, Double.NaN)) - assert(!check(0.0, Double.NaN, Double.NaN)) + assertTrue(check(Double.MIN_VALUE, 0.0, 1.0)) + assertTrue(check(Double.MAX_VALUE, Double.MAX_VALUE - Double.MIN_VALUE, Double.MAX_VALUE)) + assertTrue(!check(Double.NaN, Double.NaN, Double.NaN)) + assertTrue(!check(0.0, Double.NaN, Double.NaN)) - assert(check(-0.0, -0.0, +0.0)) - assert(check(-0.0, -0.0, -0.0)) - assert(check(-0.0, +0.0, +0.0)) - assert(check(+0.0, -0.0, -0.0)) - assert(check(+0.0, +0.0, +0.0)) - assert(check(+0.0, -0.0, +0.0)) + assertTrue(check(-0.0, -0.0, +0.0)) + assertTrue(check(-0.0, -0.0, -0.0)) + assertTrue(check(-0.0, +0.0, +0.0)) + assertTrue(check(+0.0, -0.0, -0.0)) + assertTrue(check(+0.0, +0.0, +0.0)) + assertTrue(check(+0.0, -0.0, +0.0)) var value = 0.0 - assert(++value in 1.0..1.0) - assert(++value !in 1.0..1.0) + assertTrue(++value in 1.0..1.0) + assertTrue(++value !in 1.0..1.0) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inOptimizableFloatRange.kt b/compiler/testData/codegen/box/ranges/contains/inOptimizableFloatRange.kt index 85b2d3a8e38..54392597ad7 100644 --- a/compiler/testData/codegen/box/ranges/contains/inOptimizableFloatRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inOptimizableFloatRange.kt @@ -1,16 +1,12 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun check(x: Float, left: Float, right: Float): Boolean { val result = x in left..right val manual = x >= left && x <= right val range = left..right - assert(result == manual) { "Failed: optimized === manual for $range" } - assert(result == checkUnoptimized(x, range)) { "Failed: optimized === unoptimized for $range" } + assertTrue(result == manual, "Failed: optimized === manual for $range") + assertTrue(result == checkUnoptimized(x, range), "Failed: optimized === unoptimized for $range") return result } @@ -19,23 +15,23 @@ fun checkUnoptimized(x: Float, range: ClosedRange): Boolean { } fun box(): String { - assert(check(1.0f, 0.0f, 2.0f)) - assert(!check(1.0f, -1.0f, 0.0f)) + assertTrue(check(1.0f, 0.0f, 2.0f)) + assertTrue(!check(1.0f, -1.0f, 0.0f)) - assert(check(Float.MIN_VALUE, 0.0f, 1.0f)) - assert(check(Float.MAX_VALUE, Float.MAX_VALUE - Float.MIN_VALUE, Float.MAX_VALUE)) - assert(!check(Float.NaN, Float.NaN, Float.NaN)) - assert(!check(0.0f, Float.NaN, Float.NaN)) + assertTrue(check(Float.MIN_VALUE, 0.0f, 1.0f)) + assertTrue(check(Float.MAX_VALUE, Float.MAX_VALUE - Float.MIN_VALUE, Float.MAX_VALUE)) + assertTrue(!check(Float.NaN, Float.NaN, Float.NaN)) + assertTrue(!check(0.0f, Float.NaN, Float.NaN)) - assert(check(-0.0f, -0.0f, +0.0f)) - assert(check(-0.0f, -0.0f, -0.0f)) - assert(check(-0.0f, +0.0f, +0.0f)) - assert(check(+0.0f, -0.0f, -0.0f)) - assert(check(+0.0f, +0.0f, +0.0f)) - assert(check(+0.0f, -0.0f, +0.0f)) + assertTrue(check(-0.0f, -0.0f, +0.0f)) + assertTrue(check(-0.0f, -0.0f, -0.0f)) + assertTrue(check(-0.0f, +0.0f, +0.0f)) + assertTrue(check(+0.0f, -0.0f, -0.0f)) + assertTrue(check(+0.0f, +0.0f, +0.0f)) + assertTrue(check(+0.0f, -0.0f, +0.0f)) var value = 0.0f - assert(++value in 1.0f..1.0f) - assert(++value !in 1.0f..1.0f) + assertTrue(++value in 1.0f..1.0f) + assertTrue(++value !in 1.0f..1.0f) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inOptimizableIntRange.kt b/compiler/testData/codegen/box/ranges/contains/inOptimizableIntRange.kt index 4b1121cd3c5..377ced92278 100644 --- a/compiler/testData/codegen/box/ranges/contains/inOptimizableIntRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inOptimizableIntRange.kt @@ -1,16 +1,12 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun check(x: Int, left: Int, right: Int): Boolean { val result = x in left..right val manual = x >= left && x <= right val range = left..right - assert(result == manual) { "Failed: optimized === manual for $range" } - assert(result == checkUnoptimized(x, range)) { "Failed: optimized === unoptimized for $range" } + assertTrue(result == manual, "Failed: optimized === manual for $range") + assertTrue(result == checkUnoptimized(x, range), "Failed: optimized === unoptimized for $range") return result } @@ -19,16 +15,16 @@ fun checkUnoptimized(x: Int, range: ClosedRange): Boolean { } fun box(): String { - assert(check(1, 0, 2)) - assert(!check(1, -1, 0)) - assert(!check(239, 239, 238)) - assert(check(239, 238, 239)) + assertTrue(check(1, 0, 2)) + assertTrue(!check(1, -1, 0)) + assertTrue(!check(239, 239, 238)) + assertTrue(check(239, 238, 239)) - assert(check(Int.MIN_VALUE, Int.MIN_VALUE, Int.MIN_VALUE)) - assert(check(Int.MAX_VALUE, Int.MIN_VALUE, Int.MAX_VALUE)) + assertTrue(check(Int.MIN_VALUE, Int.MIN_VALUE, Int.MIN_VALUE)) + assertTrue(check(Int.MAX_VALUE, Int.MIN_VALUE, Int.MAX_VALUE)) var value = 0 - assert(++value in 1..1) - assert(++value !in 1..1) + assertTrue(++value in 1..1) + assertTrue(++value !in 1..1) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inOptimizableLongRange.kt b/compiler/testData/codegen/box/ranges/contains/inOptimizableLongRange.kt index fe8c03ed2df..22a087970f5 100644 --- a/compiler/testData/codegen/box/ranges/contains/inOptimizableLongRange.kt +++ b/compiler/testData/codegen/box/ranges/contains/inOptimizableLongRange.kt @@ -1,16 +1,12 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun check(x: Long, left: Long, right: Long): Boolean { val result = x in left..right val manual = x >= left && x <= right val range = left..right - assert(result == manual) { "Failed: optimized === manual for $range" } - assert(result == checkUnoptimized(x, range)) { "Failed: optimized === unoptimized for $range" } + assertTrue(result == manual, "Failed: optimized === manual for $range") + assertTrue(result == checkUnoptimized(x, range), "Failed: optimized === unoptimized for $range") return result } @@ -19,16 +15,16 @@ fun checkUnoptimized(x: Long, range: ClosedRange): Boolean { } fun box(): String { - assert(check(1L, 0L, 2L)) - assert(!check(1L, -1L, 0L)) - assert(!check(239L, 239L, 238L)) - assert(check(239L, 238L, 239L)) + assertTrue(check(1L, 0L, 2L)) + assertTrue(!check(1L, -1L, 0L)) + assertTrue(!check(239L, 239L, 238L)) + assertTrue(check(239L, 238L, 239L)) - assert(check(Long.MIN_VALUE, Long.MIN_VALUE, Long.MIN_VALUE)) - assert(check(Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE)) + assertTrue(check(Long.MIN_VALUE, Long.MIN_VALUE, Long.MIN_VALUE)) + assertTrue(check(Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE)) var value = 0L - assert(++value in 1L..1L) - assert(++value !in 1L..1L) + assertTrue(++value in 1L..1L) + assertTrue(++value !in 1L..1L) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inRangeWithCustomContains.kt b/compiler/testData/codegen/box/ranges/contains/inRangeWithCustomContains.kt index b8a47f34c99..a03a60539d4 100644 --- a/compiler/testData/codegen/box/ranges/contains/inRangeWithCustomContains.kt +++ b/compiler/testData/codegen/box/ranges/contains/inRangeWithCustomContains.kt @@ -1,9 +1,5 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* class Value(val x: Int) : Comparable { override fun compareTo(other: Value): Int { @@ -22,8 +18,8 @@ class ValueRange(override val start: Value, operator fun Value.rangeTo(other: Value): ClosedRange = ValueRange(this, other) fun box(): String { - assert(Value(42) in Value(1)..Value(2)) - assert(Value(41) !in Value(40)..Value(42)) + assertTrue(Value(42) in Value(1)..Value(2)) + assertTrue(Value(41) !in Value(40)..Value(42)) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inRangeWithImplicitReceiver.kt b/compiler/testData/codegen/box/ranges/contains/inRangeWithImplicitReceiver.kt index 6757cb246f6..8a700cd7644 100644 --- a/compiler/testData/codegen/box/ranges/contains/inRangeWithImplicitReceiver.kt +++ b/compiler/testData/codegen/box/ranges/contains/inRangeWithImplicitReceiver.kt @@ -1,9 +1,5 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun Long.inLongs(l: Long, r: Long): Boolean { return this in l..r @@ -14,11 +10,11 @@ fun Double.inDoubles(l: Double, r: Double): Boolean { } fun box(): String { - assert(2L.inLongs(1L, 3L)) - assert(!2L.inLongs(0L, 1L)) + assertTrue(2L.inLongs(1L, 3L)) + assertTrue(!2L.inLongs(0L, 1L)) - assert(2.0.inDoubles(1.0, 3.0)) - assert(!2.0.inDoubles(0.0, 1.0)) + assertTrue(2.0.inDoubles(1.0, 3.0)) + assertTrue(!2.0.inDoubles(0.0, 1.0)) return "OK" } diff --git a/compiler/testData/codegen/box/ranges/contains/inRangeWithNonmatchingArguments.kt b/compiler/testData/codegen/box/ranges/contains/inRangeWithNonmatchingArguments.kt index e9bef793ec5..c675f47decf 100644 --- a/compiler/testData/codegen/box/ranges/contains/inRangeWithNonmatchingArguments.kt +++ b/compiler/testData/codegen/box/ranges/contains/inRangeWithNonmatchingArguments.kt @@ -1,17 +1,13 @@ -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - // WITH_RUNTIME +import kotlin.test.* fun box(): String { - assert(Long.MAX_VALUE !in Int.MIN_VALUE..Int.MAX_VALUE) - assert(Int.MAX_VALUE in Long.MIN_VALUE..Long.MAX_VALUE) - assert(Double.MAX_VALUE !in Float.MIN_VALUE..Float.MAX_VALUE) - assert(Float.MIN_VALUE in 0.0..1.0) - assert(2.0 !in 1.0f..0.0f) - assert(1L in 0..2) + assertTrue(Long.MAX_VALUE !in Int.MIN_VALUE..Int.MAX_VALUE) + assertTrue(Int.MAX_VALUE in Long.MIN_VALUE..Long.MAX_VALUE) + assertTrue(Double.MAX_VALUE !in Float.MIN_VALUE..Float.MAX_VALUE) + assertTrue(Float.MIN_VALUE in 0.0..1.0) + assertTrue(2.0 !in 1.0f..0.0f) + assertTrue(1L in 0..2) return "OK" }