KT-12733 'rangeTo' as a non-qualified call is not optimized in 'for'
Use ResolvedCall corresponding to 'for' loop range expression for optimized "for-in-range-literal". Cleanup.
This commit is contained in:
@@ -572,22 +572,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@Override
|
||||
public StackValue visitForExpression(@NotNull KtForExpression forExpression, StackValue receiver) {
|
||||
// Is it a "1..2" or so
|
||||
RangeCodegenUtil.BinaryCall binaryCall = RangeCodegenUtil.getRangeAsBinaryCall(forExpression);
|
||||
if (binaryCall != null) {
|
||||
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(binaryCall.op, bindingContext);
|
||||
if (resolvedCall != null) {
|
||||
if (RangeCodegenUtil.isOptimizableRangeTo(resolvedCall.getResultingDescriptor())) {
|
||||
generateForLoop(new ForInRangeLiteralLoopGenerator(forExpression, binaryCall));
|
||||
return StackValue.none();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedCall<? extends CallableDescriptor> loopRangeCall = RangeCodegenUtil.getLoopRangeResolvedCall(forExpression, bindingContext);
|
||||
if (loopRangeCall != null) {
|
||||
CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor();
|
||||
if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
||||
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
||||
generateForLoop(createForInRangeLiteralLoopGenerator(forExpression, loopRangeCall));
|
||||
return StackValue.none();
|
||||
}
|
||||
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
||||
generateForLoop(createForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall));
|
||||
return StackValue.none();
|
||||
}
|
||||
@@ -621,6 +613,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
private AbstractForLoopGenerator createForInRangeLiteralLoopGenerator(
|
||||
@NotNull KtForExpression forExpression,
|
||||
@NotNull ResolvedCall<? extends CallableDescriptor> loopRangeCall
|
||||
) {
|
||||
ReceiverValue from = loopRangeCall.getDispatchReceiver();
|
||||
KtExpression to = loopRangeCall.getValueArgumentsByIndex().get(0).getArguments().get(0).getArgumentExpression();
|
||||
return new ForInRangeLiteralLoopGenerator(forExpression, from, to);
|
||||
}
|
||||
|
||||
private AbstractForLoopGenerator createForInCollectionIndicesRangeLoopGenerator(
|
||||
@NotNull KtForExpression forExpression,
|
||||
@NotNull ResolvedCall<? extends CallableDescriptor> loopRangeCall
|
||||
@@ -1075,23 +1076,23 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
private class ForInRangeLiteralLoopGenerator extends AbstractForInRangeLoopGenerator {
|
||||
private final RangeCodegenUtil.BinaryCall rangeCall;
|
||||
private final ReceiverValue from;
|
||||
private final KtExpression to;
|
||||
|
||||
private ForInRangeLiteralLoopGenerator(
|
||||
@NotNull KtForExpression forExpression,
|
||||
@NotNull RangeCodegenUtil.BinaryCall rangeCall
|
||||
@NotNull ReceiverValue from,
|
||||
@NotNull KtExpression to
|
||||
) {
|
||||
super(forExpression);
|
||||
this.rangeCall = rangeCall;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storeRangeStartAndEnd() {
|
||||
gen(rangeCall.left, loopParameterType);
|
||||
v.store(loopParameterVar, loopParameterType);
|
||||
|
||||
gen(rangeCall.right, asmElementType);
|
||||
v.store(endVar, asmElementType);
|
||||
loopParameter().store(generateReceiverValue(from, false), v);
|
||||
StackValue.local(endVar, asmElementType).store(gen(to), v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,7 +1125,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@Override
|
||||
protected void storeRangeStartAndEnd() {
|
||||
StackValue.local(loopParameterVar, loopParameterType).store(StackValue.constant(0, asmElementType), v);
|
||||
loopParameter().store(StackValue.constant(0, asmElementType), v);
|
||||
|
||||
StackValue receiver = generateReceiverValue(receiverValue, false);
|
||||
receiver.put(receiver.type, v);
|
||||
|
||||
@@ -70,36 +70,6 @@ public class RangeCodegenUtil {
|
||||
return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BinaryCall getRangeAsBinaryCall(@NotNull KtForExpression forExpression) {
|
||||
// We are looking for rangeTo() calls
|
||||
// Other binary operations will succeed too, but will be filtered out later (by examining a resolvedCall)
|
||||
KtExpression rangeExpression = forExpression.getLoopRange();
|
||||
assert rangeExpression != null;
|
||||
KtExpression loopRange = KtPsiUtil.deparenthesize(rangeExpression);
|
||||
if (loopRange instanceof KtQualifiedExpression) {
|
||||
// a.rangeTo(b)
|
||||
KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange;
|
||||
KtExpression selector = qualifiedExpression.getSelectorExpression();
|
||||
if (selector instanceof KtCallExpression) {
|
||||
KtCallExpression callExpression = (KtCallExpression) selector;
|
||||
List<? extends ValueArgument> arguments = callExpression.getValueArguments();
|
||||
if (arguments.size() == 1) {
|
||||
return new BinaryCall(qualifiedExpression.getReceiverExpression(), callExpression.getCalleeExpression(),
|
||||
arguments.get(0).getArgumentExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (loopRange instanceof KtBinaryExpression) {
|
||||
// a rangeTo b
|
||||
// a .. b
|
||||
KtBinaryExpression binaryExpression = (KtBinaryExpression) loopRange;
|
||||
return new BinaryCall(binaryExpression.getLeft(), binaryExpression.getOperationReference(), binaryExpression.getRight());
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ResolvedCall<? extends CallableDescriptor> getLoopRangeResolvedCall(@NotNull KtForExpression forExpression, @NotNull BindingContext bindingContext) {
|
||||
KtExpression loopRange = KtPsiUtil.deparenthesize(forExpression.getLoopRange());
|
||||
@@ -111,9 +81,12 @@ public class RangeCodegenUtil {
|
||||
return CallUtilKt.getResolvedCall(selector, bindingContext);
|
||||
}
|
||||
}
|
||||
else if (loopRange instanceof KtSimpleNameExpression) {
|
||||
else if (loopRange instanceof KtSimpleNameExpression || loopRange instanceof KtCallExpression) {
|
||||
return CallUtilKt.getResolvedCall(loopRange, bindingContext);
|
||||
}
|
||||
else if (loopRange instanceof KtBinaryExpression) {
|
||||
return CallUtilKt.getResolvedCall(((KtBinaryExpression) loopRange).getOperationReference(), bindingContext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -191,16 +164,4 @@ public class RangeCodegenUtil {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class BinaryCall {
|
||||
public final KtExpression left;
|
||||
public final KtExpression op;
|
||||
public final KtExpression right;
|
||||
|
||||
private BinaryCall(KtExpression left, KtExpression op, KtExpression right) {
|
||||
this.left = left;
|
||||
this.op = op;
|
||||
this.right = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
fun Int.digitsUpto(end: Int): Int {
|
||||
var sum = 0
|
||||
for (i in rangeTo(end)) {
|
||||
sum = sum*10 + i
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(1234, 1.digitsUpto(4))
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
fun suppressBoxingOptimization(ni: Int?) {}
|
||||
|
||||
fun Int.digitsUpto(end: Int): Int {
|
||||
var sum = 0
|
||||
for (i: Int? in rangeTo(end)) {
|
||||
suppressBoxingOptimization(i)
|
||||
sum = sum*10 + i!!
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(1234, 1.digitsUpto(4))
|
||||
return "OK"
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fun Int.digitsUpto(end: Int): Int {
|
||||
var sum = 0
|
||||
for (i in rangeTo(end)) {
|
||||
sum = sum*10 + i
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// 0 iterator
|
||||
// 0 getStart
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
@@ -10486,12 +10486,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("forInRangeWithImplicitReceiver.kt")
|
||||
public void testForInRangeWithImplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("forIntRange.kt")
|
||||
public void testForIntRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forIntRange.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("forNullableIntInRangeWithImplicitReceiver.kt")
|
||||
public void testForNullableIntInRangeWithImplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forNullableIntInRangeWithImplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multiAssignmentIterationOverIntRange.kt")
|
||||
public void testMultiAssignmentIterationOverIntRange() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/multiAssignmentIterationOverIntRange.kt");
|
||||
|
||||
@@ -853,6 +853,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("forInRangeWithImplicitReceiver.kt")
|
||||
public void testForInRangeWithImplicitReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("loopVarInterval.kt")
|
||||
public void testLoopVarInterval() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt");
|
||||
|
||||
Reference in New Issue
Block a user