Skip to content

Commit d582c71

Browse files
committed
[JVM] Emit lookupswitch/tableswitch for Short/Byte if possible
^KT-76589
1 parent af8682e commit d582c71

File tree

6 files changed

+43
-31
lines changed

6 files changed

+43
-31
lines changed

compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.IrWhenUtils
99
import org.jetbrains.kotlin.codegen.`when`.SwitchCodegen.Companion.preferLookupOverSwitch
1010
import org.jetbrains.kotlin.ir.expressions.*
1111
import org.jetbrains.kotlin.ir.types.*
12+
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
1213
import org.jetbrains.kotlin.ir.util.hasAnnotation
1314
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
1415
import org.jetbrains.org.objectweb.asm.Label
@@ -62,11 +63,13 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
6263

6364
val firstCondition = callToLabels[0].call
6465
if (firstCondition.symbol != context.irBuiltIns.eqeqSymbol) return null
65-
val subject = firstCondition.arguments[0]
66+
val subject = firstCondition.arguments[0] ?: return null
6667
return when {
6768
subject is IrCall && subject.isCoerceFromUIntToInt() ->
6869
generateUIntSwitch(subject.arguments[0]!! as? IrGetValue, calls, callToLabels, expressionToLabels, elseExpression)
69-
subject is IrGetValue || subject is IrConst && subject.type.isString() -> // also generate tableswitch for literal string subject
70+
subject is IrGetValue
71+
|| subject is IrConst && subject.type.isString() // also generate tableswitch for literal string subject
72+
|| subject.isByteOrShortToInt() ->
7073
generatePrimitiveSwitch(subject, calls, callToLabels, expressionToLabels, elseExpression)
7174
else ->
7275
null
@@ -111,6 +114,12 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
111114
)
112115
}
113116

117+
fun IrExpression.isByteOrShortToInt(): Boolean {
118+
if (this !is IrCall) return false
119+
val fqName = symbol.owner.fqNameWhenAvailable?.asString()
120+
return fqName == "kotlin.Byte.toInt" || fqName == "kotlin.Short.toInt"
121+
}
122+
114123
private fun generatePrimitiveSwitch(
115124
subject: IrExpression,
116125
conditions: List<IrCall>,
@@ -122,7 +131,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
122131
if (!areConstantComparisons(conditions)) return null
123132

124133
return when {
125-
subject is IrGetValue && areConstIntComparisons(conditions) -> {
134+
(subject is IrGetValue || subject.isByteOrShortToInt()) && areConstIntComparisons(conditions) -> {
126135
val cases = extractSwitchCasesAndFilterUnreachableLabels(callToLabels, expressionToLabels)
127136
IntSwitch(
128137
subject,
@@ -187,8 +196,17 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
187196
return lhs.all { it != null && it.value == lhs[0]!!.value }
188197
}
189198

199+
fun isValidCallToIntLHS(): Boolean {
200+
val lhs = conditions.map {
201+
val call = it.takeIf { it.symbol == context.irBuiltIns.eqeqSymbol }?.arguments[0] ?: return false
202+
if (!call.isByteOrShortToInt()) return false
203+
(call as IrCall).arguments[0] as? IrGetValue
204+
}
205+
return lhs.all { it != null && it.symbol.owner == lhs[0]!!.symbol.owner }
206+
}
207+
190208
// All conditions are equality checks && all LHS refer to the same tmp variable.
191-
if (!isValidIrGetValueTypeLHS() && !isValidIrConstTypeLHS())
209+
if (!isValidIrGetValueTypeLHS() && !isValidIrConstTypeLHS() && !isValidCallToIntLHS())
192210
return false
193211

194212
// All RHS are constants
@@ -218,7 +236,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
218236
subjectTypePredicate: (IrType) -> Boolean,
219237
irConstPredicate: (IrConst) -> Boolean
220238
): Boolean {
221-
val lhs = conditions.map { it.arguments[0] as? IrGetValue ?: it.arguments[0] as IrConst }
239+
val lhs = conditions.map { it.arguments[0] as? IrGetValue ?: it.arguments[0] as? IrConst ?: it.arguments[0] as IrCall }
222240
if (lhs.any { !subjectTypePredicate(it.type) })
223241
return false
224242

@@ -314,7 +332,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
314332
}
315333

316334
inner class IntSwitch(
317-
subject: IrGetValue,
335+
subject: IrExpression,
318336
elseExpression: IrExpression?,
319337
expressionToLabels: ArrayList<ExpressionToLabel>,
320338
private val cases: List<ValueToLabel>

compiler/testData/codegen/box/when/lookupSwitchOverByte.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
val byte10: Byte = 10
2-
val byte30: Byte = 30
3-
val byte50: Byte = 50
4-
val byte42: Byte = 42
1+
const val byte10: Byte = 10
2+
const val byte30: Byte = 30
3+
const val byte50: Byte = 50
4+
const val byte42: Byte = 42
55

66
fun foo(p: Byte): String {
7-
val localByte50: Byte = 50
87
return when (p) {
98
byte10 -> "10"
109
byte30 -> "30"
11-
localByte50 -> "50"
10+
byte50 -> "50"
1211
else -> "else"
1312
}
1413
}

compiler/testData/codegen/box/when/lookupSwitchOverMixTypes.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ const val byte10: Byte = 10
22
const val short50: Short = 50
33

44
fun foo(p: Any): String {
5-
val newByte10: Byte = 10
65
return when (p) {
76
0 -> "0"
8-
newByte10 -> "byte10"
7+
byte10 -> "byte10"
98
short50 -> "short50"
109
'z' -> "z"
1110
else -> "else"
@@ -20,8 +19,4 @@ fun box(): String {
2019
&& foo("else") == "else"
2120
) "OK"
2221
else "FAIL"
23-
}
24-
25-
// CHECK_BYTECODE_TEXT
26-
// 1 LOOKUPSWITCH
27-
// 0 TABLESWITCH
22+
}

compiler/testData/codegen/box/when/lookupSwitchOverShort.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
val short100: Short = 100
2-
val short200: Short = 200
3-
val short300: Short = 300
4-
val short400: Short = 400
1+
const val short100: Short = 100
2+
const val short200: Short = 200
3+
const val short300: Short = 300
4+
const val short400: Short = 400
55

66
fun foo(p: Short): String {
77
return when (p) {

compiler/testData/codegen/box/when/tableSwitchOverByte.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
val byte101: Byte = 101
2-
val byte102: Byte = 102
3-
val byte103: Byte = 103
4-
val byte104: Byte = 104
1+
const val byte101: Byte = 101
2+
const val byte102: Byte = 102
3+
const val byte103: Byte = 103
4+
const val byte104: Byte = 104
55

66
fun foo(p: Byte): String {
77
return when (p) {

compiler/testData/codegen/box/when/tableSwitchOverShort.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
val short101: Short = 101
2-
val short102: Short = 102
3-
val short103: Short = 103
4-
val short104: Short = 104
1+
const val short101: Short = 101
2+
const val short102: Short = 102
3+
const val short103: Short = 103
4+
const val short104: Short = 104
55

66
fun foo(p: Short): String {
77
return when (p) {

0 commit comments

Comments
 (0)