Use common EnumWhenLowering
This commit is contained in:
+1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
|||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.EnumWhenLowering
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENUM
|
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENUM
|
||||||
|
|||||||
-157
@@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.lower
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|
||||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedVariableDescriptor
|
|
||||||
import org.jetbrains.kotlin.backend.common.peek
|
|
||||||
import org.jetbrains.kotlin.backend.common.pop
|
|
||||||
import org.jetbrains.kotlin.backend.common.push
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.containsNull
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
|
||||||
import org.jetbrains.kotlin.ir.util.getArguments
|
|
||||||
import org.jetbrains.kotlin.ir.util.getPropertyGetter
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
|
|
||||||
/** Look for when-constructs where subject is enum entry.
|
|
||||||
* Replace branches that are comparisons with compile-time known enum entries
|
|
||||||
* with comparisons of ordinals.
|
|
||||||
*/
|
|
||||||
internal class EnumWhenLowering(private val context: Context) : IrElementTransformerVoid(), FileLoweringPass {
|
|
||||||
|
|
||||||
private val subjectWithOrdinalStack = mutableListOf<Pair<IrVariable, Lazy<IrVariable>>>()
|
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
|
||||||
visitFile(irFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that irBlock satisfies all constrains of this lowering.
|
|
||||||
// 1. Block's origin is WHEN
|
|
||||||
// 2. Subject of `when` is variable of enum type
|
|
||||||
// NB: See BranchingExpressionGenerator in Kotlin sources to get insight about
|
|
||||||
// `when` block translation to IR.
|
|
||||||
private fun shouldLower(irBlock: IrBlock): Boolean {
|
|
||||||
if (irBlock.origin != IrStatementOrigin.WHEN) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// when-block with subject should have two children: temporary variable and when itself.
|
|
||||||
if (irBlock.statements.size != 2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val subject = irBlock.statements[0] as IrVariable
|
|
||||||
// Subject should not be nullable because we will access the `ordinal` property.
|
|
||||||
if (subject.type.containsNull()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Check that subject is enum entry.
|
|
||||||
val enumClass = subject.type.getClass()
|
|
||||||
?: return false
|
|
||||||
return enumClass.kind == ClassKind.ENUM_CLASS
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitBlock(expression: IrBlock): IrExpression {
|
|
||||||
if (!shouldLower(expression)) {
|
|
||||||
return super.visitBlock(expression)
|
|
||||||
}
|
|
||||||
// Will be initialized only when we found a branch that compares
|
|
||||||
// subject with compile-time known enum entry.
|
|
||||||
val subject = expression.statements[0] as IrVariable
|
|
||||||
val subjectOrdinalProvider = lazy {
|
|
||||||
createEnumOrdinalVariable(subject)
|
|
||||||
}
|
|
||||||
subjectWithOrdinalStack.push(Pair(subject, subjectOrdinalProvider))
|
|
||||||
// Process nested `when` and comparisons.
|
|
||||||
expression.transformChildrenVoid(this)
|
|
||||||
// If variable was initialized then it was actually used and we need to insert it
|
|
||||||
// into the block's IR.
|
|
||||||
if (subjectOrdinalProvider.isInitialized()) {
|
|
||||||
expression.statements.add(1, subjectOrdinalProvider.value)
|
|
||||||
}
|
|
||||||
subjectWithOrdinalStack.pop()
|
|
||||||
return expression
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create temporary variable for subject's ordinal.
|
|
||||||
private fun createEnumOrdinalVariable(enumVariable: IrVariable) = WrappedVariableDescriptor().let {
|
|
||||||
IrVariableImpl(
|
|
||||||
enumVariable.startOffset, enumVariable.endOffset,
|
|
||||||
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
|
||||||
IrVariableSymbolImpl(it),
|
|
||||||
Name.identifier(enumVariable.name.asString() + "_ordinal"),
|
|
||||||
context.irBuiltIns.intType,
|
|
||||||
isVar = false,
|
|
||||||
isConst = false,
|
|
||||||
isLateinit = false
|
|
||||||
).apply {
|
|
||||||
it.bind(this)
|
|
||||||
parent = enumVariable.parent
|
|
||||||
|
|
||||||
val ordinalPropertyGetter = context.ir.symbols.enum.getPropertyGetter("ordinal")!!
|
|
||||||
initializer = IrCallImpl(
|
|
||||||
enumVariable.startOffset, enumVariable.endOffset,
|
|
||||||
ordinalPropertyGetter.owner.returnType,
|
|
||||||
ordinalPropertyGetter
|
|
||||||
).apply {
|
|
||||||
dispatchReceiver = IrGetValueImpl(
|
|
||||||
enumVariable.startOffset, enumVariable.endOffset,
|
|
||||||
enumVariable.type, enumVariable.symbol
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
|
||||||
expression.transformChildrenVoid(this)
|
|
||||||
// Try to do actual lowering.
|
|
||||||
return tryLower(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val areEqualByValue = context.irBuiltIns.eqeqSymbol
|
|
||||||
|
|
||||||
// We are looking for branch that is a comparison of the subject and another enum entry.
|
|
||||||
private fun tryLower(call: IrCall): IrExpression {
|
|
||||||
if (call.origin != IrStatementOrigin.EQEQ) {
|
|
||||||
return call
|
|
||||||
}
|
|
||||||
val callArgs = call.getArguments()
|
|
||||||
if (callArgs.size != 2) {
|
|
||||||
return call
|
|
||||||
}
|
|
||||||
val lhs = callArgs[0].second
|
|
||||||
val rhs = callArgs[1].second
|
|
||||||
|
|
||||||
// If there is nothing on stack then nothing we can do.
|
|
||||||
val (topmostSubject, topmostOrdinalProvider) = subjectWithOrdinalStack.peek()
|
|
||||||
?: return call
|
|
||||||
if (lhs is IrValueAccessExpression && lhs.symbol.owner == topmostSubject && rhs is IrGetEnumValue &&
|
|
||||||
// Both entries should belong to the same class:
|
|
||||||
topmostSubject.type.classifierOrNull?.owner == rhs.symbol.owner.parent) {
|
|
||||||
val entryOrdinal = context.specialDeclarationsFactory.getEnumEntryOrdinal(rhs.symbol.owner)
|
|
||||||
val subjectOrdinal = topmostOrdinalProvider.value
|
|
||||||
return IrCallImpl(call.startOffset, call.endOffset, areEqualByValue.owner.returnType, areEqualByValue).apply {
|
|
||||||
putValueArgument(0,
|
|
||||||
IrGetValueImpl(lhs.startOffset, lhs.endOffset, subjectOrdinal.type, subjectOrdinal.symbol))
|
|
||||||
putValueArgument(1,
|
|
||||||
IrConstImpl.int(rhs.startOffset, rhs.endOffset, context.irBuiltIns.intType, entryOrdinal))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user