For loops lowering improvements (#2191)
* Reduce descriptors and KotlinType usage * Add Array.indices lowering. * Added IrCallMatcher and IrFunctionMatcher * Added benchmarks
This commit is contained in:
+1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.lower.FinallyBlocksLowering
|
|||||||
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
|
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.LateinitLowering
|
import org.jetbrains.kotlin.backend.konan.lower.LateinitLowering
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.SharedVariablesLowering
|
import org.jetbrains.kotlin.backend.konan.lower.SharedVariablesLowering
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.loops.ForLoopsLowering
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.util.checkDeclarationParents
|
import org.jetbrains.kotlin.ir.util.checkDeclarationParents
|
||||||
|
|||||||
+12
-3
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.ir.util.*
|
|||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
@@ -289,15 +290,14 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
|||||||
{ findArrayExtension(it.descriptor, "contentHashCode") }
|
{ findArrayExtension(it.descriptor, "contentHashCode") }
|
||||||
)
|
)
|
||||||
|
|
||||||
fun findArrayExtension(descriptor: ClassDescriptor, name: String): IrSimpleFunctionSymbol {
|
private fun findArrayExtension(descriptor: ClassDescriptor, name: String): IrSimpleFunctionSymbol {
|
||||||
val functionDescriptor = builtInsPackage("kotlin", "collections")
|
val functionDescriptor = builtInsPackage("kotlin", "collections")
|
||||||
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||||
.singleOrNull {
|
.singleOrNull {
|
||||||
it.valueParameters.isEmpty()
|
it.valueParameters.isEmpty()
|
||||||
&& it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == descriptor
|
&& it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == descriptor
|
||||||
&& !it.isExpect
|
&& !it.isExpect
|
||||||
}
|
} ?: error(descriptor.toString())
|
||||||
?: throw Error(descriptor.toString())
|
|
||||||
return symbolTable.referenceSimpleFunction(functionDescriptor)
|
return symbolTable.referenceSimpleFunction(functionDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,6 +319,15 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
|||||||
.getContributedFunctions(Name.identifier("set"), NoLookupLocation.FROM_BACKEND)
|
.getContributedFunctions(Name.identifier("set"), NoLookupLocation.FROM_BACKEND)
|
||||||
.single().let { symbolTable.referenceSimpleFunction(it) }
|
.single().let { symbolTable.referenceSimpleFunction(it) }
|
||||||
|
|
||||||
|
val arraySize = arrays.associateBy(
|
||||||
|
{ it },
|
||||||
|
{ it.descriptor.unsubstitutedMemberScope
|
||||||
|
.getContributedVariables(Name.identifier("size"), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single().let { symbolTable.referenceSimpleFunction(it.getter!!) } }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val valuesForEnum = symbolTable.referenceSimpleFunction(
|
val valuesForEnum = symbolTable.referenceSimpleFunction(
|
||||||
context.getInternalFunctions("valuesForEnum").single())
|
context.getInternalFunctions("valuesForEnum").single())
|
||||||
|
|
||||||
|
|||||||
-557
@@ -1,557 +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.IrElementTransformerVoidWithContext
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
|
||||||
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
|
||||||
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.expressions.*
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.*
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
|
|
||||||
/** This lowering pass optimizes range-based for loops. */
|
|
||||||
internal class ForLoopsLowering(val context: Context) : FileLoweringPass {
|
|
||||||
override fun lower(irFile: IrFile) {
|
|
||||||
val transformer = ForLoopsTransformer(context)
|
|
||||||
// Lower loops
|
|
||||||
irFile.transformChildrenVoid(transformer)
|
|
||||||
|
|
||||||
// Update references in break/continue.
|
|
||||||
irFile.transformChildrenVoid(object: IrElementTransformerVoid() {
|
|
||||||
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
|
|
||||||
transformer.oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
|
|
||||||
return jump
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ForLoopsTransformer(val context: Context) : IrElementTransformerVoidWithContext() {
|
|
||||||
|
|
||||||
private val symbols = context.ir.symbols
|
|
||||||
private val iteratorToLoopInfo = mutableMapOf<IrVariableSymbol, ForLoopInfo>()
|
|
||||||
internal val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
|
|
||||||
|
|
||||||
private val iteratorType = symbols.iterator.descriptor.defaultType.replaceArgumentsWithStarProjections()
|
|
||||||
|
|
||||||
private val scopeOwnerSymbol
|
|
||||||
get() = currentScope!!.scope.scopeOwnerSymbol
|
|
||||||
|
|
||||||
private val progressionElementClasses: List<IrClassSymbol> = mutableListOf(symbols.char).apply {
|
|
||||||
addAll(symbols.integerClasses)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val progressionElementClassesTypes: List<SimpleType> = mutableListOf<SimpleType>().apply {
|
|
||||||
progressionElementClasses.mapTo(this) { it.descriptor.defaultType }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val progressionElementClassesNullableTypes: List<SimpleType> = mutableListOf<SimpleType>().apply {
|
|
||||||
progressionElementClassesTypes.mapTo(this) { it.makeNullableAsSpecified(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
//region Symbols for progression building functions ================================================================
|
|
||||||
private fun getProgressionBuildingMethods(name: String): Set<IrFunctionSymbol> =
|
|
||||||
getMethodsForProgressionElements(name) {
|
|
||||||
it.valueParameters.size == 1 && it.valueParameters[0].type in progressionElementClassesTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getProgressionBuildingExtensions(name: String, pkg: FqName): Set<IrFunctionSymbol> =
|
|
||||||
getExtensionsForProgressionElements(name, pkg) {
|
|
||||||
it.extensionReceiverParameter?.type in progressionElementClassesTypes &&
|
|
||||||
it.valueParameters.size == 1 &&
|
|
||||||
it.valueParameters[0].type in progressionElementClassesTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getMethodsForProgressionElements(name: String,
|
|
||||||
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
|
||||||
mutableSetOf<IrFunctionSymbol>().apply {
|
|
||||||
progressionElementClasses.flatMapTo(this) { receiver ->
|
|
||||||
receiver.descriptor.unsubstitutedMemberScope
|
|
||||||
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
|
||||||
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getExtensionsForProgressionElements(name: String,
|
|
||||||
pkg: FqName,
|
|
||||||
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
|
||||||
mutableSetOf<IrFunctionSymbol>().apply {
|
|
||||||
progressionElementClasses.flatMapTo(this) { _ /* receiver */ ->
|
|
||||||
context.builtIns.builtInsModule.getPackage(pkg).memberScope
|
|
||||||
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
|
||||||
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val rangeToSymbols by lazy { getProgressionBuildingMethods("rangeTo") }
|
|
||||||
private val untilSymbols by lazy { getProgressionBuildingExtensions("until", FqName("kotlin.ranges")) }
|
|
||||||
private val downToSymbols by lazy { getProgressionBuildingExtensions("downTo", FqName("kotlin.ranges")) }
|
|
||||||
private val stepSymbols by lazy {
|
|
||||||
getExtensionsForProgressionElements("step", FqName("kotlin.ranges")) {
|
|
||||||
it.extensionReceiverParameter?.type in symbols.progressionClassesTypes &&
|
|
||||||
it.valueParameters.size == 1 &&
|
|
||||||
(KotlinBuiltIns.isLong(it.valueParameters[0].type) || KotlinBuiltIns.isInt(it.valueParameters[0].type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Util methods ==============================================================================================
|
|
||||||
private fun IrExpression.castIfNecessary(progressionType: ProgressionType): IrExpression {
|
|
||||||
val type = this.type.toKotlinType()
|
|
||||||
assert(type in progressionElementClassesTypes || type in progressionElementClassesNullableTypes)
|
|
||||||
return if (type == progressionType.elementType) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
val function = symbols.getFunction(progressionType.numberCastFunctionName, type)
|
|
||||||
IrCallImpl(startOffset, endOffset, function.owner.returnType, function)
|
|
||||||
.apply { dispatchReceiver = this@castIfNecessary }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression): IrExpression {
|
|
||||||
return if (expression.type.isSimpleTypeWithQuestionMark) {
|
|
||||||
irImplicitCast(expression, expression.type.makeNotNull())
|
|
||||||
} else {
|
|
||||||
expression
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun IrExpression.unaryMinus(): IrExpression {
|
|
||||||
val unaryOperator = symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type.toKotlinType())
|
|
||||||
return IrCallImpl(startOffset, endOffset, unaryOperator.owner.returnType, unaryOperator).apply {
|
|
||||||
dispatchReceiver = this@unaryMinus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ProgressionInfo.defaultStep(startOffset: Int, endOffset: Int): IrExpression =
|
|
||||||
progressionType.elementType.let { type ->
|
|
||||||
val step = if (increasing) 1 else -1
|
|
||||||
when {
|
|
||||||
KotlinBuiltIns.isInt(type) || KotlinBuiltIns.isChar(type) ->
|
|
||||||
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, step)
|
|
||||||
KotlinBuiltIns.isLong(type) ->
|
|
||||||
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, step.toLong())
|
|
||||||
else -> throw IllegalArgumentException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun IrConst<*>.isOne() =
|
|
||||||
when (kind) {
|
|
||||||
IrConstKind.Long -> value as Long == 1L
|
|
||||||
IrConstKind.Int -> value as Int == 1
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used only by the assert.
|
|
||||||
private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) =
|
|
||||||
((progressionType.isCharProgression() || progressionType.isIntProgression()) &&
|
|
||||||
KotlinBuiltIns.isInt(step.type.toKotlinType().makeNotNullable())) ||
|
|
||||||
(progressionType.isLongProgression() &&
|
|
||||||
KotlinBuiltIns.isLong(step.type.toKotlinType().makeNotNullable()))
|
|
||||||
|
|
||||||
private fun irCheckProgressionStep(progressionType: ProgressionType,
|
|
||||||
step: IrExpression): Pair<IrExpression, Boolean> {
|
|
||||||
if (step is IrConst<*> &&
|
|
||||||
((step.kind == IrConstKind.Long && step.value as Long > 0) ||
|
|
||||||
(step.kind == IrConstKind.Int && step.value as Int > 0))) {
|
|
||||||
return step to !step.isOne()
|
|
||||||
}
|
|
||||||
// The frontend checks if the step has a right type (Long for LongProgression and Int for {Int/Char}Progression)
|
|
||||||
// so there is no need to cast it.
|
|
||||||
assert(stepHasRightType(step, progressionType))
|
|
||||||
|
|
||||||
val symbol = symbols.checkProgressionStep[step.type.toKotlinType().makeNotNullable()]
|
|
||||||
?: throw IllegalArgumentException("Unknown progression element type: ${step.type}")
|
|
||||||
return IrCallImpl(step.startOffset, step.endOffset, symbol.owner.returnType, symbol).apply {
|
|
||||||
putValueArgument(0, step)
|
|
||||||
} to true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun irGetProgressionLast(progressionType: ProgressionType,
|
|
||||||
first: IrVariable,
|
|
||||||
lastExpression: IrExpression,
|
|
||||||
step: IrVariable): IrExpression {
|
|
||||||
val symbol = symbols.getProgressionLast[progressionType.elementType]
|
|
||||||
?: throw IllegalArgumentException("Unknown progression element type: ${lastExpression.type}")
|
|
||||||
val startOffset = lastExpression.startOffset
|
|
||||||
val endOffset = lastExpression.endOffset
|
|
||||||
return IrCallImpl(startOffset, lastExpression.endOffset, symbol.owner.returnType, symbol).apply {
|
|
||||||
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first.type, first.symbol))
|
|
||||||
putValueArgument(1, lastExpression.castIfNecessary(progressionType))
|
|
||||||
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step.type, step.symbol))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Util classes ==============================================================================================
|
|
||||||
// TODO: Replace with a cast when such support is added in the boxing lowering.
|
|
||||||
private data class ProgressionType(val elementType: KotlinType, val numberCastFunctionName: Name) {
|
|
||||||
fun isIntProgression() = KotlinBuiltIns.isInt(elementType)
|
|
||||||
fun isLongProgression() = KotlinBuiltIns.isLong(elementType)
|
|
||||||
fun isCharProgression() = KotlinBuiltIns.isChar(elementType)
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ProgressionInfo(
|
|
||||||
val progressionType: ProgressionType,
|
|
||||||
val first: IrExpression,
|
|
||||||
val bound: IrExpression,
|
|
||||||
val step: IrExpression? = null,
|
|
||||||
val increasing: Boolean = true,
|
|
||||||
var needLastCalculation: Boolean = false,
|
|
||||||
val closed: Boolean = true)
|
|
||||||
|
|
||||||
/** Contains information about variables used in the loop. */
|
|
||||||
private data class ForLoopInfo(
|
|
||||||
val progressionInfo: ProgressionInfo,
|
|
||||||
val inductionVariable: IrVariable,
|
|
||||||
val bound: IrVariable,
|
|
||||||
val last: IrVariable,
|
|
||||||
val step: IrVariable,
|
|
||||||
var loopVariable: IrVariable? = null)
|
|
||||||
|
|
||||||
private inner class ProgressionInfoBuilder : IrElementVisitor<ProgressionInfo?, Nothing?> {
|
|
||||||
|
|
||||||
val INT_PROGRESSION = ProgressionType(context.builtIns.intType, Name.identifier("toInt"))
|
|
||||||
val LONG_PROGRESSION = ProgressionType(context.builtIns.longType, Name.identifier("toLong"))
|
|
||||||
val CHAR_PROGRESSION = ProgressionType(context.builtIns.charType, Name.identifier("toChar"))
|
|
||||||
|
|
||||||
private fun buildRangeTo(expression: IrCall, progressionType: ProgressionType) =
|
|
||||||
ProgressionInfo(progressionType,
|
|
||||||
expression.dispatchReceiver!!,
|
|
||||||
expression.getValueArgument(0)!!)
|
|
||||||
|
|
||||||
private fun buildUntil(expression: IrCall, progressionType: ProgressionType): ProgressionInfo =
|
|
||||||
ProgressionInfo(progressionType,
|
|
||||||
expression.extensionReceiver!!,
|
|
||||||
expression.getValueArgument(0)!!,
|
|
||||||
closed = false)
|
|
||||||
|
|
||||||
private fun buildDownTo(expression: IrCall, progressionType: ProgressionType) =
|
|
||||||
ProgressionInfo(progressionType,
|
|
||||||
expression.extensionReceiver!!,
|
|
||||||
expression.getValueArgument(0)!!,
|
|
||||||
increasing = false)
|
|
||||||
|
|
||||||
private fun buildStep(expression: IrCall, progressionType: ProgressionType) =
|
|
||||||
expression.extensionReceiver!!.accept(this, null)?.let {
|
|
||||||
val newStep = expression.getValueArgument(0)!!
|
|
||||||
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(progressionType, newStep)
|
|
||||||
val step = when {
|
|
||||||
it.step == null -> newStepCheck
|
|
||||||
// There were step calls before. Just add our check in the container or create a new one.
|
|
||||||
it.step is IrStatementContainer -> {
|
|
||||||
it.step.statements.add(newStepCheck)
|
|
||||||
it.step
|
|
||||||
}
|
|
||||||
else -> IrCompositeImpl(expression.startOffset, expression.endOffset, newStep.type).apply {
|
|
||||||
statements.add(it.step)
|
|
||||||
statements.add(newStepCheck)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProgressionInfo(progressionType, it.first, it.bound, step, it.increasing, needBoundCalculation, it.closed)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitElement(element: IrElement, data: Nothing?): ProgressionInfo? = null
|
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: Nothing?): ProgressionInfo? {
|
|
||||||
val type = expression.type.toKotlinType()
|
|
||||||
val progressionType = when {
|
|
||||||
type.isSubtypeOf(symbols.charProgression.descriptor.defaultType) -> CHAR_PROGRESSION
|
|
||||||
type.isSubtypeOf(symbols.intProgression.descriptor.defaultType) -> INT_PROGRESSION
|
|
||||||
type.isSubtypeOf(symbols.longProgression.descriptor.defaultType) -> LONG_PROGRESSION
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Process constructors and other factory functions.
|
|
||||||
return when (expression.symbol) {
|
|
||||||
in rangeToSymbols -> buildRangeTo(expression, progressionType)
|
|
||||||
in untilSymbols -> buildUntil(expression, progressionType)
|
|
||||||
in downToSymbols -> buildDownTo(expression, progressionType)
|
|
||||||
in stepSymbols -> buildStep(expression, progressionType)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Lowering ==================================================================================================
|
|
||||||
// Lower a loop header.
|
|
||||||
private fun processHeader(variable: IrVariable, initializer: IrCall): IrStatement? {
|
|
||||||
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
|
|
||||||
val symbol = variable.symbol
|
|
||||||
if (!variable.descriptor.type.isSubtypeOf(iteratorType)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
assert(symbol !in iteratorToLoopInfo)
|
|
||||||
|
|
||||||
val builder = context.createIrBuilder(scopeOwnerSymbol, variable.startOffset, variable.endOffset)
|
|
||||||
// Collect loop info and form the loop header composite.
|
|
||||||
val progressionInfo = initializer.dispatchReceiver?.accept(ProgressionInfoBuilder(), null) ?: return null
|
|
||||||
|
|
||||||
with(builder) {
|
|
||||||
with(progressionInfo) {
|
|
||||||
// Due to features of PSI2IR we can obtain nullable arguments here while actually
|
|
||||||
// they are non-nullable (the frontend takes care about this). So we need to cast them to non-nullable.
|
|
||||||
val statements = mutableListOf<IrStatement>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For this loop:
|
|
||||||
* `for (i in a() .. b() step c() step d())`
|
|
||||||
* We need to call functions in the following order: a, b, c, d.
|
|
||||||
* So we call b() before step calculations and then call last element calculation function (if required).
|
|
||||||
*/
|
|
||||||
val inductionVariable = scope.createTemporaryVariable(first.castIfNecessary(progressionType),
|
|
||||||
nameHint = "inductionVariable",
|
|
||||||
isMutable = true,
|
|
||||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
|
||||||
statements.add(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val boundValue = scope.createTemporaryVariable(bound.castIfNecessary(progressionType),
|
|
||||||
nameHint = "bound",
|
|
||||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
|
|
||||||
.also { statements.add(it) }
|
|
||||||
|
|
||||||
|
|
||||||
val stepExpression = (if (increasing) step else step?.unaryMinus()) ?: defaultStep(startOffset, endOffset)
|
|
||||||
val stepValue = scope.createTemporaryVariable(ensureNotNullable(stepExpression),
|
|
||||||
nameHint = "step",
|
|
||||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
|
||||||
statements.add(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the last element of the progression
|
|
||||||
// The last element can be:
|
|
||||||
// boundValue, if step is 1 and the range is closed.
|
|
||||||
// boundValue - 1, if step is 1 and the range is open.
|
|
||||||
// getProgressionLast(inductionVariable, boundValue, step), if step != 1 and the range is closed.
|
|
||||||
// getProgressionLast(inductionVariable, boundValue - 1, step), if step != 1 and the range is open.
|
|
||||||
var lastExpression: IrExpression? = null
|
|
||||||
if (!closed) {
|
|
||||||
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, boundValue.descriptor.type)
|
|
||||||
lastExpression = irCall(decrementSymbol.owner).apply {
|
|
||||||
dispatchReceiver = irGet(boundValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (needLastCalculation) {
|
|
||||||
lastExpression = irGetProgressionLast(progressionType,
|
|
||||||
inductionVariable,
|
|
||||||
lastExpression ?: irGet(boundValue),
|
|
||||||
stepValue)
|
|
||||||
}
|
|
||||||
val lastValue = if (lastExpression != null) {
|
|
||||||
scope.createTemporaryVariable(lastExpression,
|
|
||||||
nameHint = "last",
|
|
||||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
|
||||||
statements.add(it)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
boundValue
|
|
||||||
}
|
|
||||||
|
|
||||||
iteratorToLoopInfo[symbol] = ForLoopInfo(progressionInfo,
|
|
||||||
inductionVariable,
|
|
||||||
boundValue,
|
|
||||||
lastValue,
|
|
||||||
stepValue)
|
|
||||||
|
|
||||||
return IrCompositeImpl(startOffset, endOffset, context.irBuiltIns.unitType, null, statements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lower getting a next induction variable value.
|
|
||||||
private fun processNext(variable: IrVariable, initializer: IrCall): IrExpression? {
|
|
||||||
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE
|
|
||||||
|| variable.origin == IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
|
|
||||||
val irIteratorAccess = initializer.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
|
||||||
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null // If we didn't lower a corresponding header.
|
|
||||||
val builder = context.createIrBuilder(scopeOwnerSymbol, initializer.startOffset, initializer.endOffset)
|
|
||||||
|
|
||||||
val plusOperator = symbols.getBinaryOperator(
|
|
||||||
OperatorNameConventions.PLUS,
|
|
||||||
forLoopInfo.inductionVariable.descriptor.type,
|
|
||||||
forLoopInfo.step.descriptor.type
|
|
||||||
)
|
|
||||||
forLoopInfo.loopVariable = variable
|
|
||||||
|
|
||||||
with(builder) {
|
|
||||||
variable.initializer = irGet(forLoopInfo.inductionVariable)
|
|
||||||
val increment = irSetVar(forLoopInfo.inductionVariable,
|
|
||||||
irCallOp(plusOperator.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.step)))
|
|
||||||
return IrCompositeImpl(variable.startOffset,
|
|
||||||
variable.endOffset,
|
|
||||||
context.irBuiltIns.unitType,
|
|
||||||
IrStatementOrigin.FOR_LOOP_NEXT,
|
|
||||||
listOf(variable, increment))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopInfo: ForLoopInfo): IrExpression {
|
|
||||||
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
|
|
||||||
// Check if forLoopInfo.bound > MIN_VALUE.
|
|
||||||
val progressionType = forLoopInfo.progressionInfo.progressionType
|
|
||||||
return irCall(context.irBuiltIns.greaterFunByOperandType[context.irBuiltIns.int]?.symbol!!).apply {
|
|
||||||
val minConst = when {
|
|
||||||
progressionType.isIntProgression() -> IrConstImpl
|
|
||||||
.int(startOffset, endOffset, context.irBuiltIns.intType, Int.MIN_VALUE)
|
|
||||||
progressionType.isCharProgression() -> IrConstImpl
|
|
||||||
.char(startOffset, endOffset, context.irBuiltIns.charType, 0.toChar())
|
|
||||||
progressionType.isLongProgression() -> IrConstImpl
|
|
||||||
.long(startOffset, endOffset, context.irBuiltIns.longType, Long.MIN_VALUE)
|
|
||||||
else -> throw IllegalArgumentException("Unknown progression type")
|
|
||||||
}
|
|
||||||
val compareToCall = irCall(symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
|
||||||
forLoopInfo.bound.descriptor.type,
|
|
||||||
minConst.type.toKotlinType())).apply {
|
|
||||||
dispatchReceiver = irGet(forLoopInfo.bound)
|
|
||||||
putValueArgument(0, minConst)
|
|
||||||
}
|
|
||||||
putValueArgument(0, compareToCall)
|
|
||||||
putValueArgument(1, irInt(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Eliminate the loop if we can prove that it will not be executed.
|
|
||||||
private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
|
|
||||||
val builtIns = context.irBuiltIns
|
|
||||||
val increasing = forLoopInfo.progressionInfo.increasing
|
|
||||||
val comparingBuiltIn = if (increasing) builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol
|
|
||||||
else builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol
|
|
||||||
|
|
||||||
// Check if inductionVariable <= last.
|
|
||||||
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
|
||||||
forLoopInfo.inductionVariable.descriptor.type,
|
|
||||||
forLoopInfo.last.descriptor.type)
|
|
||||||
|
|
||||||
val check: IrExpression = irCall(comparingBuiltIn!!).apply {
|
|
||||||
putValueArgument(0, irCallOp(compareTo.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.last)))
|
|
||||||
putValueArgument(1, irInt(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process closed and open ranges in different manners.
|
|
||||||
return if (forLoopInfo.progressionInfo.closed) {
|
|
||||||
irIfThen(check, loop) // if (inductionVariable <= last) { loop }
|
|
||||||
} else {
|
|
||||||
// Take into account a corner case: for (i in a until Int.MIN_VALUE) {}.
|
|
||||||
// if (inductionVariable <= last && bound > MIN_VALUE) { loop }
|
|
||||||
return irIfThen(check, irIfThen(buildMinValueCondition(forLoopInfo), loop))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DeclarationIrBuilder.buildNewCondition(oldCondition: IrExpression): Pair<IrExpression, ForLoopInfo>? {
|
|
||||||
if (oldCondition !is IrCall || oldCondition.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
|
||||||
// Return null if we didn't lower a corresponding header.
|
|
||||||
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null
|
|
||||||
assert(forLoopInfo.loopVariable != null)
|
|
||||||
|
|
||||||
return irCall(context.irBuiltIns.booleanNotSymbol).apply {
|
|
||||||
val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply {
|
|
||||||
putValueArgument(0, irGet(forLoopInfo.loopVariable!!))
|
|
||||||
putValueArgument(1, irGet(forLoopInfo.last))
|
|
||||||
}
|
|
||||||
putValueArgument(0, eqeqCall)
|
|
||||||
} to forLoopInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This loop
|
|
||||||
*
|
|
||||||
* for (i in first..last step foo) { ... }
|
|
||||||
*
|
|
||||||
* is represented in IR in such a manner:
|
|
||||||
*
|
|
||||||
* val it = (first..last step foo).iterator()
|
|
||||||
* while (it.hasNext()) {
|
|
||||||
* val i = it.next()
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* We transform it into the following loop:
|
|
||||||
*
|
|
||||||
* var it = first
|
|
||||||
* if (it <= last) { // (it >= last if the progression is decreasing)
|
|
||||||
* do {
|
|
||||||
* val i = it++
|
|
||||||
* ...
|
|
||||||
* } while (i != last)
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
// TODO: Lower `for (i in a until b)` to loop with precondition: for (i = a; i < b; a++);
|
|
||||||
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
|
|
||||||
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
|
|
||||||
return super.visitWhileLoop(loop)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(context.createIrBuilder(scopeOwnerSymbol, loop.startOffset, loop.endOffset)) {
|
|
||||||
// Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo.
|
|
||||||
// Replace not transparent containers with transparent ones (IrComposite)
|
|
||||||
val newBody = loop.body?.transform(this@ForLoopsTransformer, null)?.let {
|
|
||||||
if (it is IrContainerExpression && !it.isTransparentScope) {
|
|
||||||
with(it) { IrCompositeImpl(startOffset, endOffset, type, origin, statements) }
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop)
|
|
||||||
|
|
||||||
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
|
||||||
label = loop.label
|
|
||||||
condition = newCondition
|
|
||||||
body = newBody
|
|
||||||
}
|
|
||||||
oldLoopToNewLoop[loop] = newLoop
|
|
||||||
// Build a check for an empty progression before the loop.
|
|
||||||
return buildEmptyCheck(newLoop, forLoopInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
|
||||||
val initializer = declaration.initializer
|
|
||||||
if (initializer == null || initializer !is IrCall) {
|
|
||||||
return super.visitVariable(declaration)
|
|
||||||
}
|
|
||||||
val result = when (initializer.origin) {
|
|
||||||
IrStatementOrigin.FOR_LOOP_ITERATOR -> processHeader(declaration, initializer)
|
|
||||||
IrStatementOrigin.FOR_LOOP_NEXT -> processNext(declaration, initializer)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return result ?: super.visitVariable(declaration)
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
+395
@@ -0,0 +1,395 @@
|
|||||||
|
/*
|
||||||
|
* 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.loops
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.typeWithStarProjections
|
||||||
|
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
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.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
|
/** This lowering pass optimizes range-based for loops.
|
||||||
|
*
|
||||||
|
* NB! not implemented yet.
|
||||||
|
* Process `for (elem in array) { f(elem) }` construct first.
|
||||||
|
* Replace with `for (i in array.indices) { f(array.get(i)) }`.
|
||||||
|
*
|
||||||
|
* Next replace iteration over ranges (X.indices, a..b, etc.) with
|
||||||
|
* simple while loop over primitive induction variable.
|
||||||
|
*/
|
||||||
|
internal class ForLoopsLowering(val context: Context) : FileLoweringPass {
|
||||||
|
|
||||||
|
private val progressionInfoBuilder = ProgressionInfoBuilder(context)
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
val transformer = ForLoopsTransformer(context, progressionInfoBuilder)
|
||||||
|
// Lower loops
|
||||||
|
irFile.transformChildrenVoid(transformer)
|
||||||
|
|
||||||
|
// Update references in break/continue.
|
||||||
|
irFile.transformChildrenVoid(object: IrElementTransformerVoid() {
|
||||||
|
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
|
||||||
|
transformer.oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
|
||||||
|
return jump
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Contains information about variables used in the loop. */
|
||||||
|
internal data class ForLoopInfo(
|
||||||
|
val progressionInfo: ProgressionInfo,
|
||||||
|
val inductionVariable: IrVariable,
|
||||||
|
val bound: IrVariable,
|
||||||
|
val last: IrVariable,
|
||||||
|
val step: IrVariable,
|
||||||
|
var loopVariable: IrVariable? = null)
|
||||||
|
|
||||||
|
private fun ProgressionType.elementType(context: Context): IrType = when (this) {
|
||||||
|
ProgressionType.INT_PROGRESSION -> context.irBuiltIns.intType
|
||||||
|
ProgressionType.LONG_PROGRESSION -> context.irBuiltIns.longType
|
||||||
|
ProgressionType.CHAR_PROGRESSION -> context.irBuiltIns.charType
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ForLoopsTransformer(val context: Context, val progressionInfoBuilder: ProgressionInfoBuilder) : IrElementTransformerVoidWithContext() {
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
private val iteratorToLoopInfo = mutableMapOf<IrVariableSymbol, ForLoopInfo>()
|
||||||
|
internal val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
|
||||||
|
|
||||||
|
private val progressionElementClasses =
|
||||||
|
(symbols.integerClasses + symbols.char).toSet()
|
||||||
|
|
||||||
|
private val scopeOwnerSymbol
|
||||||
|
get() = currentScope!!.scope.scopeOwnerSymbol
|
||||||
|
|
||||||
|
//region Util methods ==============================================================================================
|
||||||
|
private fun IrExpression.castIfNecessary(progressionType: ProgressionType): IrExpression {
|
||||||
|
assert(type.classifierOrNull in progressionElementClasses)
|
||||||
|
return if (type.classifierOrNull == progressionType.elementType(context).classifierOrNull) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
val function = symbols.getFunction(progressionType.numberCastFunctionName, type.toKotlinType())
|
||||||
|
IrCallImpl(startOffset, endOffset, function.owner.returnType, function)
|
||||||
|
.apply { dispatchReceiver = this@castIfNecessary }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression): IrExpression {
|
||||||
|
return if (expression.type.isSimpleTypeWithQuestionMark) {
|
||||||
|
irImplicitCast(expression, expression.type.makeNotNull())
|
||||||
|
} else {
|
||||||
|
expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrExpression.unaryMinus(): IrExpression {
|
||||||
|
val unaryOperator = symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type.toKotlinType())
|
||||||
|
return IrCallImpl(startOffset, endOffset, unaryOperator.owner.returnType, unaryOperator).apply {
|
||||||
|
dispatchReceiver = this@unaryMinus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ProgressionInfo.defaultStep(startOffset: Int, endOffset: Int): IrExpression {
|
||||||
|
val type = progressionType.elementType(context)
|
||||||
|
val step = if (increasing) 1 else -1
|
||||||
|
return when {
|
||||||
|
type.isInt() || type.isChar() ->
|
||||||
|
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, step)
|
||||||
|
type.isLong() ->
|
||||||
|
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, step.toLong())
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun irGetProgressionLast(progressionType: ProgressionType,
|
||||||
|
first: IrVariable,
|
||||||
|
lastExpression: IrExpression,
|
||||||
|
step: IrVariable): IrExpression {
|
||||||
|
val symbol = symbols.getProgressionLast[progressionType.elementType(context).toKotlinType()]
|
||||||
|
?: throw IllegalArgumentException("No `getProgressionLast` for type ${step.type} ${lastExpression.type}")
|
||||||
|
val startOffset = lastExpression.startOffset
|
||||||
|
val endOffset = lastExpression.endOffset
|
||||||
|
return IrCallImpl(startOffset, lastExpression.endOffset, symbol.owner.returnType, symbol).apply {
|
||||||
|
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first.type, first.symbol))
|
||||||
|
putValueArgument(1, lastExpression.castIfNecessary(progressionType))
|
||||||
|
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step.type, step.symbol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region Lowering ==================================================================================================
|
||||||
|
// Lower a loop header.
|
||||||
|
private fun processHeader(variable: IrVariable, initializer: IrCall): IrStatement? {
|
||||||
|
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
|
||||||
|
val symbol = variable.symbol
|
||||||
|
val iteratorType = symbols.iterator.typeWithStarProjections
|
||||||
|
|
||||||
|
if (!variable.type.isSubtypeOf(iteratorType)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
assert(symbol !in iteratorToLoopInfo)
|
||||||
|
|
||||||
|
val builder = context.createIrBuilder(scopeOwnerSymbol, variable.startOffset, variable.endOffset)
|
||||||
|
// Collect loop info and form the loop header composite.
|
||||||
|
val progressionInfo = initializer.dispatchReceiver?.accept(progressionInfoBuilder, null)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
with(builder) {
|
||||||
|
with(progressionInfo) {
|
||||||
|
// Due to features of PSI2IR we can obtain nullable arguments here while actually
|
||||||
|
// they are non-nullable (the frontend takes care about this). So we need to cast them to non-nullable.
|
||||||
|
val statements = mutableListOf<IrStatement>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For this loop:
|
||||||
|
* `for (i in a() .. b() step c() step d())`
|
||||||
|
* We need to call functions in the following order: a, b, c, d.
|
||||||
|
* So we call b() before step calculations and then call last element calculation function (if required).
|
||||||
|
*/
|
||||||
|
val inductionVariable = scope.createTemporaryVariable(first.castIfNecessary(progressionType),
|
||||||
|
nameHint = "inductionVariable",
|
||||||
|
isMutable = true,
|
||||||
|
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
||||||
|
statements.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val boundValue = scope.createTemporaryVariable(ensureNotNullable(bound.castIfNecessary(progressionType)),
|
||||||
|
nameHint = "bound",
|
||||||
|
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
|
||||||
|
.also { statements.add(it) }
|
||||||
|
|
||||||
|
val stepExpression = if (step != null) {
|
||||||
|
if (increasing) step else step.unaryMinus()
|
||||||
|
} else {
|
||||||
|
defaultStep(startOffset, endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
val stepValue = scope.createTemporaryVariable(ensureNotNullable(stepExpression),
|
||||||
|
nameHint = "step",
|
||||||
|
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
||||||
|
statements.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the last element of the progression
|
||||||
|
// The last element can be:
|
||||||
|
// boundValue, if step is 1 and the range is closed.
|
||||||
|
// boundValue - 1, if step is 1 and the range is open.
|
||||||
|
// getProgressionLast(inductionVariable, boundValue, step), if step != 1 and the range is closed.
|
||||||
|
// getProgressionLast(inductionVariable, boundValue - 1, step), if step != 1 and the range is open.
|
||||||
|
var lastExpression: IrExpression = if (!closed) {
|
||||||
|
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, boundValue.type.toKotlinType())
|
||||||
|
irCall(decrementSymbol.owner).apply {
|
||||||
|
dispatchReceiver = irGet(boundValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
irGet(boundValue)
|
||||||
|
}
|
||||||
|
if (needLastCalculation) {
|
||||||
|
lastExpression = irGetProgressionLast(progressionType,
|
||||||
|
inductionVariable,
|
||||||
|
lastExpression,
|
||||||
|
stepValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastValue = scope.createTemporaryVariable(lastExpression,
|
||||||
|
nameHint = "last",
|
||||||
|
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
|
||||||
|
statements.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
iteratorToLoopInfo[symbol] = ForLoopInfo(progressionInfo,
|
||||||
|
inductionVariable,
|
||||||
|
boundValue,
|
||||||
|
lastValue,
|
||||||
|
stepValue)
|
||||||
|
|
||||||
|
return IrCompositeImpl(startOffset, endOffset, context.irBuiltIns.unitType, null, statements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower getting a next induction variable value.
|
||||||
|
private fun processNext(variable: IrVariable, initializer: IrCall): IrExpression? {
|
||||||
|
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE
|
||||||
|
|| variable.origin == IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
|
||||||
|
val irIteratorAccess = initializer.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
||||||
|
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null // If we didn't lower a corresponding header.
|
||||||
|
val builder = context.createIrBuilder(scopeOwnerSymbol, initializer.startOffset, initializer.endOffset)
|
||||||
|
|
||||||
|
val plusOperator = symbols.getBinaryOperator(
|
||||||
|
OperatorNameConventions.PLUS,
|
||||||
|
forLoopInfo.inductionVariable.type.toKotlinType(),
|
||||||
|
forLoopInfo.step.type.toKotlinType()
|
||||||
|
)
|
||||||
|
forLoopInfo.loopVariable = variable
|
||||||
|
|
||||||
|
with(builder) {
|
||||||
|
variable.initializer = irGet(forLoopInfo.inductionVariable)
|
||||||
|
val increment = irSetVar(forLoopInfo.inductionVariable,
|
||||||
|
irCallOp(plusOperator.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.step)))
|
||||||
|
return IrCompositeImpl(variable.startOffset,
|
||||||
|
variable.endOffset,
|
||||||
|
context.irBuiltIns.unitType,
|
||||||
|
IrStatementOrigin.FOR_LOOP_NEXT,
|
||||||
|
listOf(variable, increment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopInfo: ForLoopInfo): IrExpression {
|
||||||
|
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
|
||||||
|
// Check if forLoopInfo.bound > MIN_VALUE.
|
||||||
|
val progressionType = forLoopInfo.progressionInfo.progressionType
|
||||||
|
val irBuiltIns = context.irBuiltIns
|
||||||
|
val minConst = when (progressionType) {
|
||||||
|
ProgressionType.INT_PROGRESSION -> IrConstImpl
|
||||||
|
.int(startOffset, endOffset, irBuiltIns.intType, Int.MIN_VALUE)
|
||||||
|
ProgressionType.CHAR_PROGRESSION -> IrConstImpl
|
||||||
|
.char(startOffset, endOffset, irBuiltIns.charType, 0.toChar())
|
||||||
|
ProgressionType.LONG_PROGRESSION -> IrConstImpl
|
||||||
|
.long(startOffset, endOffset, irBuiltIns.longType, Long.MIN_VALUE)
|
||||||
|
}
|
||||||
|
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
||||||
|
forLoopInfo.bound.type.toKotlinType(),
|
||||||
|
minConst.type.toKotlinType())
|
||||||
|
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]?.symbol!!).apply {
|
||||||
|
val compareToCall = irCall(compareTo).apply {
|
||||||
|
dispatchReceiver = irGet(forLoopInfo.bound)
|
||||||
|
putValueArgument(0, minConst)
|
||||||
|
}
|
||||||
|
putValueArgument(0, compareToCall)
|
||||||
|
putValueArgument(1, irInt(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Eliminate the loop if we can prove that it will not be executed.
|
||||||
|
private fun DeclarationIrBuilder.buildEmptinessCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
|
||||||
|
val builtIns = context.irBuiltIns
|
||||||
|
val increasing = forLoopInfo.progressionInfo.increasing
|
||||||
|
val comparingBuiltIn = if (increasing) builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol
|
||||||
|
else builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol
|
||||||
|
|
||||||
|
// Check if inductionVariable <= last.
|
||||||
|
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
||||||
|
forLoopInfo.inductionVariable.type.toKotlinType(),
|
||||||
|
forLoopInfo.last.type.toKotlinType())
|
||||||
|
|
||||||
|
val check: IrExpression = irCall(comparingBuiltIn!!).apply {
|
||||||
|
putValueArgument(0, irCallOp(compareTo.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.last)))
|
||||||
|
putValueArgument(1, irInt(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process closed and open ranges in different manners.
|
||||||
|
return if (forLoopInfo.progressionInfo.closed) {
|
||||||
|
irIfThen(check, loop) // if (inductionVariable <= last) { loop }
|
||||||
|
} else {
|
||||||
|
// Take into account a corner case: for (i in a until Int.MIN_VALUE) {}.
|
||||||
|
// if (inductionVariable <= last && bound > MIN_VALUE) { loop }
|
||||||
|
irIfThen(check, irIfThen(buildMinValueCondition(forLoopInfo), loop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.buildNewCondition(oldCondition: IrExpression): Pair<IrExpression, ForLoopInfo>? {
|
||||||
|
if (oldCondition !is IrCall || oldCondition.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
||||||
|
// Return null if we didn't lower a corresponding header.
|
||||||
|
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null
|
||||||
|
assert(forLoopInfo.loopVariable != null)
|
||||||
|
|
||||||
|
return irCall(context.irBuiltIns.booleanNotSymbol).apply {
|
||||||
|
val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||||
|
putValueArgument(0, irGet(forLoopInfo.loopVariable!!))
|
||||||
|
putValueArgument(1, irGet(forLoopInfo.last))
|
||||||
|
}
|
||||||
|
putValueArgument(0, eqeqCall)
|
||||||
|
} to forLoopInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This loop
|
||||||
|
*
|
||||||
|
* for (i in first..last step foo) { ... }
|
||||||
|
*
|
||||||
|
* is represented in IR in such a manner:
|
||||||
|
*
|
||||||
|
* val it = (first..last step foo).iterator()
|
||||||
|
* while (it.hasNext()) {
|
||||||
|
* val i = it.next()
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* We transform it into the following loop:
|
||||||
|
*
|
||||||
|
* var it = first
|
||||||
|
* if (it <= last) { // (it >= last if the progression is decreasing)
|
||||||
|
* do {
|
||||||
|
* val i = it++
|
||||||
|
* ...
|
||||||
|
* } while (i != last)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
// TODO: Lower `for (i in a until b)` to loop with precondition: for (i = a; i < b; a++);
|
||||||
|
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
|
||||||
|
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
|
||||||
|
return super.visitWhileLoop(loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
with(context.createIrBuilder(scopeOwnerSymbol, loop.startOffset, loop.endOffset)) {
|
||||||
|
// Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo.
|
||||||
|
// Replace not transparent containers with transparent ones (IrComposite)
|
||||||
|
val newBody = loop.body?.transform(this@ForLoopsTransformer, null)?.let {
|
||||||
|
if (it is IrContainerExpression && !it.isTransparentScope) {
|
||||||
|
IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop)
|
||||||
|
|
||||||
|
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||||
|
label = loop.label
|
||||||
|
condition = newCondition
|
||||||
|
body = newBody
|
||||||
|
}
|
||||||
|
oldLoopToNewLoop[loop] = newLoop
|
||||||
|
// Build a check for an empty progression before the loop.
|
||||||
|
return buildEmptinessCheck(newLoop, forLoopInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
|
val initializer = declaration.initializer
|
||||||
|
if (initializer == null || initializer !is IrCall) {
|
||||||
|
return super.visitVariable(declaration)
|
||||||
|
}
|
||||||
|
val result = when (initializer.origin) {
|
||||||
|
IrStatementOrigin.FOR_LOOP_ITERATOR -> processHeader(declaration, initializer)
|
||||||
|
IrStatementOrigin.FOR_LOOP_NEXT -> processNext(declaration, initializer)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return result ?: super.visitVariable(declaration)
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
}
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower.loops
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.matchers.IrCallMatcher
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.matchers.SimpleCalleeMatcher
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.matchers.createIrCallMatcher
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.matchers.singleArgumentExtension
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
|
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.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
internal class RangeToHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
|
||||||
|
override val matcher = SimpleCalleeMatcher {
|
||||||
|
dispatchReceiver { it != null && it.type.classifierOrNull in progressionElementClasses }
|
||||||
|
fqName { it.pathSegments().last() == Name.identifier("rangeTo") }
|
||||||
|
parameterCount { it == 1 }
|
||||||
|
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(call: IrCall, progressionType: ProgressionType) =
|
||||||
|
ProgressionInfo(progressionType, call.dispatchReceiver!!, call.getValueArgument(0)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DownToHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
|
||||||
|
override val matcher = SimpleCalleeMatcher {
|
||||||
|
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementClasses)
|
||||||
|
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? =
|
||||||
|
ProgressionInfo(progressionType,
|
||||||
|
call.extensionReceiver!!,
|
||||||
|
call.getValueArgument(0)!!,
|
||||||
|
increasing = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class UntilHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
|
||||||
|
override val matcher = SimpleCalleeMatcher {
|
||||||
|
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementClasses)
|
||||||
|
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? =
|
||||||
|
ProgressionInfo(progressionType,
|
||||||
|
call.extensionReceiver!!,
|
||||||
|
call.getValueArgument(0)!!,
|
||||||
|
closed = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class IndicesHandler(val context: Context) : ProgressionHandler {
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
|
private val supportedArrays = symbols.primitiveArrays.values + symbols.array
|
||||||
|
|
||||||
|
override val matcher = createIrCallMatcher {
|
||||||
|
callee {
|
||||||
|
fqName { it == FqName("kotlin.collections.<get-indices>") }
|
||||||
|
parameterCount { it == 0 }
|
||||||
|
}
|
||||||
|
extensionReceiver { it != null && it.type.classifierOrNull in supportedArrays }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? {
|
||||||
|
val int0 = IrConstImpl.int(call.startOffset, call.endOffset, context.irBuiltIns.intType, 0)
|
||||||
|
|
||||||
|
val bound = with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
|
||||||
|
val clazz = call.extensionReceiver!!.type.classifierOrFail
|
||||||
|
val symbol = symbols.arraySize[clazz]!!
|
||||||
|
irCall(symbol).apply {
|
||||||
|
dispatchReceiver = call.extensionReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ProgressionInfo(progressionType, int0, bound, closed = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class StepHandler(context: Context, val visitor: IrElementVisitor<ProgressionInfo?, Nothing?>) : ProgressionHandler {
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
|
override val matcher: IrCallMatcher = SimpleCalleeMatcher {
|
||||||
|
singleArgumentExtension(FqName("kotlin.ranges.step"), symbols.progressionClasses)
|
||||||
|
parameter(0) { it.type.isInt() || it.type.isLong() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? {
|
||||||
|
val nestedInfo = call.extensionReceiver!!.accept(visitor, null)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
// Due to KT-27607 nested non-default steps could lead to incorrect behaviour.
|
||||||
|
// So disable optimization of such rare cases for now.
|
||||||
|
if (nestedInfo.step != null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val newStep = call.getValueArgument(0)!!
|
||||||
|
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(symbols, progressionType, newStep)
|
||||||
|
return ProgressionInfo(
|
||||||
|
progressionType,
|
||||||
|
nestedInfo.first,
|
||||||
|
nestedInfo.bound,
|
||||||
|
newStepCheck, nestedInfo.increasing,
|
||||||
|
needBoundCalculation,
|
||||||
|
nestedInfo.closed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrConst<*>.stepIsOne() = when (kind) {
|
||||||
|
IrConstKind.Long -> value as Long == 1L
|
||||||
|
IrConstKind.Int -> value as Int == 1
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrExpression.isPositiveConst() = this is IrConst<*> &&
|
||||||
|
((kind == IrConstKind.Long && value as Long > 0) || (kind == IrConstKind.Int && value as Int > 0))
|
||||||
|
|
||||||
|
// Used only by the assert.
|
||||||
|
private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) = when (progressionType) {
|
||||||
|
|
||||||
|
ProgressionType.CHAR_PROGRESSION,
|
||||||
|
ProgressionType.INT_PROGRESSION -> step.type.makeNotNull().isInt()
|
||||||
|
|
||||||
|
ProgressionType.LONG_PROGRESSION -> step.type.makeNotNull().isLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun irCheckProgressionStep(symbols: KonanSymbols, progressionType: ProgressionType, step: IrExpression) =
|
||||||
|
if (step.isPositiveConst()) {
|
||||||
|
step to !(step as IrConst<*>).stepIsOne()
|
||||||
|
} else {
|
||||||
|
// The frontend checks if the step has a right type (Long for LongProgression and Int for {Int/Char}Progression)
|
||||||
|
// so there is no need to cast it.
|
||||||
|
assert(stepHasRightType(step, progressionType))
|
||||||
|
|
||||||
|
val symbol = symbols.checkProgressionStep[step.type.makeNotNull().toKotlinType()]
|
||||||
|
?: throw IllegalArgumentException("No `checkProgressionStep` for type ${step.type}")
|
||||||
|
IrCallImpl(step.startOffset, step.endOffset, symbol.owner.returnType, symbol).apply {
|
||||||
|
putValueArgument(0, step)
|
||||||
|
} to true
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower.loops
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.matchers.*
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
|
|
||||||
|
enum class ProgressionType(val numberCastFunctionName: Name) {
|
||||||
|
INT_PROGRESSION(Name.identifier("toInt")),
|
||||||
|
LONG_PROGRESSION(Name.identifier("toLong")),
|
||||||
|
CHAR_PROGRESSION(Name.identifier("toChar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class ProgressionInfo(
|
||||||
|
val progressionType: ProgressionType,
|
||||||
|
val first: IrExpression,
|
||||||
|
val bound: IrExpression,
|
||||||
|
val step: IrExpression? = null,
|
||||||
|
val increasing: Boolean = true,
|
||||||
|
var needLastCalculation: Boolean = false,
|
||||||
|
val closed: Boolean = true)
|
||||||
|
|
||||||
|
internal interface ProgressionHandler {
|
||||||
|
val matcher: IrCallMatcher
|
||||||
|
|
||||||
|
fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo?
|
||||||
|
|
||||||
|
fun handle(irCall: IrCall, progressionType: ProgressionType) = if (matcher(irCall)) {
|
||||||
|
build(irCall, progressionType)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ProgressionInfoBuilder(val context: Context) : IrElementVisitor<ProgressionInfo?, Nothing?> {
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
|
private val progressionElementClasses = symbols.integerClasses + symbols.char
|
||||||
|
|
||||||
|
private val handlers = listOf(
|
||||||
|
IndicesHandler(context),
|
||||||
|
UntilHandler(progressionElementClasses),
|
||||||
|
DownToHandler(progressionElementClasses),
|
||||||
|
StepHandler(context, this),
|
||||||
|
RangeToHandler(progressionElementClasses)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun IrType.getProgressionType(): ProgressionType? = when {
|
||||||
|
isSubtypeOf(symbols.charProgression.owner.defaultType) -> ProgressionType.CHAR_PROGRESSION
|
||||||
|
isSubtypeOf(symbols.intProgression.owner.defaultType) -> ProgressionType.INT_PROGRESSION
|
||||||
|
isSubtypeOf(symbols.longProgression.owner.defaultType) -> ProgressionType.LONG_PROGRESSION
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement, data: Nothing?): ProgressionInfo? = null
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall, data: Nothing?): ProgressionInfo? {
|
||||||
|
val progressionType = expression.type.getProgressionType()
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return handlers.firstNotNullResult { it.handle(expression, progressionType) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower.matchers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
|
||||||
|
|
||||||
|
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 open class IrCallMatcherContainer : IrCallMatcher {
|
||||||
|
|
||||||
|
private val matchers = mutableListOf<IrCallMatcher>()
|
||||||
|
|
||||||
|
fun add(matcher: IrCallMatcher) {
|
||||||
|
matchers += matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extensionReceiver(restriction: (IrExpression?) -> Boolean) =
|
||||||
|
add(IrCallExtensionReceiverMatcher(restriction))
|
||||||
|
|
||||||
|
fun callee(restrictions: IrFunctionMatcherContainer.() -> Unit) {
|
||||||
|
add(SimpleCalleeMatcher(restrictions))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(call: IrCall) = matchers.all { it(call) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun createIrCallMatcher(restrictions: IrCallMatcherContainer.() -> Unit) =
|
||||||
|
IrCallMatcherContainer().apply(restrictions)
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower.matchers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
|
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.fqNameSafe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
classes: Collection<IrClassSymbol>
|
||||||
|
): IrFunctionMatcherContainer {
|
||||||
|
extensionReceiver { it != null && it.type.classifierOrNull in classes }
|
||||||
|
parameterCount { it == 1 }
|
||||||
|
fqName { it == fqName }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package org.jetbrains.ring
|
||||||
|
|
||||||
|
class ForLoopsBenchmark {
|
||||||
|
|
||||||
|
private val array: Array<Int> = Array(BENCHMARK_SIZE) {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
private val intArray: IntArray = IntArray(BENCHMARK_SIZE) {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
private val charArray: CharArray = CharArray(BENCHMARK_SIZE) {
|
||||||
|
it.toChar()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val string: String = charArray.joinToString()
|
||||||
|
|
||||||
|
private val floatArray: FloatArray = FloatArray(BENCHMARK_SIZE) {
|
||||||
|
it.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun arrayLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (e in array) {
|
||||||
|
sum += e
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intArrayLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (e in intArray) {
|
||||||
|
sum += e
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun charArrayLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (e in charArray) {
|
||||||
|
sum += e.toLong()
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stringLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (e in string) {
|
||||||
|
sum += e.hashCode()
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun floatArrayLoop(): Double {
|
||||||
|
var sum = 0.0
|
||||||
|
for (e in array) {
|
||||||
|
sum += e
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterations over .indices
|
||||||
|
|
||||||
|
fun arrayIndicesLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (i in array.indices) {
|
||||||
|
sum += array[i]
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intArrayIndicesLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (i in intArray.indices) {
|
||||||
|
sum += intArray[i]
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun charArrayIndicesLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (i in charArray.indices) {
|
||||||
|
sum += charArray[i].toLong()
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stringIndicesLoop(): Long {
|
||||||
|
var sum = 0L
|
||||||
|
for (i in string.indices) {
|
||||||
|
sum += string[i].hashCode()
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun floatArrayIndicesLoop(): Double {
|
||||||
|
var sum = 0.0
|
||||||
|
for (i in floatArray.indices) {
|
||||||
|
sum += floatArray[i]
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,6 +79,7 @@ class Launcher(val numWarmIterations: Int) {
|
|||||||
runElvisBenchmark()
|
runElvisBenchmark()
|
||||||
runEulerBenchmark()
|
runEulerBenchmark()
|
||||||
runFibonacciBenchmark()
|
runFibonacciBenchmark()
|
||||||
|
runForLoopBenchmark()
|
||||||
runInlineBenchmark()
|
runInlineBenchmark()
|
||||||
runIntArrayBenchmark()
|
runIntArrayBenchmark()
|
||||||
runIntBaselineBenchmark()
|
runIntBaselineBenchmark()
|
||||||
@@ -267,6 +268,24 @@ class Launcher(val numWarmIterations: Int) {
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
fun runForLoopBenchmark() {
|
||||||
|
val benchmark = ForLoopsBenchmark()
|
||||||
|
|
||||||
|
results["ForLoops.arrayLoop"] = launch(benchmark::arrayLoop)
|
||||||
|
results["ForLoops.intArrayLoop"] = launch(benchmark::intArrayLoop)
|
||||||
|
results["ForLoops.floatArrayLoop"] = launch(benchmark::floatArrayLoop)
|
||||||
|
results["ForLoops.charArrayLoop"] = launch(benchmark::charArrayLoop)
|
||||||
|
results["ForLoops.stringLoop"] = launch(benchmark::stringLoop)
|
||||||
|
|
||||||
|
results["ForLoops.arrayIndicesLoop"] = launch(benchmark::arrayIndicesLoop)
|
||||||
|
results["ForLoops.intArrayIndicesLoop"] = launch(benchmark::intArrayIndicesLoop)
|
||||||
|
results["ForLoops.floatArrayIndicesLoop"] = launch(benchmark::floatArrayIndicesLoop)
|
||||||
|
results["ForLoops.charArrayIndicesLoop"] = launch(benchmark::charArrayIndicesLoop)
|
||||||
|
results["ForLoops.stringIndicesLoop"] = launch(benchmark::stringIndicesLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun runInlineBenchmark() {
|
fun runInlineBenchmark() {
|
||||||
val benchmark = InlineBenchmark()
|
val benchmark = InlineBenchmark()
|
||||||
results["Inline.calculate"] = launch(benchmark::calculate)
|
results["Inline.calculate"] = launch(benchmark::calculate)
|
||||||
|
|||||||
Reference in New Issue
Block a user