From d85ca4718488322843b6c85f1fc76352ffdea466 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 20 Jun 2013 18:06:55 +0400 Subject: [PATCH] Generate optimized bytecode for 'for' in progression Instead of checking for overflow at the end of each iteration, calculate the actual final loop parameter value before the loop, save it to the local variable and at the end of the iteration check if the loop parameter is exactly equal to this final value. ProgressionUtil.getProgressionFinalElement() credits go to @geevee --- .../jet/codegen/ExpressionCodegen.java | 350 +++++++++--------- runtime/src/jet/runtime/ProgressionUtil.java | 83 +++++ 2 files changed, 252 insertions(+), 181 deletions(-) create mode 100644 runtime/src/jet/runtime/ProgressionUtil.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 97e43c748eb..e62e277244f 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -818,65 +818,28 @@ public class ExpressionCodegen extends JetVisitor implem } } - private abstract class AbstractForInRangeLoopGenerator extends AbstractForLoopGenerator { + private abstract class AbstractForInProgressionOrRangeLoopGenerator extends AbstractForLoopGenerator { protected int endVar; - // True iff instead of comparing loopParameterVar < endVar at the beginning of an iteration we check whether - // loopParameterVar == endVar at the end of the iteration (and also if there should be any iterations at all, before the loop) - private final boolean checkEqualityInsteadOfComparison; + // For integer progressions instead of comparing loopParameterVar with endVar at the beginning of an iteration we check whether + // loopParameterVar == finalVar at the end of the iteration (and also if there should be any iterations at all, before the loop) + protected final boolean isIntegerProgression; - private AbstractForInRangeLoopGenerator(@NotNull JetForExpression forExpression) { + private AbstractForInProgressionOrRangeLoopGenerator(@NotNull JetForExpression forExpression) { super(forExpression); - checkEqualityInsteadOfComparison = asmElementType.getSort() == Type.INT || asmElementType.getSort() == Type.LONG; - } - @Override - public void beforeLoop() { - super.beforeLoop(); - - endVar = createLoopTempVariable(asmElementType); - storeRangeStartAndEnd(); - } - - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { - if (!checkEqualityInsteadOfComparison) return; - - v.load(loopParameterVar, asmElementType); - v.load(endVar, asmElementType); - if (asmElementType.getSort() == Type.INT) { - v.ificmpgt(loopExit); - } - else if (asmElementType.getSort() == Type.LONG) { - v.lcmp(); - v.ifgt(loopExit); - } - else { - throw new IllegalStateException("Unexpected type for empty loop checking: " + asmElementType); - } - } - - protected abstract void storeRangeStartAndEnd(); - - @Override - public void conditionAndJump(@NotNull Label loopExit) { - if (checkEqualityInsteadOfComparison) return; - - v.load(loopParameterVar, asmElementType); - v.load(endVar, asmElementType); - - int sort = asmElementType.getSort(); - switch (sort) { - case Type.CHAR: + switch (asmElementType.getSort()) { + case Type.INT: case Type.BYTE: case Type.SHORT: - v.ificmpgt(loopExit); + case Type.CHAR: + case Type.LONG: + isIntegerProgression = true; break; - case Type.FLOAT: case Type.DOUBLE: - v.cmpg(asmElementType); - v.ifgt(loopExit); + case Type.FLOAT: + isIntegerProgression = false; break; default: @@ -885,27 +848,85 @@ public class ExpressionCodegen extends JetVisitor implem } @Override - protected void assignToLoopParameter() { + public void beforeLoop() { + super.beforeLoop(); + + endVar = createLoopTempVariable(asmElementType); } - private void checkPostCondition(@NotNull Label loopExit) { + // The local variable holding the actual last value of the loop parameter. + // For ranges it equals end, for progressions it's a function of start, end and increment + protected abstract int getFinalVar(); + + protected void checkPostCondition(@NotNull Label loopExit) { + int finalVar = getFinalVar(); + assert isIntegerProgression && finalVar != -1 : + "Post-condition should be checked only in case of integer progressions, finalVar = " + finalVar; + v.load(loopParameterVar, asmElementType); - v.load(endVar, asmElementType); - if (asmElementType.getSort() == Type.INT) { - v.ificmpeq(loopExit); - } - else if (asmElementType.getSort() == Type.LONG) { + v.load(finalVar, asmElementType); + if (asmElementType.getSort() == Type.LONG) { v.lcmp(); v.ifeq(loopExit); } else { - throw new IllegalStateException("Unexpected type for equality: " + asmElementType); + v.ificmpeq(loopExit); + } + } + } + + private abstract class AbstractForInRangeLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator { + private AbstractForInRangeLoopGenerator(@NotNull JetForExpression forExpression) { + super(forExpression); + } + + @Override + public void beforeLoop() { + super.beforeLoop(); + + storeRangeStartAndEnd(); + } + + protected abstract void storeRangeStartAndEnd(); + + @Override + protected int getFinalVar() { + return endVar; + } + + @Override + public void conditionAndJump(@NotNull Label loopExit) { + if (isIntegerProgression) return; + + v.load(loopParameterVar, asmElementType); + v.load(endVar, asmElementType); + + v.cmpg(asmElementType); + v.ifgt(loopExit); + } + + @Override + public void checkEmptyLoop(@NotNull Label loopExit) { + if (!isIntegerProgression) return; + + v.load(loopParameterVar, asmElementType); + v.load(endVar, asmElementType); + if (asmElementType.getSort() == Type.LONG) { + v.lcmp(); + v.ifgt(loopExit); + } + else { + v.ificmpgt(loopExit); } } + @Override + protected void assignToLoopParameter() { + } + @Override protected void increment(@NotNull Label loopExit) { - if (checkEqualityInsteadOfComparison) { + if (isIntegerProgression) { checkPostCondition(loopExit); } @@ -983,20 +1004,25 @@ public class ExpressionCodegen extends JetVisitor implem } } - private class ForInProgressionExpressionLoopGenerator extends AbstractForLoopGenerator { - private int endVar; + private class ForInProgressionExpressionLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator { private int incrementVar; private Type incrementType; + private int finalVar; + private ForInProgressionExpressionLoopGenerator(@NotNull JetForExpression forExpression) { super(forExpression); } + @Override + protected int getFinalVar() { + return finalVar; + } + @Override public void beforeLoop() { super.beforeLoop(); - endVar = createLoopTempVariable(asmElementType); incrementVar = createLoopTempVariable(asmElementType); JetType loopRangeType = bindingContext.get(EXPRESSION_TYPE, forExpression.getLoopRange()); @@ -1014,87 +1040,99 @@ public class ExpressionCodegen extends JetVisitor implem generateRangeOrProgressionProperty(asmLoopRangeType, "getStart", asmElementType, loopParameterVar); generateRangeOrProgressionProperty(asmLoopRangeType, "getEnd", asmElementType, endVar); generateRangeOrProgressionProperty(asmLoopRangeType, "getIncrement", incrementType, incrementVar); + + storeFinalVar(); } - @Override - public void checkEmptyLoop(@NotNull Label loopExit) { + private void storeFinalVar() { + if (!isIntegerProgression) { + finalVar = -1; + return; + } + + v.load(loopParameterVar, asmElementType); + v.load(endVar, asmElementType); + v.load(incrementVar, incrementType); + + Type methodParamType = asmElementType.getSort() == Type.LONG ? Type.LONG_TYPE : Type.INT_TYPE; + v.invokestatic("jet/runtime/ProgressionUtil", "getProgressionFinalElement", + Type.getMethodDescriptor(methodParamType, methodParamType, methodParamType, methodParamType)); + + finalVar = createLoopTempVariable(asmElementType); + v.store(finalVar, asmElementType); } @Override public void conditionAndJump(@NotNull Label loopExit) { + if (isIntegerProgression) return; + v.load(loopParameterVar, asmElementType); v.load(endVar, asmElementType); - v.load(incrementVar, asmElementType); Label negativeIncrement = new Label(); Label afterIf = new Label(); - int sort = asmElementType.getSort(); - switch (sort) { - case Type.INT: - case Type.CHAR: - case Type.BYTE: - case Type.SHORT: - v.ifle(negativeIncrement); // if increment < 0, jump - - // increment > 0 - v.ificmpgt(loopExit); - v.goTo(afterIf); - - // increment < 0 - v.visitLabel(negativeIncrement); - v.ificmplt(loopExit); - v.visitLabel(afterIf); - - break; - - case Type.LONG: - v.lconst(0L); - v.lcmp(); - v.ifle(negativeIncrement); // if increment < 0, jump - - // increment > 0 - v.lcmp(); - v.ifgt(loopExit); - v.goTo(afterIf); - - // increment < 0 - v.visitLabel(negativeIncrement); - v.lcmp(); - v.iflt(loopExit); - v.visitLabel(afterIf); - - break; - - case Type.DOUBLE: - case Type.FLOAT: - if (sort == Type.DOUBLE) { - v.dconst(0.0); - } - else { - v.fconst(0.0f); - } - v.cmpl(incrementType); - v.ifle(negativeIncrement); // if increment < 0, jump - - // increment > 0 - v.cmpg(asmElementType); // if loop parameter is NaN, exit from loop, as well - v.ifgt(loopExit); - v.goTo(afterIf); - - // increment < 0 - v.visitLabel(negativeIncrement); - v.cmpl(asmElementType); // if loop parameter is NaN, exit from loop, as well - v.iflt(loopExit); - v.visitLabel(afterIf); - - break; - - default: - throw new IllegalStateException("Unexpected range element type: " + asmElementType); + if (asmElementType.getSort() == Type.DOUBLE) { + v.dconst(0.0); } + else { + v.fconst(0.0f); + } + v.cmpl(incrementType); + v.ifle(negativeIncrement); // if increment < 0, jump + // increment > 0 + v.cmpg(asmElementType); // if loop parameter is NaN, exit from loop, as well + v.ifgt(loopExit); + v.goTo(afterIf); + + // increment < 0 + v.mark(negativeIncrement); + v.cmpl(asmElementType); // if loop parameter is NaN, exit from loop, as well + v.iflt(loopExit); + v.mark(afterIf); + } + + @Override + public void checkEmptyLoop(@NotNull Label loopExit) { + if (!isIntegerProgression) return; + + v.load(loopParameterVar, asmElementType); + v.load(endVar, asmElementType); + v.load(incrementVar, asmElementType); + + Label negativeIncrement = new Label(); + Label afterIf = new Label(); + + if (asmElementType.getSort() == Type.LONG) { + v.lconst(0L); + v.lcmp(); + v.ifle(negativeIncrement); // if increment < 0, jump + + // increment > 0 + v.lcmp(); + v.ifgt(loopExit); + v.goTo(afterIf); + + // increment < 0 + v.mark(negativeIncrement); + v.lcmp(); + v.iflt(loopExit); + v.mark(afterIf); + } + else { + v.ifle(negativeIncrement); // if increment < 0, jump + + // increment > 0 + v.ificmpgt(loopExit); + v.goTo(afterIf); + + // increment < 0 + v.mark(negativeIncrement); + v.ificmplt(loopExit); + v.mark(afterIf); + } } @Override @@ -1103,66 +1141,16 @@ public class ExpressionCodegen extends JetVisitor implem @Override protected void increment(@NotNull Label loopExit) { + if (isIntegerProgression) { + checkPostCondition(loopExit); + } + v.load(loopParameterVar, asmElementType); v.load(incrementVar, asmElementType); v.add(asmElementType); - int sort = asmElementType.getSort(); - if (sort == Type.INT || sort == Type.LONG) { - checkNewLoopParameterValue(loopExit); - } - v.store(loopParameterVar, asmElementType); } - - // Checks that (increment > 0) == (new value of loop parameter > old value of loop parameter). - // Old value should be stored in loopParameterVar, new value should be on top of the stack - private void checkNewLoopParameterValue(@NotNull Label loopExit) { - Label negativeIncrement = new Label(); - Label afterIf = new Label(); - Label popAndExit = new Label(); - - dup(v, asmElementType); - v.load(loopParameterVar, asmElementType); - - v.load(incrementVar, asmElementType); - - if (asmElementType.getSort() == Type.LONG) { - v.lconst(0L); - v.lcmp(); - v.ifle(negativeIncrement); - - // increment > 0 - v.lcmp(); - v.iflt(popAndExit); - v.goTo(afterIf); - - // increment < 0 - v.mark(negativeIncrement); - v.lcmp(); - v.ifgt(popAndExit); - v.goTo(afterIf); - } - else { - v.ifle(negativeIncrement); - - // increment > 0 - v.ificmplt(popAndExit); - v.goTo(afterIf); - - // increment < 0 - v.mark(negativeIncrement); - v.ificmpgt(popAndExit); - v.goTo(afterIf); - } - - // Pop the new value of loop parameter from the stack and exit the loop - v.mark(popAndExit); - pop(v, asmElementType); - v.goTo(loopExit); - - v.mark(afterIf); - } } diff --git a/runtime/src/jet/runtime/ProgressionUtil.java b/runtime/src/jet/runtime/ProgressionUtil.java new file mode 100644 index 00000000000..a03226a82ce --- /dev/null +++ b/runtime/src/jet/runtime/ProgressionUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jet.runtime; + +public class ProgressionUtil { + private ProgressionUtil() { + } + + // a mod b (in arithmetical sense) + private static int mod(int a, int b) { + return a >= 0 ? a % b : a % b + b; + } + + private static long mod(long a, long b) { + return a >= 0 ? a % b : a % b + b; + } + + // (a - b) mod c + private static int differenceModulo(int a, int b, int c) { + return mod(mod(a, c) - mod(b, c), c); + } + + private static long differenceModulo(long a, long b, long c) { + return mod(mod(a, c) - mod(b, c), c); + } + + + /** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from {@code start} to {@code end} in case of a positive {@code increment}, or from {@code end} to {@code start} in case of a negative + * increment. + * + *

No validation on passed parameters is performed. The given parameters should satisfy the condition: either + * {@code increment > 0} and {@code start <= end}, or {@code increment < 0} and {@code start >= end}. + * @param start first element of the progression + * @param end ending bound for the progression + * @param increment increment, or difference of successive elements in the progression + * @return the final element of the progression + */ + public static int getProgressionFinalElement(int start, int end, int increment) { + if (increment > 0) { + return end - differenceModulo(end, start, increment); + } + else { + return end + differenceModulo(start, end, -increment); + } + } + + /** + * Calculates the final element of a bounded arithmetic progression, i.e. the last element of the progression which is in the range + * from {@code start} to {@code end} in case of a positive {@code increment}, or from {@code end} to {@code start} in case of a negative + * increment. + * + *

No validation on passed parameters is performed. The given parameters should satisfy the condition: either + * {@code increment > 0} and {@code start <= end}, or {@code increment < 0} and {@code start >= end}. + * @param start first element of the progression + * @param end ending bound for the progression + * @param increment increment, or difference of successive elements in the progression + * @return the final element of the progression + */ + public static long getProgressionFinalElement(long start, long end, long increment) { + if (increment > 0) { + return end - differenceModulo(end, start, increment); + } + else { + return end + differenceModulo(start, end, -increment); + } + } +}