IR: remove IrCallMatcher/IrFunctionMatcher and usages

There doesn't seem to be much value over a simple imperative code which
checks exactly the same things in the same order. The main downside of
the removed API is that it was more difficult to debug.
This commit is contained in:
Alexander Udalov
2022-12-11 23:24:11 +01:00
parent 18950b448c
commit d2b66e5004
16 changed files with 131 additions and 360 deletions
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.loops.handlers.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.UnsignedType
@@ -29,10 +28,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -60,14 +56,14 @@ private class Transformer(
val context: CommonBackendContext,
val container: IrSymbolOwner
) : IrElementTransformerVoidWithContext() {
private val headerInfoBuilder = RangeHeaderInfoBuilder(context, this::getScopeOwnerSymbol)
fun getScopeOwnerSymbol() = currentScope?.scope?.scopeOwnerSymbol ?: container.symbol
val stdlibExtensionContainsCallMatcher = SimpleCalleeMatcher {
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.closedRange) }
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.CONTAINS}") }
parameterCount { it == 1 }
private fun matchStdlibExtensionContainsCall(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.size == 1 &&
callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.closedRange) == true &&
callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.CONTAINS}")
}
override fun visitCall(expression: IrCall): IrExpression {
@@ -89,7 +85,7 @@ private class Transformer(
return super.visitCall(expression) // Preserve the call to not().
}
if (expression.extensionReceiver != null && !stdlibExtensionContainsCallMatcher(expression)) {
if (expression.extensionReceiver != null && !matchStdlibExtensionContainsCall(expression)) {
// We can only optimize calls to the stdlib extension functions and not a user-defined extension.
// TODO: This breaks the optimization for *Range.reversed().contains(). The function called there is the extension function
// Iterable.contains(). Figure out if we can safely match on that as well.
@@ -412,15 +408,13 @@ internal open class RangeHeaderInfoBuilder(context: CommonBackendContext, scopeO
/** Builds a [HeaderInfo] for closed floating-point ranges built using the `rangeTo` function. */
internal object FloatingPointRangeToHandler : HeaderInfoHandler<IrCall, Nothing?> {
private val matcher = SimpleCalleeMatcher {
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}") }
extensionReceiver { it != null && it.type.run { isFloat() || isDouble() } }
parameterCount { it == 1 }
parameter(0) { it.type.run { isFloat() || isDouble() } }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type?.let { it.isFloat() || it.isDouble() } == true &&
callee.extensionReceiverParameter?.type?.let { it.isFloat() || it.isDouble() } == true &&
callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
FloatingPointRangeHeaderInfo(
start = expression.extensionReceiver!!,
@@ -429,15 +423,14 @@ internal object FloatingPointRangeToHandler : HeaderInfoHandler<IrCall, Nothing?
}
/** Builds a [HeaderInfo] for ranges of Comparables built using the `rangeTo` extension function. */
internal class ComparableRangeToHandler(context: CommonBackendContext) : HeaderInfoHandler<IrCall, Nothing?> {
private val matcher = SimpleCalleeMatcher {
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}") }
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.comparable) }
parameterCount { it == 1 }
internal class ComparableRangeToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, Nothing?> {
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.size == 1 &&
callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.comparable) == true &&
callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
ComparableRangeInfo(
start = expression.extensionReceiver!!,
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -15,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
@@ -26,8 +26,8 @@ class StringTrimLowering(val context: CommonBackendContext) : FileLoweringPass,
override fun visitCall(expression: IrCall): IrExpression {
return when {
trimIndentMatcher(expression) -> maybeComputeTrimIndent(expression)
trimMarginMatcher(expression) -> maybeComputeTrimMargin(expression)
matchTrimIndent(expression) -> maybeComputeTrimIndent(expression)
matchTrimMargin(expression) -> maybeComputeTrimMargin(expression)
else -> super.visitCall(expression)
}
}
@@ -64,20 +64,21 @@ class StringTrimLowering(val context: CommonBackendContext) : FileLoweringPass,
return null
}
private val trimIndentMatcher = SimpleCalleeMatcher {
extensionReceiver { it != null && it.type.isString() }
fqName { it == TRIM_INDENT_FQ_NAME }
parameterCount { it == 0 }
private fun matchTrimIndent(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.isString() == true &&
callee.kotlinFqName == TRIM_INDENT_FQ_NAME
}
private val trimMarginMatcher = SimpleCalleeMatcher {
extensionReceiver { it != null && it.type.isString() }
fqName { it == TRIM_MARGIN_FQ_NAME }
parameterCount { it == 1 }
parameter(0) { it.type.isString() }
private fun matchTrimMargin(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type?.isString() == true &&
callee.extensionReceiverParameter?.type?.isString() == true &&
callee.kotlinFqName == TRIM_MARGIN_FQ_NAME
}
private val TRIM_MARGIN_FQ_NAME = FqName.fromSegments(listOf("kotlin", "text", "trimMargin"))
private val TRIM_INDENT_FQ_NAME = FqName.fromSegments(listOf("kotlin", "text", "trimIndent"))
private val TRIM_MARGIN_FQ_NAME = FqName("kotlin.text.trimMargin")
private val TRIM_INDENT_FQ_NAME = FqName("kotlin.text.trimIndent")
}
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.loops.handlers.*
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrVariable
@@ -242,14 +241,13 @@ internal interface HeaderInfoHandler<E : IrExpression, D> {
* Matches the `iterator()` call that produced the iterable; if the call matches (or the matcher is null),
* the handler can build a [HeaderInfo] from the iterable.
*/
val iteratorCallMatcher: IrCallMatcher?
get() = null
fun matchIteratorCall(call: IrCall): Boolean = true
/** Builds a [HeaderInfo] from the expression. */
fun build(expression: E, data: D, scopeOwner: IrSymbol): HeaderInfo?
fun handle(expression: E, iteratorCall: IrCall?, data: D, scopeOwner: IrSymbol) =
if ((iteratorCall == null || iteratorCallMatcher == null || iteratorCallMatcher!!(iteratorCall)) && matchIterable(expression)) {
fun handle(expression: E, iteratorCall: IrCall?, data: D, scopeOwner: IrSymbol): HeaderInfo? =
if ((iteratorCall == null || matchIteratorCall(iteratorCall)) && matchIterable(expression)) {
build(expression, data, scopeOwner)
} else {
null
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal class DefaultIterableHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> {
private val iterableClassSymbol = context.ir.symbols.iterable
override fun matchIterable(expression: IrExpression) = expression.type.isSubtypeOfClass(iterableClassSymbol)
override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isSubtypeOfClass(iterableClassSymbol)
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
@@ -24,11 +24,8 @@ internal class DefaultProgressionHandler(
private val symbols = context.ir.symbols
private val rangeClassesTypes = symbols.rangeClasses.map { it.defaultType }.toSet()
override fun matchIterable(expression: IrExpression) = ProgressionType.fromIrType(
expression.type,
symbols,
allowUnsignedBounds
) != null
override fun matchIterable(expression: IrExpression): Boolean =
ProgressionType.fromIrType(expression.type, symbols, allowUnsignedBounds) != null
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal class DefaultSequenceHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> {
private val sequenceClassSymbol = context.ir.symbols.sequence
override fun matchIterable(expression: IrExpression) =
override fun matchIterable(expression: IrExpression): Boolean =
sequenceClassSymbol != null && expression.type.isSubtypeOfClass(sequenceClassSymbol)
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -17,22 +15,21 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `downTo` extension function. */
internal class DownToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop
private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes)
parameterCount { it == 1 }
parameter(0) { it.type in progressionElementTypes }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
callee.extensionReceiverParameter?.type in progressionElementTypes &&
callee.kotlinFqName == FqName("kotlin.ranges.downTo")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
val first = expression.extensionReceiver!!
@@ -10,9 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfo
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.loops.IndexedGetHeaderInfo
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
@@ -77,22 +74,14 @@ abstract class IndexedGetIterationHandler(
internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context, canCacheLast = true) {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
override fun matchIterable(expression: IrExpression) =
expression.type.run { isArrayType() } ||
expression.run {
this is IrCall && reversedArrayMatcher(this)
}
override fun matchIterable(expression: IrExpression): Boolean {
if (expression.type.isArrayType()) return true
private val reversedArrayMatcher =
createIrCallMatcher(Quantifier.ANY) {
callee {
fqName { it == FqName("kotlin.collections.reversed") }
extensionReceiver {
it != null && it.type.run { isArray() || isPrimitiveArray() }
}
parameterCount { it == 0 }
}
}
val callee = (expression as? IrCall)?.symbol?.owner ?: return false
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.let { it.isArray() || it.isPrimitiveArray() } == true &&
callee.kotlinFqName == FqName("kotlin.collections.reversed")
}
override val IrType.sizePropertyGetter
get() = getClass()!!.getPropertyGetter("size")!!.owner
@@ -123,17 +112,21 @@ internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGet
* Note: The value for "last" can NOT be cached (i.e., stored in a variable) because the size/length can change within the loop. This means
* that "last" is re-evaluated with each iteration of the loop.
*/
internal open class CharSequenceIterationHandler(context: CommonBackendContext, canCacheLast: Boolean = false) :
IndexedGetIterationHandler(context, canCacheLast) {
override fun matchIterable(expression: IrExpression) = expression.type.isSubtypeOfClass(context.ir.symbols.charSequence)
internal open class CharSequenceIterationHandler(
context: CommonBackendContext,
canCacheLast: Boolean = false
) : IndexedGetIterationHandler(context, canCacheLast) {
override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isSubtypeOfClass(context.ir.symbols.charSequence)
// We only want to handle the known extension function for CharSequence in the standard library (top level `kotlin.text.iterator`).
// The behavior of this iterator is well-defined and can be lowered. CharSequences can have their own iterators, either as a member or
// extension function, and the behavior of those custom iterators is unknown.
override val iteratorCallMatcher = SimpleCalleeMatcher {
extensionReceiver { it != null && it.type.run { isCharSequence() } }
fqName { it == FqName("kotlin.text.${OperatorNameConventions.ITERATOR}") }
parameterCount { it == 0 }
override fun matchIteratorCall(call: IrCall): Boolean {
val callee = call.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.isCharSequence() == true &&
callee.kotlinFqName == FqName("kotlin.text.${OperatorNameConventions.ITERATOR}")
}
override val IrType.sizePropertyGetter: IrSimpleFunction
@@ -149,7 +142,8 @@ internal open class CharSequenceIterationHandler(context: CommonBackendContext,
* Note: The value for "last" CAN be cached for Strings as they are immutable and the size/length cannot change.
*/
internal class StringIterationHandler(context: CommonBackendContext) : CharSequenceIterationHandler(context, canCacheLast = true) {
override fun matchIterable(expression: IrExpression) = expression.type.isString()
override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isString()
override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.string.getPropertyGetter("length")!!.owner
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.isUnsignedArray
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `indices` extension property. */
@@ -62,14 +62,13 @@ internal abstract class IndicesHandler(protected val context: CommonBackendConte
}
internal class CollectionIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val matcher = SimpleCalleeMatcher {
extensionReceiver { it?.type?.isCollection() == true }
fqName { it == FqName("kotlin.collections.<get-indices>") }
parameterCount { it == 0 }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.isCollection() == true &&
callee.kotlinFqName == FqName("kotlin.collections.<get-indices>")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.collection.getPropertyGetter("size")!!.owner
}
@@ -77,31 +76,27 @@ internal class CollectionIndicesHandler(context: CommonBackendContext) : Indices
internal class ArrayIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
private val matcher = SimpleCalleeMatcher {
extensionReceiver {
it != null && it.type.run {
isArray() || isPrimitiveArray() || (supportsUnsignedArrays && isUnsignedArray())
}
}
fqName { it == FqName("kotlin.collections.<get-indices>") }
parameterCount { it == 0 }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.let {
it.isArray() || it.isPrimitiveArray() || (supportsUnsignedArrays && it.isUnsignedArray())
} == true &&
callee.kotlinFqName == FqName("kotlin.collections.<get-indices>")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction
get() = getClass()!!.getPropertyGetter("size")!!.owner
}
internal class CharSequenceIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val matcher = SimpleCalleeMatcher {
extensionReceiver { it != null && it.type.run { isCharSequence() } }
fqName { it == FqName("kotlin.text.<get-indices>") }
parameterCount { it == 0 }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type?.isCharSequence() == true &&
callee.kotlinFqName == FqName("kotlin.text.<get-indices>")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.charSequence.getPropertyGetter("length")!!.owner
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
@@ -19,18 +18,15 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
/** Builds a [HeaderInfo] for progressions built using the `rangeTo` function. */
internal class RangeToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop
private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher {
dispatchReceiver { it != null && it.type in progressionElementTypes }
fqName { it.pathSegments().last() == OperatorNameConventions.RANGE_TO }
parameterCount { it == 1 }
parameter(0) { it.type in progressionElementTypes }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
callee.dispatchReceiverParameter?.type in progressionElementTypes &&
callee.name == OperatorNameConventions.RANGE_TO
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
val first = expression.dispatchReceiver!!
@@ -8,11 +8,10 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for calls to reverse an iterable. */
@@ -20,20 +19,13 @@ internal class ReversedHandler(context: CommonBackendContext, private val visito
HeaderInfoHandler<IrCall, Nothing?> {
private val progressionClassesTypes = context.ir.symbols.progressionClasses.map { it.defaultType }.toSet()
// Use Quantifier.ANY so we can handle all reversed iterables in the same manner.
private val matcher =
createIrCallMatcher(Quantifier.ANY) {
// Matcher for reversed progression.
callee {
fqName { it == FqName("kotlin.ranges.reversed") }
extensionReceiver { it != null && it.type in progressionClassesTypes }
parameterCount { it == 0 }
}
// TODO: Handle reversed String, Progression.withIndex(), etc.
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun matchIterable(expression: IrCall): Boolean {
// TODO: Handle reversed String, Progression.withIndex(), etc.
val callee = expression.symbol.owner
return callee.valueParameters.isEmpty() &&
callee.extensionReceiverParameter?.type in progressionClassesTypes &&
callee.kotlinFqName == FqName("kotlin.ranges.reversed")
}
// Reverse the HeaderInfo from the underlying progression or array (if any).
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
@@ -9,8 +9,6 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrVariable
@@ -18,10 +16,11 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.addArgument
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.types.isLong
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.name.FqName
import kotlin.math.absoluteValue
@@ -30,17 +29,13 @@ import kotlin.math.absoluteValue
internal class StepHandler(
private val context: CommonBackendContext, private val visitor: HeaderInfoBuilder
) : HeaderInfoHandler<IrCall, ProgressionType> {
private val symbols = context.ir.symbols
private val matcher = SimpleCalleeMatcher {
singleArgumentExtension(
FqName("kotlin.ranges.step"),
symbols.progressionClasses.map { it.defaultType })
parameter(0) { it.type.isInt() || it.type.isLong() }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type?.let { it.isInt() || it.isLong() } == true &&
callee.extensionReceiverParameter?.type?.classOrNull in context.ir.symbols.progressionClasses &&
callee.kotlinFqName == FqName("kotlin.ranges.step")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Retrieve the HeaderInfo from the underlying progression (if any).
@@ -8,26 +8,23 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `until` extension function. */
internal class UntilHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val symbols = context.ir.symbols
private val progressionElementTypes = symbols.progressionElementTypes
private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes)
parameterCount { it == 1 }
parameter(0) { it.type in progressionElementTypes }
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
callee.extensionReceiverParameter?.type in progressionElementTypes &&
callee.kotlinFqName == FqName("kotlin.ranges.until")
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
ProgressionHeaderInfo(
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfo
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.loops.NestedHeaderInfoBuilderForWithIndex
import org.jetbrains.kotlin.backend.common.lower.loops.WithIndexHeaderInfo
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.isArray
@@ -20,40 +18,32 @@ import org.jetbrains.kotlin.ir.types.isSequence
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.isUnsignedArray
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.ir.util.kotlinFqName
/** Builds a [HeaderInfo] for calls to `withIndex()`. */
internal class WithIndexHandler(
context: CommonBackendContext, private val visitor: NestedHeaderInfoBuilderForWithIndex
private val context: CommonBackendContext,
private val visitor: NestedHeaderInfoBuilderForWithIndex
) : HeaderInfoHandler<IrCall, Nothing?> {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
// Use Quantifier.ANY so we can handle all `withIndex()` calls in the same manner.
private val matcher =
createIrCallMatcher(Quantifier.ANY) {
callee {
fqName { it == FqName("kotlin.collections.withIndex") }
extensionReceiver {
it != null && it.type.run {
isArray() || isPrimitiveArray() || isIterable() ||
(supportsUnsignedArrays && isUnsignedArray())
}
}
parameterCount { it == 0 }
}
callee {
fqName { it == FqName("kotlin.text.withIndex") }
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.charSequence) }
parameterCount { it == 0 }
}
callee {
fqName { it == FqName("kotlin.sequences.withIndex") }
extensionReceiver { it != null && it.type.run { isSequence() } }
parameterCount { it == 0 }
}
}
override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
if (callee.valueParameters.isNotEmpty() || callee.name.asString() != "withIndex") return false
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
return when (callee.kotlinFqName.asString()) {
"kotlin.collections.withIndex" ->
callee.extensionReceiverParameter?.type?.run {
isArray() || isPrimitiveArray() || isIterable() ||
(supportsUnsignedArrays && isUnsignedArray())
} == true
"kotlin.text.withIndex" ->
callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.charSequence) == true
"kotlin.sequences.withIndex" ->
callee.extensionReceiverParameter?.type?.isSequence() == true
else -> false
}
}
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo? {
// WithIndexHeaderInfo is a composite that contains the HeaderInfo for the underlying iterable (if any).
@@ -1,77 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.lower.matchers
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
internal interface IrCallMatcher : (IrCall) -> Boolean
/**
* IrCallMatcher that puts restrictions only on its callee.
*/
internal class SimpleCalleeMatcher(
restrictions: IrFunctionMatcherContainer.() -> Unit
) : IrCallMatcher {
private val calleeRestriction: IrFunctionMatcher = createIrFunctionRestrictions(restrictions)
override fun invoke(call: IrCall) = calleeRestriction(call.symbol.owner)
}
internal class IrCallExtensionReceiverMatcher(
val restriction: (IrExpression?) -> Boolean
) : IrCallMatcher {
override fun invoke(call: IrCall) = restriction(call.extensionReceiver)
}
internal class IrCallDispatchReceiverMatcher(
val restriction: (IrExpression?) -> Boolean
) : IrCallMatcher {
override fun invoke(call: IrCall) = restriction(call.dispatchReceiver)
}
internal class IrCallOriginMatcher(
val restriction: (IrStatementOrigin?) -> Boolean
) : IrCallMatcher {
override fun invoke(call: IrCall) = restriction(call.origin)
}
internal enum class Quantifier { ALL, ANY }
internal open class IrCallMatcherContainer(private val quantifier: Quantifier) : IrCallMatcher {
private val matchers = mutableListOf<IrCallMatcher>()
fun add(matcher: IrCallMatcher) {
matchers += matcher
}
fun extensionReceiver(restriction: (IrExpression?) -> Boolean) =
add(IrCallExtensionReceiverMatcher(restriction))
fun origin(restriction: (IrStatementOrigin?) -> Boolean) =
add(IrCallOriginMatcher(restriction))
fun callee(restrictions: IrFunctionMatcherContainer.() -> Unit) {
add(SimpleCalleeMatcher(restrictions))
}
fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) =
add(IrCallDispatchReceiverMatcher(restriction))
override fun invoke(call: IrCall) = when (quantifier) {
Quantifier.ALL -> matchers.all { it(call) }
Quantifier.ANY -> matchers.any { it(call) }
}
}
internal fun createIrCallMatcher(
quantifier: Quantifier = Quantifier.ALL,
restrictions: IrCallMatcherContainer.() -> Unit
) =
IrCallMatcherContainer(quantifier).apply(restrictions)
@@ -1,98 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.lower.matchers
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
internal interface IrFunctionMatcher : (IrFunction) -> Boolean
internal class ParameterMatcher(
val index: Int,
val restriction: (IrValueParameter) -> Boolean
) : IrFunctionMatcher {
override fun invoke(function: IrFunction): Boolean {
val params = function.valueParameters
return params.size > index && restriction(params[index])
}
}
internal class DispatchReceiverMatcher(
val restriction: (IrValueParameter?) -> Boolean
) : IrFunctionMatcher {
override fun invoke(function: IrFunction): Boolean {
return restriction(function.dispatchReceiverParameter)
}
}
internal class ExtensionReceiverMatcher(
val restriction: (IrValueParameter?) -> Boolean
) : IrFunctionMatcher {
override fun invoke(function: IrFunction): Boolean {
return restriction(function.extensionReceiverParameter)
}
}
internal class ParameterCountMatcher(
val restriction: (Int) -> Boolean
) : IrFunctionMatcher {
override fun invoke(function: IrFunction): Boolean {
return restriction(function.valueParameters.size)
}
}
internal class FqNameMatcher(
val restriction: (FqName) -> Boolean
) : IrFunctionMatcher {
override fun invoke(function: IrFunction): Boolean {
return restriction(function.kotlinFqName)
}
}
internal open class IrFunctionMatcherContainer : IrFunctionMatcher {
private val restrictions = mutableListOf<IrFunctionMatcher>()
fun add(restriction: IrFunctionMatcher) {
restrictions += restriction
}
fun fqName(restriction: (FqName) -> Boolean) =
add(FqNameMatcher(restriction))
fun parameterCount(restriction: (Int) -> Boolean) =
add(ParameterCountMatcher(restriction))
fun extensionReceiver(restriction: (IrValueParameter?) -> Boolean) =
add(ExtensionReceiverMatcher(restriction))
fun dispatchReceiver(restriction: (IrValueParameter?) -> Boolean) =
add(DispatchReceiverMatcher(restriction))
fun parameter(index: Int, restriction: (IrValueParameter) -> Boolean) =
add(ParameterMatcher(index, restriction))
override fun invoke(function: IrFunction) = restrictions.all { it(function) }
}
internal fun createIrFunctionRestrictions(restrictions: IrFunctionMatcherContainer.() -> Unit) =
IrFunctionMatcherContainer().apply(restrictions)
internal fun IrFunctionMatcherContainer.singleArgumentExtension(
fqName: FqName,
types: Collection<IrType>
): IrFunctionMatcherContainer {
extensionReceiver { it != null && it.type in types }
parameterCount { it == 1 }
fqName { it == fqName }
return this
}