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:
Dmitry Petrov
2016-06-16 09:42:12 +03:00
parent 5e21af7bdd
commit 9240c82934
7 changed files with 95 additions and 65 deletions
@@ -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"
}
@@ -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"
}
@@ -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");