JVM_IR KT-50074 generate tableswitch for literal string subject of when expression
This commit is contained in:
+27
-9
@@ -65,7 +65,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
return when {
|
return when {
|
||||||
subject is IrCall && subject.isCoerceFromUIntToInt() ->
|
subject is IrCall && subject.isCoerceFromUIntToInt() ->
|
||||||
generateUIntSwitch(subject.getValueArgument(0)!! as? IrGetValue, calls, callToLabels, expressionToLabels, elseExpression)
|
generateUIntSwitch(subject.getValueArgument(0)!! as? IrGetValue, calls, callToLabels, expressionToLabels, elseExpression)
|
||||||
subject is IrGetValue ->
|
subject is IrGetValue || subject is IrConst<*> && subject.type.isString() -> // also generate tableswitch for literal string subject
|
||||||
generatePrimitiveSwitch(subject, calls, callToLabels, expressionToLabels, elseExpression)
|
generatePrimitiveSwitch(subject, calls, callToLabels, expressionToLabels, elseExpression)
|
||||||
else ->
|
else ->
|
||||||
null
|
null
|
||||||
@@ -111,7 +111,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generatePrimitiveSwitch(
|
private fun generatePrimitiveSwitch(
|
||||||
subject: IrGetValue,
|
subject: IrExpression,
|
||||||
conditions: List<IrCall>,
|
conditions: List<IrCall>,
|
||||||
callToLabels: ArrayList<CallToLabel>,
|
callToLabels: ArrayList<CallToLabel>,
|
||||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||||
@@ -121,7 +121,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
if (!areConstantComparisons(conditions)) return null
|
if (!areConstantComparisons(conditions)) return null
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
areConstIntComparisons(conditions) -> {
|
subject is IrGetValue && areConstIntComparisons(conditions) -> {
|
||||||
val cases = extractSwitchCasesAndFilterUnreachableLabels(callToLabels, expressionToLabels)
|
val cases = extractSwitchCasesAndFilterUnreachableLabels(callToLabels, expressionToLabels)
|
||||||
IntSwitch(
|
IntSwitch(
|
||||||
subject,
|
subject,
|
||||||
@@ -162,9 +162,23 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun areConstantComparisons(conditions: List<IrCall>): Boolean {
|
private fun areConstantComparisons(conditions: List<IrCall>): Boolean {
|
||||||
|
|
||||||
|
fun isValidIrGetValueTypeLHS(): Boolean {
|
||||||
|
val lhs = conditions.map {
|
||||||
|
it.takeIf { it.symbol == codegen.context.irBuiltIns.eqeqSymbol }?.getValueArgument(0) as? IrGetValue
|
||||||
|
}
|
||||||
|
return lhs.all { it != null && it.symbol == lhs[0]!!.symbol }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValidIrConstTypeLHS(): Boolean {
|
||||||
|
val lhs = conditions.map {
|
||||||
|
it.takeIf { it.symbol == codegen.context.irBuiltIns.eqeqSymbol }?.getValueArgument(0) as? IrConst<*>
|
||||||
|
}
|
||||||
|
return lhs.all { it != null && it.value == lhs[0]!!.value }
|
||||||
|
}
|
||||||
|
|
||||||
// All conditions are equality checks && all LHS refer to the same tmp variable.
|
// All conditions are equality checks && all LHS refer to the same tmp variable.
|
||||||
val lhs = conditions.map { it.takeIf { it.symbol == codegen.context.irBuiltIns.eqeqSymbol }?.getValueArgument(0) as? IrGetValue }
|
if (!isValidIrGetValueTypeLHS() && !isValidIrConstTypeLHS())
|
||||||
if (lhs.any { it == null || it.symbol != lhs[0]!!.symbol })
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
// All RHS are constants
|
// All RHS are constants
|
||||||
@@ -190,7 +204,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
subjectTypePredicate: (IrType) -> Boolean,
|
subjectTypePredicate: (IrType) -> Boolean,
|
||||||
irConstPredicate: (IrConst<*>) -> Boolean
|
irConstPredicate: (IrConst<*>) -> Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val lhs = conditions.map { it.getValueArgument(0) as IrGetValue }
|
val lhs = conditions.map { it.getValueArgument(0) as? IrGetValue ?: it.getValueArgument(0) as IrConst<*> }
|
||||||
if (lhs.any { !subjectTypePredicate(it.type) })
|
if (lhs.any { !subjectTypePredicate(it.type) })
|
||||||
return false
|
return false
|
||||||
|
|
||||||
@@ -286,7 +300,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract inner class Switch(
|
abstract inner class Switch(
|
||||||
val subject: IrGetValue,
|
val subject: IrExpression,
|
||||||
val elseExpression: IrExpression?,
|
val elseExpression: IrExpression?,
|
||||||
val expressionToLabels: ArrayList<ExpressionToLabel>
|
val expressionToLabels: ArrayList<ExpressionToLabel>
|
||||||
) {
|
) {
|
||||||
@@ -427,7 +441,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
// A tableswitch or lookupswitch is then used for the hash code lookup.
|
// A tableswitch or lookupswitch is then used for the hash code lookup.
|
||||||
|
|
||||||
inner class StringSwitch(
|
inner class StringSwitch(
|
||||||
subject: IrGetValue,
|
subject: IrExpression,
|
||||||
elseExpression: IrExpression?,
|
elseExpression: IrExpression?,
|
||||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||||
private val cases: List<ValueToLabel>
|
private val cases: List<ValueToLabel>
|
||||||
@@ -457,8 +471,12 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
// see:
|
// see:
|
||||||
// box/when/stringOptimization/enhancedNullability.kt
|
// box/when/stringOptimization/enhancedNullability.kt
|
||||||
// box/when/stringOptimization/flexibleNullability.kt
|
// box/when/stringOptimization/flexibleNullability.kt
|
||||||
|
//
|
||||||
|
// Generate "optimized" version for literal string subject to avoid performance regression
|
||||||
|
// see:
|
||||||
|
// box/unit/nullableUnitInWhen3.kt
|
||||||
override fun shouldOptimize() =
|
override fun shouldOptimize() =
|
||||||
hashAndSwitchLabels.size > 2 ||
|
hashAndSwitchLabels.size > (if (subject is IrConst<*>) 0 else 2) ||
|
||||||
subject.type.hasAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) && hashAndSwitchLabels.isNotEmpty()
|
subject.type.hasAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) && hashAndSwitchLabels.isNotEmpty()
|
||||||
|
|
||||||
override fun genSwitch() {
|
override fun genSwitch() {
|
||||||
|
|||||||
@@ -10,3 +10,8 @@ fun box(): String {
|
|||||||
|
|
||||||
return if (x == null) "OK" else "Fail"
|
return if (x == null) "OK" else "Fail"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK_BYTECODE_TEXT
|
||||||
|
// JVM_IR_TEMPLATES
|
||||||
|
// 0 Intrinsics.areEqual
|
||||||
|
// 1 TABLESWITCH
|
||||||
Reference in New Issue
Block a user