KT-6916: do not create Progression instances in for-in-downTo loops
This commit is contained in:
@@ -635,12 +635,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) {
|
||||||
ReceiverValue from = loopRangeCall.getDispatchReceiver();
|
ReceiverValue from = loopRangeCall.getDispatchReceiver();
|
||||||
assert from != null : "Dispatch receiver should be non-null for optimizable 'rangeTo' call";
|
assert from != null : "Dispatch receiver should be non-null for optimizable 'rangeTo' call";
|
||||||
List<ResolvedValueArgument> valueArgumentsByIndex = loopRangeCall.getValueArgumentsByIndex();
|
KtExpression to = getSingleArgumentExpression(loopRangeCall);
|
||||||
assert valueArgumentsByIndex != null : "Value arguments should be non-null for optimizable 'rangeTo' call";
|
assert to != null : "Optimizable 'rangeTo' call should have a single expression argument";
|
||||||
KtExpression to = valueArgumentsByIndex.get(0).getArguments().get(0).getArgumentExpression();
|
|
||||||
assert to != null : "1st value argument should be non-null for optimizable 'rangeTo' call";
|
|
||||||
return new ForInRangeLiteralLoopGenerator(forExpression, from, to);
|
return new ForInRangeLiteralLoopGenerator(forExpression, from, to);
|
||||||
}
|
}
|
||||||
|
else if (RangeCodegenUtil.isOptimizableDownTo(loopRangeCallee)) {
|
||||||
|
ReceiverValue from = loopRangeCall.getExtensionReceiver();
|
||||||
|
assert from != null : "Extension receiver should be non-null for optimizable 'downTo' call";
|
||||||
|
KtExpression to = getSingleArgumentExpression(loopRangeCall);
|
||||||
|
assert to != null : "Optimizable 'downTo' call should have a single expression argument";
|
||||||
|
return new ForInDownToProgressionLoopGenerator(forExpression, from, to);
|
||||||
|
}
|
||||||
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) {
|
||||||
ReceiverValue extensionReceiver = loopRangeCall.getExtensionReceiver();
|
ReceiverValue extensionReceiver = loopRangeCall.getExtensionReceiver();
|
||||||
assert extensionReceiver != null : "Extension receiver should be non-null for optimizable 'Array.indices' call";
|
assert extensionReceiver != null : "Extension receiver should be non-null for optimizable 'Array.indices' call";
|
||||||
@@ -655,6 +660,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static KtExpression getSingleArgumentExpression(@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall) {
|
||||||
|
List<ResolvedValueArgument> resolvedValueArguments = resolvedCall.getValueArgumentsByIndex();
|
||||||
|
if (resolvedValueArguments == null) return null;
|
||||||
|
if (resolvedValueArguments.size() != 1) return null;
|
||||||
|
List<ValueArgument> valueArguments = resolvedValueArguments.get(0).getArguments();
|
||||||
|
if (valueArguments.size() != 1) return null;
|
||||||
|
return valueArguments.get(0).getArgumentExpression();
|
||||||
|
}
|
||||||
|
|
||||||
private OwnerKind contextKind() {
|
private OwnerKind contextKind() {
|
||||||
return context.getContextKind();
|
return context.getContextKind();
|
||||||
}
|
}
|
||||||
@@ -1044,8 +1059,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract class AbstractForInRangeLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator {
|
private abstract class AbstractForInRangeLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator {
|
||||||
private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression) {
|
private final int step;
|
||||||
|
|
||||||
|
private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression, int step) {
|
||||||
super(forExpression);
|
super(forExpression);
|
||||||
|
this.step = step;
|
||||||
|
assert step == 1 || step == -1 : "'step' should be either 1 or -1: " + step;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractForInRangeLoopGenerator(@NotNull KtForExpression forExpression) {
|
||||||
|
this(forExpression, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1063,10 +1086,20 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
v.load(endVar, asmElementType);
|
v.load(endVar, asmElementType);
|
||||||
if (asmElementType.getSort() == Type.LONG) {
|
if (asmElementType.getSort() == Type.LONG) {
|
||||||
v.lcmp();
|
v.lcmp();
|
||||||
v.ifgt(loopExit);
|
if (step > 0) {
|
||||||
|
v.ifgt(loopExit);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v.iflt(loopExit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
v.ificmpgt(loopExit);
|
if (step > 0) {
|
||||||
|
v.ificmpgt(loopExit);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v.ificmplt(loopExit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1079,12 +1112,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
checkPostCondition(loopExit);
|
checkPostCondition(loopExit);
|
||||||
|
|
||||||
if (loopParameterType == Type.INT_TYPE) {
|
if (loopParameterType == Type.INT_TYPE) {
|
||||||
v.iinc(loopParameterVar, 1);
|
v.iinc(loopParameterVar, step);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
StackValue loopParameter = loopParameter();
|
StackValue loopParameter = loopParameter();
|
||||||
loopParameter.put(asmElementType, v);
|
loopParameter.put(asmElementType, v);
|
||||||
genIncrement(asmElementType, 1, v);
|
genIncrement(asmElementType, step, v);
|
||||||
loopParameter.store(StackValue.onStack(asmElementType), v);
|
loopParameter.store(StackValue.onStack(asmElementType), v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1111,6 +1144,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ForInDownToProgressionLoopGenerator extends AbstractForInRangeLoopGenerator {
|
||||||
|
private final ReceiverValue from;
|
||||||
|
private final KtExpression to;
|
||||||
|
|
||||||
|
private ForInDownToProgressionLoopGenerator(
|
||||||
|
@NotNull KtForExpression forExpression,
|
||||||
|
@NotNull ReceiverValue from,
|
||||||
|
@NotNull KtExpression to
|
||||||
|
) {
|
||||||
|
super(forExpression, -1);
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void storeRangeStartAndEnd() {
|
||||||
|
loopParameter().store(generateReceiverValue(from, false), v);
|
||||||
|
StackValue.local(endVar, asmElementType).store(gen(to), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ForInRangeInstanceLoopGenerator extends AbstractForInRangeLoopGenerator {
|
private class ForInRangeInstanceLoopGenerator extends AbstractForInRangeLoopGenerator {
|
||||||
private ForInRangeInstanceLoopGenerator(@NotNull KtForExpression forExpression) {
|
private ForInRangeInstanceLoopGenerator(@NotNull KtForExpression forExpression) {
|
||||||
super(forExpression);
|
super(forExpression);
|
||||||
|
|||||||
@@ -132,6 +132,17 @@ public class RangeCodegenUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isOptimizableDownTo(@NotNull CallableDescriptor descriptor) {
|
||||||
|
if (!isTopLevelInPackage(descriptor, "downTo", "kotlin.ranges")) return false;
|
||||||
|
|
||||||
|
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||||
|
if (extensionReceiver == null) return false;
|
||||||
|
ClassifierDescriptor extensionReceiverClassifier = extensionReceiver.getType().getConstructor().getDeclarationDescriptor();
|
||||||
|
if (!isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isArrayOrPrimitiveArrayIndices(@NotNull CallableDescriptor descriptor) {
|
public static boolean isArrayOrPrimitiveArrayIndices(@NotNull CallableDescriptor descriptor) {
|
||||||
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
|
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var sum = 0
|
||||||
|
for (i in 4 downTo 1) {
|
||||||
|
sum = sum * 10 + i
|
||||||
|
}
|
||||||
|
assertEquals(4321, sum)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var sum = 0
|
||||||
|
val dt = 4 downTo 1
|
||||||
|
for (i in dt) {
|
||||||
|
sum = sum * 10 + i
|
||||||
|
}
|
||||||
|
assertEquals(4321, sum)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var sum = 0L
|
||||||
|
for (i in 4L downTo 1L) {
|
||||||
|
sum = sum * 10L + i
|
||||||
|
}
|
||||||
|
assertEquals(4321L, sum)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var sum = 0
|
||||||
|
for (i: Int? in 4 downTo 1) {
|
||||||
|
sum = sum * 10 + (i ?: 0)
|
||||||
|
}
|
||||||
|
assertEquals(4321, sum)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun test(): Int {
|
||||||
|
var sum = 0
|
||||||
|
for (i in 4 downTo 1) {
|
||||||
|
sum = sum * 10 + i
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 iterator
|
||||||
|
// 0 getStart
|
||||||
|
// 0 getEnd
|
||||||
|
// 0 getFirst
|
||||||
|
// 0 getLast
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
fun f() {
|
fun f() {
|
||||||
for (i in 0..5 step 2) {
|
for (i in 0..5 step 2) {
|
||||||
}
|
}
|
||||||
for (i in 5 downTo 1) {
|
|
||||||
|
for (i in 5 downTo 1 step 1) { // suppress optimized code generation for 'for-in-downTo'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10801,6 +10801,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInDownTo")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ForInDownTo extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInForInDownTo() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInDownTo"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forIntInDownTo.kt")
|
||||||
|
public void testForIntInDownTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forIntInNonOptimizedDownTo.kt")
|
||||||
|
public void testForIntInNonOptimizedDownTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forIntInNonOptimizedDownTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forLongInDownTo.kt")
|
||||||
|
public void testForLongInDownTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forLongInDownTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forNullableIntInDownTo.kt")
|
||||||
|
public void testForNullableIntInDownTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInDownTo/forNullableIntInDownTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/ranges/forInIndices")
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInIndices")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -859,6 +859,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forIntInDownTo.kt")
|
||||||
|
public void testForIntInDownTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("loopVarInterval.kt")
|
@TestMetadata("loopVarInterval.kt")
|
||||||
public void testLoopVarInterval() throws Exception {
|
public void testLoopVarInterval() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user