isl_ast_build_expr.c: isl_ast_expr_add_term: simplify sign changing code
[platform/upstream/isl.git] / isl_ast_build_expr.c
index 86a9e6e..97ec5d2 100644 (file)
@@ -31,8 +31,8 @@ static __isl_give isl_aff *oppose_div_arg(__isl_take isl_aff *aff, isl_int d)
 /* Create an isl_ast_expr evaluating the div at position "pos" in "ls".
  * The result is simplified in terms of build->domain.
  *
- * "v" points to the coefficient of the div in the expression where it
- * appears and may be updated by this function.
+ * *change_sign is set by this function if the sign of
+ * the expression has changed.
  * "ls" is known to be non-NULL.
  *
  * Let the div be of the form floor(e/d).
@@ -53,7 +53,7 @@ static __isl_give isl_aff *oppose_div_arg(__isl_take isl_aff *aff, isl_int d)
  *
  * and still use pdiv_q.
  */
-static __isl_give isl_ast_expr *var_div(isl_int *v,
+static __isl_give isl_ast_expr *var_div(int *change_sign,
        __isl_keep isl_local_space *ls,
        int pos, __isl_keep isl_ast_build *build)
 {
@@ -76,7 +76,7 @@ static __isl_give isl_ast_expr *var_div(isl_int *v,
                        isl_aff *opp = oppose_div_arg(isl_aff_copy(aff), d);
                        non_neg = isl_ast_build_aff_is_nonneg(build, opp);
                        if (non_neg >= 0 && non_neg) {
-                               isl_int_neg(*v, *v);
+                               *change_sign = 1;
                                isl_aff_free(aff);
                                aff = opp;
                        } else
@@ -96,22 +96,23 @@ static __isl_give isl_ast_expr *var_div(isl_int *v,
 /* Create an isl_ast_expr evaluating the specified dimension of "ls".
  * The result is simplified in terms of build->domain.
  *
- * "v" points to the coefficient of the specified dimension
- * in the expression where it appears and may be updated by this function.
+ * *change_sign is set by this function if the sign of
+ * the expression has changed.
  *
  * The isl_ast_expr is constructed based on the type of the dimension.
  * - divs are constructed by var_div
  * - set variables are constructed from the iterator isl_ids in "build"
  * - parameters are constructed from the isl_ids in "ls"
  */
-static __isl_give isl_ast_expr *var(isl_int *v, __isl_keep isl_local_space *ls,
+static __isl_give isl_ast_expr *var(int *change_sign,
+       __isl_keep isl_local_space *ls,
        enum isl_dim_type type, int pos, __isl_keep isl_ast_build *build)
 {
        isl_ctx *ctx = isl_local_space_get_ctx(ls);
        isl_id *id;
 
        if (type == isl_dim_div)
-               return var_div(v, ls, pos, build);
+               return var_div(change_sign, ls, pos, build);
 
        if (type == isl_dim_set) {
                id = isl_ast_build_get_iterator_id(build, pos);
@@ -133,7 +134,7 @@ static int ast_expr_is_zero(__isl_keep isl_ast_expr *expr)
                return -1;
        if (expr->type != isl_ast_expr_int)
                return 0;
-       return isl_int_is_zero(expr->u.i);
+       return isl_val_is_zero(expr->u.v);
 }
 
 /* Create an expression representing the sum of "expr1" and "expr2",
@@ -282,21 +283,25 @@ static __isl_give isl_ast_expr *scale(__isl_take isl_ast_expr *expr, isl_int v)
 static __isl_give isl_ast_expr *isl_ast_expr_add_term(
        __isl_take isl_ast_expr *expr,
        __isl_keep isl_local_space *ls, enum isl_dim_type type, int pos,
-       isl_int *v, __isl_keep isl_ast_build *build)
+       isl_int v, __isl_keep isl_ast_build *build)
 {
        isl_ast_expr *term;
+       int change_sign;
 
        if (!expr)
                return NULL;
 
-       term = var(v, ls, type, pos, build);
+       change_sign = 0;
+       term = var(&change_sign, ls, type, pos, build);
+       if (change_sign)
+               isl_int_neg(v, v);
 
-       if (isl_int_is_neg(*v) && !ast_expr_is_zero(expr)) {
-               isl_int_neg(*v, *v);
-               term = scale(term, *v);
+       if (isl_int_is_neg(v) && !ast_expr_is_zero(expr)) {
+               isl_int_neg(v, v);
+               term = scale(term, v);
                return ast_expr_sub(expr, term);
        } else {
-               term = scale(term, *v);
+               term = scale(term, v);
                return ast_expr_add(expr, term);
        }
 }
@@ -510,7 +515,7 @@ __isl_give isl_ast_expr *isl_ast_expr_from_aff(__isl_take isl_aff *aff,
                        if (isl_int_is_zero(v))
                                continue;
                        expr = isl_ast_expr_add_term(expr,
-                                                       ls, l[i], j, &v, build);
+                                                       ls, l[i], j, v, build);
                }
        }
 
@@ -554,22 +559,35 @@ static __isl_give isl_ast_expr *add_signed_terms(__isl_take isl_ast_expr *expr,
                                continue;
                        isl_int_abs(v, v);
                        expr = isl_ast_expr_add_term(expr,
-                                               ls, l[i], j, &v, build);
+                                               ls, l[i], j, v, build);
                }
        }
 
-       isl_aff_get_constant(aff, &v);
-       if (sign * isl_int_sgn(v) > 0) {
-               isl_int_abs(v, v);
-               expr = isl_ast_expr_add_int(expr, v);
-       }
-
        isl_local_space_free(ls);
        isl_int_clear(v);
 
        return expr;
 }
 
+/* Should the constant term "v" be considered positive?
+ *
+ * A positive constant will be added to "pos" by the caller,
+ * while a negative constant will be added to "neg".
+ * If either "pos" or "neg" is exactly zero, then we prefer
+ * to add the constant "v" to that side, irrespective of the sign of "v".
+ * This results in slightly shorter expressions and may reduce the risk
+ * of overflows.
+ */
+static int constant_is_considered_positive(isl_int v,
+       __isl_keep isl_ast_expr *pos, __isl_keep isl_ast_expr *neg)
+{
+       if (ast_expr_is_zero(pos))
+               return 1;
+       if (ast_expr_is_zero(neg))
+               return 0;
+       return isl_int_is_pos(v);
+}
+
 /* Construct an isl_ast_expr that evaluates the condition "constraint",
  * The result is simplified in terms of build->domain.
  *
@@ -589,6 +607,10 @@ static __isl_give isl_ast_expr *add_signed_terms(__isl_take isl_ast_expr *expr,
  * However, if the first expression is an integer constant (and the second
  * is not), then we swap the two expressions.  This ensures that we construct,
  * e.g., "i <= 5" rather than "5 >= i".
+ *
+ * Furthermore, is there are no terms with positive coefficients (or no terms
+ * with negative coefficients), then the constant term is added to "pos"
+ * (or "neg"), ignoring the sign of the constant term.
  */
 static __isl_give isl_ast_expr *isl_ast_expr_from_constraint(
        __isl_take isl_constraint *constraint, __isl_keep isl_ast_build *build)
@@ -598,6 +620,7 @@ static __isl_give isl_ast_expr *isl_ast_expr_from_constraint(
        isl_ast_expr *expr_neg;
        isl_ast_expr *expr;
        isl_aff *aff;
+       isl_int v;
        int eq;
        enum isl_ast_op_type type;
 
@@ -615,6 +638,16 @@ static __isl_give isl_ast_expr *isl_ast_expr_from_constraint(
        expr_pos = add_signed_terms(expr_pos, aff, 1, build);
        expr_neg = add_signed_terms(expr_neg, aff, -1, build);
 
+       isl_int_init(v);
+       isl_aff_get_constant(aff, &v);
+       if (constant_is_considered_positive(v, expr_pos, expr_neg)) {
+               expr_pos = isl_ast_expr_add_int(expr_pos, v);
+       } else {
+               isl_int_neg(v, v);
+               expr_neg = isl_ast_expr_add_int(expr_neg, v);
+       }
+       isl_int_clear(v);
+
        eq = isl_constraint_is_equality(constraint);
 
        if (isl_ast_expr_get_type(expr_pos) == isl_ast_expr_int &&