Cache array length in for-in-array loop if possible
If the range expression is not a local variable (which can be updated in the loop body affecting loop behavior, see KT-21354), we can cache the array length, thus turning a for-in-array loop into a simple optimizable counter loop. #KT-21321 In Progress
This commit is contained in:
@@ -23,9 +23,9 @@ import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class ArrayRangeValue : RangeValue {
|
||||
class ArrayRangeValue(private val canCacheArrayLength: Boolean) : RangeValue {
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
ForInArrayLoopGenerator(codegen, forExpression)
|
||||
ForInArrayLoopGenerator(codegen, forExpression, canCacheArrayLength)
|
||||
|
||||
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
|
||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.range
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -44,7 +45,7 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
|
||||
|
||||
return when {
|
||||
asmRangeType.sort == Type.ARRAY ->
|
||||
ArrayRangeValue()
|
||||
ArrayRangeValue(!isLocalVarReference(rangeExpression, bindingContext))
|
||||
isPrimitiveRange(rangeType) ->
|
||||
PrimitiveRangeRangeValue()
|
||||
isPrimitiveProgression(rangeType) ->
|
||||
@@ -56,6 +57,12 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
|
||||
}
|
||||
}
|
||||
|
||||
fun isLocalVarReference(rangeExpression: KtExpression, bindingContext: BindingContext): Boolean {
|
||||
if (rangeExpression !is KtSimpleNameExpression) return false
|
||||
val resultingDescriptor = rangeExpression.getResolvedCall(bindingContext)?.resultingDescriptor ?: return false
|
||||
return resultingDescriptor is LocalVariableDescriptor && resultingDescriptor.isVar
|
||||
}
|
||||
|
||||
private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) =
|
||||
KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.getBuiltInClassByName(Name.identifier("CharSequence")).defaultType)
|
||||
|
||||
|
||||
+26
-10
@@ -17,21 +17,25 @@
|
||||
package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
|
||||
class ForInArrayLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression)
|
||||
: AbstractForLoopGenerator(codegen, forExpression)
|
||||
{
|
||||
class ForInArrayLoopGenerator(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
// We can cache array length if the corresponding range expression is not a local var.
|
||||
// See https://youtrack.jetbrains.com/issue/KT-21354.
|
||||
private val canCacheArrayLength: Boolean
|
||||
) : AbstractForLoopGenerator(codegen, forExpression) {
|
||||
private var indexVar: Int = 0
|
||||
private var arrayVar: Int = 0
|
||||
private var arrayLengthVar: Int = 0
|
||||
private val loopRangeType: KotlinType = bindingContext.getType(forExpression.loopRange!!)!!
|
||||
|
||||
override fun beforeLoop() {
|
||||
@@ -51,6 +55,13 @@ class ForInArrayLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForEx
|
||||
v.store(arrayVar, OBJECT_TYPE)
|
||||
}
|
||||
|
||||
if (canCacheArrayLength) {
|
||||
arrayLengthVar = createLoopTempVariable(Type.INT_TYPE)
|
||||
v.load(arrayVar, OBJECT_TYPE)
|
||||
v.arraylength()
|
||||
v.store(arrayLengthVar, Type.INT_TYPE)
|
||||
}
|
||||
|
||||
v.iconst(0)
|
||||
v.store(indexVar, Type.INT_TYPE)
|
||||
}
|
||||
@@ -59,8 +70,13 @@ class ForInArrayLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForEx
|
||||
|
||||
override fun checkPreCondition(loopExit: Label) {
|
||||
v.load(indexVar, Type.INT_TYPE)
|
||||
v.load(arrayVar, OBJECT_TYPE)
|
||||
v.arraylength()
|
||||
if (canCacheArrayLength) {
|
||||
v.load(arrayLengthVar, Type.INT_TYPE)
|
||||
}
|
||||
else {
|
||||
v.load(arrayVar, OBJECT_TYPE)
|
||||
v.arraylength()
|
||||
}
|
||||
v.ificmpge(loopExit)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user