Makefile.in (OBJS-common): Add tree-chrec.o.
authorSebastian Pop <pop@cri.ensmp.fr>
Wed, 30 Jun 2004 15:37:42 +0000 (17:37 +0200)
committerSebastian Pop <spop@gcc.gnu.org>
Wed, 30 Jun 2004 15:37:42 +0000 (15:37 +0000)
* Makefile.in (OBJS-common): Add tree-chrec.o.
(tree-chrec.o): New rule.
(GTFILES): Add tree-chrec.h.
* gengtype.c (open_base_files): Add tree-chrec.h.
* tree-chrec.c: New file.
* tree-chrec.h: New file.
* tree.def (SCEV_KNOWN, SCEV_NOT_KNOWN, POLYNOMIAL_CHREC): New nodes.

From-SVN: r83909

gcc/ChangeLog
gcc/Makefile.in
gcc/gengtype.c
gcc/tree-chrec.c [new file with mode: 0644]
gcc/tree-chrec.h [new file with mode: 0644]
gcc/tree.def

index 86bb7fa288353b880ca51f8706423e0306737c48..1a1f1f3cc06246226cbdc3c1588e30c4fbdda68d 100644 (file)
@@ -1,3 +1,13 @@
+2004-06-30  Sebastian Pop  <pop@cri.ensmp.fr>
+
+       * Makefile.in (OBJS-common): Add tree-chrec.o.
+       (tree-chrec.o): New rule.
+       (GTFILES): Add tree-chrec.h.
+       * gengtype.c (open_base_files): Add tree-chrec.h.
+       * tree-chrec.c: New file.
+       * tree-chrec.h: New file.
+       * tree.def (SCEV_KNOWN, SCEV_NOT_KNOWN, POLYNOMIAL_CHREC): New nodes.
+
 2004-06-30  Roger Sayle  <roger@eyesopen.com>
 
        * combine.c: Include "output.h" to define dump_file.
index 35dcb4fed5f11b76fc1c744e89bccb24a63523f6..183d63332eced9f761c9aabefbba322b2e5214f9 100644 (file)
@@ -885,6 +885,7 @@ C_OBJS = c-parse.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
 # Language-independent object files.
 
 OBJS-common = \
+ tree-chrec.o                                                              \
  tree-cfg.o tree-dfa.o tree-eh.o tree-ssa.o tree-optimize.o tree-gimple.o  \
  tree-alias-type.o gimplify.o tree-pretty-print.o tree-into-ssa.o          \
  tree-outof-ssa.o tree-alias-common.o tree-ssa-ccp.o tree-vn.o             \
@@ -1699,6 +1700,8 @@ gimple-low.o : gimple-low.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) errors.h \
 tree-browser.o : tree-browser.c tree-browser.def $(CONFIG_H) $(SYSTEM_H) \
    $(TREE_H) errors.h tree-inline.h diagnostic.h $(HASHTAB_H) \
    $(TM_H) coretypes.h
+tree-chrec.o: tree-chrec.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+   errors.h $(GGC_H) $(TREE_H) tree-chrec.h tree-pass.h
 tree-gimple.o : tree-gimple.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(EXPR_H) \
        $(RTL_H) $(TREE_GIMPLE_H) $(TM_H) coretypes.h bitmap.h $(GGC_H)
 tree-mudflap.o : $(CONFIG_H) errors.h $(SYSTEM_H) $(TREE_H) tree-inline.h \
@@ -2339,6 +2342,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-alias-type.h $(srcdir)/tree-alias-common.h \
   $(srcdir)/tree-alias-type.c $(srcdir)/tree-alias-common.c \
+  $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-ssa-operands.h $(srcdir)/tree-ssa-operands.c \
   $(srcdir)/tree-profile.c $(srcdir)/rtl-profile.c $(srcdir)/tree-nested.c \
   $(out_file) \
index 5d7082437b02ffb4ee5d9bafa094413a32cd3485..85e856f89fdeb51e96ce10497a264feb401f6347 100644 (file)
@@ -1110,6 +1110,7 @@ open_base_files (void)
       "libfuncs.h", "debug.h", "ggc.h", "cgraph.h",
       "tree-alias-type.h", "tree-flow.h", "reload.h",
       "cpp-id-data.h",
+      "tree-chrec.h",
       NULL
     };
     const char *const *ifp;
diff --git a/gcc/tree-chrec.c b/gcc/tree-chrec.c
new file mode 100644 (file)
index 0000000..9146ec3
--- /dev/null
@@ -0,0 +1,1019 @@
+/* Chains of recurrences.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Contributed by Sebastian Pop <s.pop@laposte.net>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+/* This file implements operations on chains of recurrences.  Chains
+   of recurrences are used for modeling evolution functions of scalar
+   variables.
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "errors.h"
+#include "ggc.h"
+#include "tree.h"
+#include "diagnostic.h"
+#include "varray.h"
+#include "tree-chrec.h"
+#include "tree-pass.h"
+
+\f
+/* This part will be removed once the merging is finished.  */
+
+
+
+/* The following trees are unique elements.  Thus the comparison of
+   another element to these elements should be done on the pointer to
+   these trees, and not on their value.  */
+
+/* The SSA_NAMEs that are not yet analyzed are qualified with NULL_TREE.  */
+tree chrec_not_analyzed_yet;
+
+/* Reserved to the cases where the analyzer has detected an
+   undecidable property at compile time.  */
+tree chrec_dont_know;
+
+/* When the analyzer has detected that a property will never
+   happen, then it qualifies it with chrec_known.  */
+tree chrec_known;
+
+/* Empty hook.  Will be replaced by the main function from
+   tree-scalar-evolution.c.  */
+
+tree
+count_ev_in_wider_type (tree foo ATTRIBUTE_UNUSED, 
+                       tree bar ATTRIBUTE_UNUSED)
+{
+  return NULL_TREE;
+}
+
+/* Empty hook.  Will be replaced by the main function from
+   tree-scalar-evolution.c.  */
+
+bool 
+chrec_contains_symbols_defined_in_loop (tree chrec ATTRIBUTE_UNUSED, 
+                                       unsigned loop_nb ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+
+\f
+
+/* Extended folder for chrecs.  */
+
+/* Determines whether CST is not a constant evolution.  */
+
+static inline bool
+is_not_constant_evolution (tree cst)
+{
+  return (TREE_CODE (cst) == POLYNOMIAL_CHREC);
+}
+
+/* Fold CODE for a polynomial function and a constant.  */
+
+static inline tree 
+chrec_fold_poly_cst (enum tree_code code, 
+                    tree type, 
+                    tree poly, 
+                    tree cst)
+{
+#if defined ENABLE_CHECKING
+  if (poly == NULL_TREE
+      || cst == NULL_TREE
+      || TREE_CODE (poly) != POLYNOMIAL_CHREC
+      || is_not_constant_evolution (cst))
+    abort ();
+#endif
+  
+  switch (code)
+    {
+    case PLUS_EXPR:
+      return build_polynomial_chrec 
+       (CHREC_VARIABLE (poly), 
+        chrec_fold_plus (type, CHREC_LEFT (poly), cst),
+        CHREC_RIGHT (poly));
+      
+    case MINUS_EXPR:
+      return build_polynomial_chrec 
+       (CHREC_VARIABLE (poly), 
+        chrec_fold_minus (type, CHREC_LEFT (poly), cst),
+        CHREC_RIGHT (poly));
+      
+    case MULT_EXPR:
+      return build_polynomial_chrec 
+       (CHREC_VARIABLE (poly), 
+        chrec_fold_multiply (type, CHREC_LEFT (poly), cst),
+        chrec_fold_multiply (type, CHREC_RIGHT (poly), cst));
+      
+    default:
+      return chrec_dont_know;
+    }
+}
+
+/* Fold the addition of two polynomial functions.  */
+
+static inline tree 
+chrec_fold_plus_poly_poly (enum tree_code code, 
+                          tree type, 
+                          tree poly0, 
+                          tree poly1)
+{
+  tree left, right;
+  
+#if defined ENABLE_CHECKING
+  if (poly0 == NULL_TREE
+      || poly1 == NULL_TREE
+      || TREE_CODE (poly0) != POLYNOMIAL_CHREC
+      || TREE_CODE (poly1) != POLYNOMIAL_CHREC)
+    abort ();
+#endif
+  
+  /*
+    {a, +, b}_1 + {c, +, d}_2  ->  {{a, +, b}_1 + c, +, d}_2,
+    {a, +, b}_2 + {c, +, d}_1  ->  {{c, +, d}_1 + a, +, b}_2,
+    {a, +, b}_x + {c, +, d}_x  ->  {a+c, +, b+d}_x.  */
+  if (CHREC_VARIABLE (poly0) < CHREC_VARIABLE (poly1))
+    {
+      if (code == PLUS_EXPR)
+       return build_polynomial_chrec 
+         (CHREC_VARIABLE (poly1), 
+          chrec_fold_plus (type, poly0, CHREC_LEFT (poly1)),
+          CHREC_RIGHT (poly1));
+      else
+       return build_polynomial_chrec 
+         (CHREC_VARIABLE (poly1), 
+          chrec_fold_minus (type, poly0, CHREC_LEFT (poly1)),
+          chrec_fold_multiply (type, CHREC_RIGHT (poly1), 
+                               convert (type, integer_minus_one_node)));
+    }
+  
+  if (CHREC_VARIABLE (poly0) > CHREC_VARIABLE (poly1))
+    {
+      if (code == PLUS_EXPR)
+       return build_polynomial_chrec 
+         (CHREC_VARIABLE (poly0), 
+          chrec_fold_plus (type, CHREC_LEFT (poly0), poly1),
+          CHREC_RIGHT (poly0));
+      else
+       return build_polynomial_chrec 
+         (CHREC_VARIABLE (poly0), 
+          chrec_fold_minus (type, CHREC_LEFT (poly0), poly1),
+          CHREC_RIGHT (poly0));
+    }
+  
+  if (code == PLUS_EXPR)
+    {
+      left = chrec_fold_plus 
+       (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1));
+      right = chrec_fold_plus 
+       (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1));
+    }
+  else
+    {
+      left = chrec_fold_minus 
+       (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1));
+      right = chrec_fold_minus 
+       (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1));
+    }
+
+  if (chrec_zerop (right))
+    return left;
+  else
+    return build_polynomial_chrec 
+      (CHREC_VARIABLE (poly0), left, right); 
+}
+
+\f
+
+/* Fold the multiplication of two polynomial functions.  */
+
+static inline tree 
+chrec_fold_multiply_poly_poly (tree type, 
+                              tree poly0, 
+                              tree poly1)
+{
+#if defined ENABLE_CHECKING
+  if (poly0 == NULL_TREE
+      || poly1 == NULL_TREE
+      || TREE_CODE (poly0) != POLYNOMIAL_CHREC
+      || TREE_CODE (poly1) != POLYNOMIAL_CHREC)
+    abort ();
+#endif
+  
+  /* {a, +, b}_1 * {c, +, d}_2  ->  {c*{a, +, b}_1, +, d}_2,
+     {a, +, b}_2 * {c, +, d}_1  ->  {a*{c, +, d}_1, +, b}_2,
+     {a, +, b}_x * {c, +, d}_x  ->  {a*c, +, a*d + b*c + b*d, +, 2*b*d}_x.  */
+  if (CHREC_VARIABLE (poly0) < CHREC_VARIABLE (poly1))
+    /* poly0 is a constant wrt. poly1.  */
+    return build_polynomial_chrec 
+      (CHREC_VARIABLE (poly1), 
+       chrec_fold_multiply (type, CHREC_LEFT (poly1), poly0),
+       CHREC_RIGHT (poly1));
+  
+  if (CHREC_VARIABLE (poly1) < CHREC_VARIABLE (poly0))
+    /* poly1 is a constant wrt. poly0.  */
+    return build_polynomial_chrec 
+      (CHREC_VARIABLE (poly0), 
+       chrec_fold_multiply (type, CHREC_LEFT (poly0), poly1),
+       CHREC_RIGHT (poly0));
+  
+  /* poly0 and poly1 are two polynomials in the same variable,
+     {a, +, b}_x * {c, +, d}_x  ->  {a*c, +, a*d + b*c + b*d, +, 2*b*d}_x.  */
+  return 
+    build_polynomial_chrec 
+    (CHREC_VARIABLE (poly0), 
+     build_polynomial_chrec 
+     (CHREC_VARIABLE (poly0), 
+      
+      /* "a*c".  */
+      chrec_fold_multiply (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1)),
+      
+      /* "a*d + b*c + b*d".  */
+      chrec_fold_plus 
+      (type, chrec_fold_multiply (type, CHREC_LEFT (poly0), CHREC_RIGHT (poly1)),
+       
+       chrec_fold_plus 
+       (type, 
+       chrec_fold_multiply (type, CHREC_RIGHT (poly0), CHREC_LEFT (poly1)),
+       chrec_fold_multiply (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1))))),
+     
+     /* "2*b*d".  */
+     chrec_fold_multiply
+     (type, build_int_2 (2, 0),
+      chrec_fold_multiply (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1))));
+}
+
+/* When the operands are automatically_generated_chrec_p, the fold has
+   to respect the semantics of the operands.  */
+
+static inline tree 
+chrec_fold_automatically_generated_operands (tree op0, 
+                                            tree op1)
+{
+  if (op0 == chrec_dont_know
+      || op1 == chrec_dont_know)
+    return chrec_dont_know;
+  
+  if (op0 == chrec_known
+      || op1 == chrec_known)
+    return chrec_known;
+  
+  if (op0 == chrec_not_analyzed_yet
+      || op1 == chrec_not_analyzed_yet)
+    return chrec_not_analyzed_yet;
+  
+  /* The default case produces a safe result. */
+  return chrec_dont_know;
+}
+
+/* Fold the addition of two chrecs.  */
+
+static tree
+chrec_fold_plus_1 (enum tree_code code, 
+                  tree type, 
+                  tree op0,
+                  tree op1)
+{
+  if (automatically_generated_chrec_p (op0)
+      || automatically_generated_chrec_p (op1))
+    return chrec_fold_automatically_generated_operands (op0, op1);
+  
+  switch (TREE_CODE (op0))
+    {
+    case POLYNOMIAL_CHREC:
+      switch (TREE_CODE (op1))
+       {
+       case POLYNOMIAL_CHREC:
+         return chrec_fold_plus_poly_poly (code, type, op0, op1);
+
+       default:
+         if (code == PLUS_EXPR)
+           return build_polynomial_chrec 
+             (CHREC_VARIABLE (op0), 
+              chrec_fold_plus (type, CHREC_LEFT (op0), op1),
+              CHREC_RIGHT (op0));
+         else
+           return build_polynomial_chrec 
+             (CHREC_VARIABLE (op0), 
+              chrec_fold_minus (type, CHREC_LEFT (op0), op1),
+              CHREC_RIGHT (op0));
+       }
+
+    default:
+      switch (TREE_CODE (op1))
+       {
+       case POLYNOMIAL_CHREC:
+         if (code == PLUS_EXPR)
+           return build_polynomial_chrec 
+             (CHREC_VARIABLE (op1), 
+              chrec_fold_plus (type, op0, CHREC_LEFT (op1)),
+              CHREC_RIGHT (op1));
+         else
+           return build_polynomial_chrec 
+             (CHREC_VARIABLE (op1), 
+              chrec_fold_minus (type, op0, CHREC_LEFT (op1)),
+              chrec_fold_multiply (type, CHREC_RIGHT (op1), 
+                                   convert (type,
+                                            integer_minus_one_node)));
+
+       default:
+         if (tree_contains_chrecs (op0)
+             || tree_contains_chrecs (op1))
+           return build (code, type, op0, op1);
+         else
+           return fold (build (code, type, op0, op1));
+       }
+    }
+}
+
+/* Fold the addition of two chrecs.  */
+
+tree
+chrec_fold_plus (tree type, 
+                tree op0,
+                tree op1)
+{
+  if (integer_zerop (op0))
+    return op1;
+  if (integer_zerop (op1))
+    return op0;
+  
+  return chrec_fold_plus_1 (PLUS_EXPR, type, op0, op1);
+}
+
+/* Fold the subtraction of two chrecs.  */
+
+tree 
+chrec_fold_minus (tree type, 
+                 tree op0, 
+                 tree op1)
+{
+  if (integer_zerop (op1))
+    return op0;
+  
+  return chrec_fold_plus_1 (MINUS_EXPR, type, op0, op1);
+}
+
+/* Fold the multiplication of two chrecs.  */
+
+tree
+chrec_fold_multiply (tree type, 
+                    tree op0,
+                    tree op1)
+{
+  if (automatically_generated_chrec_p (op0)
+      || automatically_generated_chrec_p (op1))
+    return chrec_fold_automatically_generated_operands (op0, op1);
+  
+  switch (TREE_CODE (op0))
+    {
+    case POLYNOMIAL_CHREC:
+      switch (TREE_CODE (op1))
+       {
+       case POLYNOMIAL_CHREC:
+         return chrec_fold_multiply_poly_poly (type, op0, op1);
+         
+       default:
+         if (integer_onep (op1))
+           return op0;
+         if (integer_zerop (op1))
+           return convert (type, integer_zero_node);
+         
+         return build_polynomial_chrec 
+           (CHREC_VARIABLE (op0), 
+            chrec_fold_multiply (type, CHREC_LEFT (op0), op1),
+            chrec_fold_multiply (type, CHREC_RIGHT (op0), op1));
+       }
+      
+    default:
+      if (integer_onep (op0))
+       return op1;
+      
+      if (integer_zerop (op0))
+       return convert (type, integer_zero_node);
+      
+      switch (TREE_CODE (op1))
+       {
+       case POLYNOMIAL_CHREC:
+         return build_polynomial_chrec 
+           (CHREC_VARIABLE (op1), 
+            chrec_fold_multiply (type, CHREC_LEFT (op1), op0),
+            chrec_fold_multiply (type, CHREC_RIGHT (op1), op0));
+         
+       default:
+         if (integer_onep (op1))
+           return op0;
+         if (integer_zerop (op1))
+           return convert (type, integer_zero_node);
+         return fold (build (MULT_EXPR, type, op0, op1));
+       }
+    }
+}
+
+\f
+
+/* Operations.  */
+
+/* The factorial.  */
+static tree 
+tree_fold_factorial (tree f)
+{
+  if (tree_int_cst_sgn (f) <= 0)
+    return integer_one_node;
+  else
+    return fold 
+      (build (MULT_EXPR, integer_type_node, f, 
+             tree_fold_factorial (fold (build (MINUS_EXPR, integer_type_node, 
+                                               f, integer_one_node)))));
+}
+
+/* The binomial coefficient.  */
+
+static tree 
+tree_fold_binomial (tree n,
+                   tree k)
+{
+  return fold 
+    (build (EXACT_DIV_EXPR, integer_type_node, tree_fold_factorial (n), 
+           fold (build (MULT_EXPR, integer_type_node, 
+                        tree_fold_factorial (k),
+                        tree_fold_factorial 
+                        (fold (build (MINUS_EXPR, integer_type_node, 
+                                      n, k)))))));
+}
+
+/* Helper function.  Use the Newton's interpolating formula for
+   evaluating the value of the evolution function.  */
+
+static tree 
+chrec_evaluate (unsigned var,
+               tree chrec,
+               tree n,
+               tree k)
+{
+  tree type = chrec_type (chrec);
+  tree binomial_n_k = tree_fold_binomial (n, k);
+  
+  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC)
+    {
+      if (CHREC_VARIABLE (chrec) > var)
+       return chrec_evaluate (var, CHREC_LEFT (chrec), n, k);
+      
+      if (CHREC_VARIABLE (chrec) == var)
+       return chrec_fold_plus 
+         (type, 
+          fold (build (MULT_EXPR, type, binomial_n_k, CHREC_LEFT (chrec))),
+          chrec_evaluate (var, CHREC_RIGHT (chrec), n, 
+                          fold (build (PLUS_EXPR, type, k, integer_one_node))));
+      
+      return fold (build (MULT_EXPR, type, binomial_n_k, chrec));
+    }
+  else
+    return fold (build (MULT_EXPR, type, binomial_n_k, chrec));
+}
+
+/* Evaluates "CHREC (X)" when the varying variable is VAR.  
+   Example:  Given the following parameters, 
+   
+   var = 1
+   chrec = {3, +, 4}_1
+   x = 10
+   
+   The result is given by the Newton's interpolating formula: 
+   3 * \binom{10}{0} + 4 * \binom{10}{1}.
+*/
+
+tree 
+chrec_apply (unsigned var,
+            tree chrec, 
+            tree x)
+{
+  tree type = chrec_type (chrec);
+  tree res = chrec_dont_know;
+
+  if (automatically_generated_chrec_p (chrec)
+      || automatically_generated_chrec_p (x)
+
+      /* When the symbols are defined in an outer loop, it is possible
+        to symbolically compute the apply, since the symbols are
+        constants with respect to the varying loop.  */
+      || chrec_contains_symbols_defined_in_loop (chrec, var)
+      || chrec_contains_symbols (x))
+    return chrec_dont_know;
+  
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "(chrec_apply \n");
+
+  if (evolution_function_is_affine_p (chrec))
+    {
+      /* "{a, +, b} (x)"  ->  "a + b*x".  */
+      if (TREE_CODE (CHREC_LEFT (chrec)) == INTEGER_CST
+         && integer_zerop (CHREC_LEFT (chrec)))
+       res = chrec_fold_multiply (type, CHREC_RIGHT (chrec), x);
+      
+      else
+       res = chrec_fold_plus (type, CHREC_LEFT (chrec), 
+                              chrec_fold_multiply (type, 
+                                                   CHREC_RIGHT (chrec), x));
+    }
+  
+  else if (TREE_CODE (chrec) != POLYNOMIAL_CHREC)
+    res = chrec;
+  
+  else if (TREE_CODE (x) == INTEGER_CST
+          && tree_int_cst_sgn (x) == 1)
+    /* testsuite/.../ssa-chrec-38.c.  */
+    res = chrec_evaluate (var, chrec, x, integer_zero_node);
+
+  else
+    res = chrec_dont_know;
+  
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  (varying_loop = %d\n", var);
+      fprintf (dump_file, ")\n  (chrec = ");
+      print_generic_expr (dump_file, chrec, 0);
+      fprintf (dump_file, ")\n  (x = ");
+      print_generic_expr (dump_file, x, 0);
+      fprintf (dump_file, ")\n  (res = ");
+      print_generic_expr (dump_file, res, 0);
+      fprintf (dump_file, "))\n");
+    }
+  
+  return res;
+}
+
+/* Replaces the initial condition in CHREC with INIT_COND.  */
+
+tree 
+chrec_replace_initial_condition (tree chrec, 
+                                tree init_cond)
+{
+  if (automatically_generated_chrec_p (chrec))
+    return chrec;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      return build_polynomial_chrec 
+       (CHREC_VARIABLE (chrec),
+        chrec_replace_initial_condition (CHREC_LEFT (chrec), init_cond),
+        CHREC_RIGHT (chrec));
+      
+    default:
+      return init_cond;
+    }
+}
+
+/* Returns the initial condition of a given CHREC.  */
+
+tree 
+initial_condition (tree chrec)
+{
+  if (automatically_generated_chrec_p (chrec))
+    return chrec;
+  
+  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC)
+    return initial_condition (CHREC_LEFT (chrec));
+  else
+    return chrec;
+}
+
+/* Returns a univariate function that represents the evolution in
+   LOOP_NUM.  Mask the evolution of any other loop.  */
+
+tree 
+hide_evolution_in_other_loops_than_loop (tree chrec, 
+                                        unsigned loop_num)
+{
+  if (automatically_generated_chrec_p (chrec))
+    return chrec;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      if (CHREC_VARIABLE (chrec) == loop_num)
+       return build_polynomial_chrec 
+         (loop_num, 
+          hide_evolution_in_other_loops_than_loop (CHREC_LEFT (chrec), 
+                                                   loop_num), 
+          CHREC_RIGHT (chrec));
+      
+      else if (CHREC_VARIABLE (chrec) < loop_num)
+       /* There is no evolution in this loop.  */
+       return initial_condition (chrec);
+      
+      else
+       return hide_evolution_in_other_loops_than_loop (CHREC_LEFT (chrec), 
+                                                       loop_num);
+      
+    default:
+      return chrec;
+    }
+}
+
+/* Returns the evolution part in LOOP_NUM.  Example: the call
+   get_evolution_in_loop (1, {{0, +, 1}_1, +, 2}_1) returns 
+   {1, +, 2}_1  */
+
+tree 
+evolution_part_in_loop_num (tree chrec, 
+                           unsigned loop_num)
+{
+  if (automatically_generated_chrec_p (chrec))
+    return chrec;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      if (CHREC_VARIABLE (chrec) == loop_num)
+       {
+         if (TREE_CODE (CHREC_LEFT (chrec)) != POLYNOMIAL_CHREC
+             || CHREC_VARIABLE (CHREC_LEFT (chrec)) != CHREC_VARIABLE (chrec))
+           return CHREC_RIGHT (chrec);
+         
+         else
+           return build_polynomial_chrec
+             (loop_num, 
+              evolution_part_in_loop_num (CHREC_LEFT (chrec), loop_num), 
+              CHREC_RIGHT (chrec));
+       }
+      
+      else if (CHREC_VARIABLE (chrec) < loop_num)
+       /* There is no evolution part in this loop.  */
+       return NULL_TREE;
+      
+      else
+       return evolution_part_in_loop_num (CHREC_LEFT (chrec), loop_num);
+      
+    default:
+      return NULL_TREE;
+    }
+}
+
+/* Set or reset the evolution of CHREC to NEW_EVOL in loop LOOP_NUM.
+   This function is essentially used for setting the evolution to
+   chrec_dont_know, for example after having determined that it is
+   impossible to say how many times a loop will execute.  */
+
+tree 
+reset_evolution_in_loop (unsigned loop_num,
+                        tree chrec, 
+                        tree new_evol)
+{
+  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC
+      && CHREC_VARIABLE (chrec) > loop_num)
+    return build 
+      (TREE_CODE (chrec), 
+       build_int_2 (CHREC_VARIABLE (chrec), 0), 
+       reset_evolution_in_loop (loop_num, CHREC_LEFT (chrec), new_evol), 
+       reset_evolution_in_loop (loop_num, CHREC_RIGHT (chrec), new_evol));
+  
+  while (TREE_CODE (chrec) == POLYNOMIAL_CHREC
+        && CHREC_VARIABLE (chrec) == loop_num)
+    chrec = CHREC_LEFT (chrec);
+  
+  return build_polynomial_chrec (loop_num, chrec, new_evol);
+}
+
+/* Merges two evolution functions that were found by following two
+   alternate paths of a conditional expression.  */
+
+tree
+chrec_merge (tree chrec1, 
+            tree chrec2)
+{
+  if (chrec1 == chrec_dont_know
+      || chrec2 == chrec_dont_know)
+    return chrec_dont_know;
+
+  if (chrec1 == chrec_known 
+      || chrec2 == chrec_known)
+    return chrec_known;
+
+  if (chrec1 == chrec_not_analyzed_yet)
+    return chrec2;
+  if (chrec2 == chrec_not_analyzed_yet)
+    return chrec1;
+
+  if (operand_equal_p (chrec1, chrec2, 0))
+    return chrec1;
+
+  return chrec_dont_know;
+}
+
+\f
+
+/* Observers.  */
+
+/* Helper function for is_multivariate_chrec.  */
+
+static bool 
+is_multivariate_chrec_rec (tree chrec, unsigned int rec_var)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC)
+    {
+      if (CHREC_VARIABLE (chrec) != rec_var)
+       return true;
+      else
+       return (is_multivariate_chrec_rec (CHREC_LEFT (chrec), rec_var) 
+               || is_multivariate_chrec_rec (CHREC_RIGHT (chrec), rec_var));
+    }
+  else
+    return false;
+}
+
+/* Determine whether the given chrec is multivariate or not.  */
+
+bool 
+is_multivariate_chrec (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC)
+    return (is_multivariate_chrec_rec (CHREC_LEFT (chrec), 
+                                      CHREC_VARIABLE (chrec))
+           || is_multivariate_chrec_rec (CHREC_RIGHT (chrec), 
+                                         CHREC_VARIABLE (chrec)));
+  else
+    return false;
+}
+
+/* Determines whether the chrec contains symbolic names or not.  */
+
+bool 
+chrec_contains_symbols (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  if (TREE_CODE (chrec) == SSA_NAME
+      || TREE_CODE (chrec) == VAR_DECL
+      || TREE_CODE (chrec) == PARM_DECL
+      || TREE_CODE (chrec) == FUNCTION_DECL
+      || TREE_CODE (chrec) == LABEL_DECL
+      || TREE_CODE (chrec) == RESULT_DECL
+      || TREE_CODE (chrec) == FIELD_DECL)
+    return true;
+  
+  switch (TREE_CODE_LENGTH (TREE_CODE (chrec)))
+    {
+    case 3:
+      if (chrec_contains_symbols (TREE_OPERAND (chrec, 2)))
+       return true;
+      
+    case 2:
+      if (chrec_contains_symbols (TREE_OPERAND (chrec, 1)))
+       return true;
+      
+    case 1:
+      if (chrec_contains_symbols (TREE_OPERAND (chrec, 0)))
+       return true;
+      
+    default:
+      return false;
+    }
+}
+
+/* Determines whether the chrec contains undetermined coefficients.  */
+
+bool 
+chrec_contains_undetermined (tree chrec)
+{
+  if (chrec == chrec_dont_know
+      || chrec == chrec_not_analyzed_yet
+      || chrec == NULL_TREE)
+    return true;
+  
+  switch (TREE_CODE_LENGTH (TREE_CODE (chrec)))
+    {
+    case 3:
+      if (chrec_contains_undetermined (TREE_OPERAND (chrec, 2)))
+       return true;
+      
+    case 2:
+      if (chrec_contains_undetermined (TREE_OPERAND (chrec, 1)))
+       return true;
+      
+    case 1:
+      if (chrec_contains_undetermined (TREE_OPERAND (chrec, 0)))
+       return true;
+      
+    default:
+      return false;
+    }
+}
+
+/* Determines whether the tree EXPR contains chrecs.  */
+
+bool
+tree_contains_chrecs (tree expr)
+{
+  if (expr == NULL_TREE)
+    return false;
+  
+  if (tree_is_chrec (expr))
+    return true;
+  
+  switch (TREE_CODE_LENGTH (TREE_CODE (expr)))
+    {
+    case 3:
+      if (tree_contains_chrecs (TREE_OPERAND (expr, 2)))
+       return true;
+      
+    case 2:
+      if (tree_contains_chrecs (TREE_OPERAND (expr, 1)))
+       return true;
+      
+    case 1:
+      if (tree_contains_chrecs (TREE_OPERAND (expr, 0)))
+       return true;
+      
+    default:
+      return false;
+    }
+}
+
+/* Determine whether the given tree is an affine multivariate
+   evolution.  */
+
+bool 
+evolution_function_is_affine_multivariate_p (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      if (evolution_function_is_constant_p (CHREC_LEFT (chrec)))
+       {
+         if (evolution_function_is_constant_p (CHREC_RIGHT (chrec)))
+           return true;
+         else
+           {
+             if (TREE_CODE (CHREC_RIGHT (chrec)) == POLYNOMIAL_CHREC
+                 && CHREC_VARIABLE (CHREC_RIGHT (chrec)) 
+                    != CHREC_VARIABLE (chrec)
+                 && evolution_function_is_affine_multivariate_p 
+                 (CHREC_RIGHT (chrec)))
+               return true;
+             else
+               return false;
+           }
+       }
+      else
+       {
+         if (evolution_function_is_constant_p (CHREC_RIGHT (chrec))
+             && TREE_CODE (CHREC_LEFT (chrec)) == POLYNOMIAL_CHREC
+             && CHREC_VARIABLE (CHREC_LEFT (chrec)) != CHREC_VARIABLE (chrec)
+             && evolution_function_is_affine_multivariate_p 
+             (CHREC_LEFT (chrec)))
+           return true;
+         else
+           return false;
+       }
+      
+    default:
+      return false;
+    }
+}
+
+/* Determine whether the given tree is a function in zero or one 
+   variables.  */
+
+bool
+evolution_function_is_univariate_p (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return true;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      switch (TREE_CODE (CHREC_LEFT (chrec)))
+       {
+       case POLYNOMIAL_CHREC:
+         if (CHREC_VARIABLE (chrec) != CHREC_VARIABLE (CHREC_LEFT (chrec)))
+           return false;
+         if (!evolution_function_is_univariate_p (CHREC_LEFT (chrec)))
+           return false;
+         break;
+         
+       default:
+         break;
+       }
+      
+      switch (TREE_CODE (CHREC_RIGHT (chrec)))
+       {
+       case POLYNOMIAL_CHREC:
+         if (CHREC_VARIABLE (chrec) != CHREC_VARIABLE (CHREC_RIGHT (chrec)))
+           return false;
+         if (!evolution_function_is_univariate_p (CHREC_RIGHT (chrec)))
+           return false;
+         break;
+         
+       default:
+         break;          
+       }
+      
+    default:
+      return true;
+    }
+}
+
+\f
+
+/* Convert the initial condition of chrec to type.  */
+
+tree 
+chrec_convert (tree type, 
+              tree chrec)
+{
+  tree ct;
+  
+  if (automatically_generated_chrec_p (chrec))
+    return chrec;
+  
+  ct = chrec_type (chrec);
+  if (ct == type)
+    return chrec;
+
+  if (TYPE_PRECISION (ct) < TYPE_PRECISION (type))
+    return count_ev_in_wider_type (type, chrec);
+
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      return build_polynomial_chrec (CHREC_VARIABLE (chrec),
+                                    chrec_convert (type,
+                                                   CHREC_LEFT (chrec)),
+                                    chrec_convert (type,
+                                                   CHREC_RIGHT (chrec)));
+
+    default:
+      {
+       tree res = convert (type, chrec);
+
+       /* Don't propagate overflows.  */
+       TREE_OVERFLOW (res) = 0;
+       if (TREE_CODE_CLASS (TREE_CODE (res)) == 'c')
+         TREE_CONSTANT_OVERFLOW (res) = 0;
+       return res;
+      }
+    }
+}
+
+/* Returns the type of the chrec.  */
+
+tree 
+chrec_type (tree chrec)
+{
+  if (automatically_generated_chrec_p (chrec))
+    return NULL_TREE;
+  
+  return TREE_TYPE (chrec);
+}
+
+extern void initialize_scalar_evolutions_analyzer (void);
+
+/* Initializer.  */
+
+void
+initialize_scalar_evolutions_analyzer (void)
+{
+  /* The elements below are unique.  */
+  if (chrec_dont_know == NULL_TREE)
+    {
+      chrec_not_analyzed_yet = NULL_TREE;
+      chrec_dont_know = make_node (SCEV_NOT_KNOWN);
+      chrec_known = make_node (SCEV_KNOWN);
+      TREE_TYPE (chrec_dont_know) = NULL_TREE;
+      TREE_TYPE (chrec_known) = NULL_TREE;
+    }
+}
diff --git a/gcc/tree-chrec.h b/gcc/tree-chrec.h
new file mode 100644 (file)
index 0000000..8e355f0
--- /dev/null
@@ -0,0 +1,206 @@
+/* Chains of recurrences.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Contributed by Sebastian Pop <s.pop@laposte.net>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef GCC_TREE_CHREC_H
+#define GCC_TREE_CHREC_H
+
+/* Accessors for the chains of recurrences.  */
+#define CHREC_VAR(NODE)           TREE_OPERAND (NODE, 0)
+#define CHREC_LEFT(NODE)          TREE_OPERAND (NODE, 1)
+#define CHREC_RIGHT(NODE)         TREE_OPERAND (NODE, 2)
+#define CHREC_VARIABLE(NODE)      TREE_INT_CST_LOW (CHREC_VAR (NODE))
+
+\f
+
+/* The following trees are unique elements.  Thus the comparison of another 
+   element to these elements should be done on the pointer to these trees, 
+   and not on their value.  */
+
+extern tree chrec_not_analyzed_yet;
+extern GTY(()) tree chrec_dont_know;
+extern GTY(()) tree chrec_known;
+
+/* After having added an automatically generated element, please
+   include it in the following function.  */
+
+static inline bool
+automatically_generated_chrec_p (tree chrec)
+{
+  return (chrec == chrec_not_analyzed_yet 
+         || chrec == chrec_dont_know
+         || chrec == chrec_known);
+}
+
+/* The tree nodes aka. CHRECs.  */
+
+static inline bool
+tree_is_chrec (tree expr)
+{
+  if (TREE_CODE (expr) == POLYNOMIAL_CHREC
+      || automatically_generated_chrec_p (expr))
+    return true;
+  else
+    return false;
+}
+
+\f
+
+/* Chrec folding functions.  */
+extern tree chrec_fold_plus (tree, tree, tree);
+extern tree chrec_fold_minus (tree, tree, tree);
+extern tree chrec_fold_multiply (tree, tree, tree);
+extern tree chrec_convert (tree, tree);
+extern tree count_ev_in_wider_type (tree, tree);
+extern tree chrec_type (tree);
+
+/* Operations.  */
+extern tree chrec_apply (unsigned, tree, tree);
+extern tree chrec_replace_initial_condition (tree, tree);
+extern tree update_initial_condition_to_origin (tree);
+extern tree initial_condition (tree);
+extern tree evolution_part_in_loop_num (tree, unsigned);
+extern tree hide_evolution_in_other_loops_than_loop (tree, unsigned);
+extern tree reset_evolution_in_loop (unsigned, tree, tree);
+extern tree chrec_merge (tree, tree);
+
+/* Observers.  */
+extern bool is_multivariate_chrec (tree);
+extern bool chrec_is_positive (tree, bool *);
+extern bool chrec_contains_symbols (tree);
+extern bool chrec_contains_symbols_defined_in_loop (tree, unsigned);
+extern bool chrec_contains_undetermined (tree);
+extern bool tree_contains_chrecs (tree);
+extern bool evolution_function_is_affine_multivariate_p (tree);
+extern bool evolution_function_is_univariate_p (tree);
+
+\f
+
+/* Build a polynomial chain of recurrence.  */
+
+static inline tree 
+build_polynomial_chrec (unsigned loop_num, 
+                       tree left, 
+                       tree right)
+{
+  if (left == chrec_dont_know
+      || right == chrec_dont_know)
+    return chrec_dont_know;
+
+  return build (POLYNOMIAL_CHREC, TREE_TYPE (left), 
+               build_int_2 (loop_num, 0), left, right);
+}
+
+\f
+
+/* Observers.  */
+
+/* Determines whether CHREC is equal to zero.  */
+
+static inline bool 
+chrec_zerop (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  if (TREE_CODE (chrec) == INTEGER_CST)
+    return integer_zerop (chrec);
+  
+  return false;
+}
+
+/* Determines whether the expression CHREC is a constant.  */
+
+static inline bool 
+evolution_function_is_constant_p (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+
+  switch (TREE_CODE (chrec))
+    {
+    case INTEGER_CST:
+    case REAL_CST:
+      return true;
+      
+    default:
+      return false;
+    }
+}
+
+/* Determine whether the given tree is an affine evolution function or not.  */
+
+static inline bool 
+evolution_function_is_affine_p (tree chrec)
+{
+  if (chrec == NULL_TREE)
+    return false;
+  
+  switch (TREE_CODE (chrec))
+    {
+    case POLYNOMIAL_CHREC:
+      if (evolution_function_is_constant_p (CHREC_LEFT (chrec))
+         && evolution_function_is_constant_p (CHREC_RIGHT (chrec)))
+       return true;
+      else
+       return false;
+      
+    default:
+      return false;
+    }
+}
+
+/* Determine whether the given tree is an affine or constant evolution
+   function.  */
+
+static inline bool 
+evolution_function_is_affine_or_constant_p (tree chrec)
+{
+  return evolution_function_is_affine_p (chrec) 
+    || evolution_function_is_constant_p (chrec);
+}
+
+/* Determines whether EXPR does not contains chrec expressions.  */
+
+static inline bool
+tree_does_not_contain_chrecs (tree expr)
+{
+  return !tree_contains_chrecs (expr);
+}
+
+/* Determines whether CHREC is a loop invariant with respect to LOOP_NUM.  
+   Set the result in RES and return true when the property can be computed.  */
+
+static inline bool
+no_evolution_in_loop_p (tree chrec, unsigned loop_num, bool *res)
+{
+  tree scev;
+  
+  if (chrec == chrec_not_analyzed_yet
+      || chrec == chrec_dont_know
+      || chrec_contains_symbols_defined_in_loop (chrec, loop_num))
+    return false;
+
+  scev = hide_evolution_in_other_loops_than_loop (chrec, loop_num);
+  *res = !tree_is_chrec (scev);
+  return true;
+}
+
+#endif  /* GCC_TREE_CHREC_H  */
index d126895369035a5ebce389edcd4b85247854fc7b..15172077bdc367ebc2da993aef5bb6370afa4fa0 100644 (file)
@@ -898,6 +898,18 @@ DEFTREECODE (CATCH_EXPR, "catch_expr", 's', 2)
    expanding.  */
 DEFTREECODE (EH_FILTER_EXPR, "eh_filter_expr", 's', 2)
 
+/* Node used for describing a property that is known at compile
+   time.  */
+DEFTREECODE (SCEV_KNOWN, "scev_known", 'e', 0)
+
+/* Node used for describing a property that is not known at compile
+   time.  */
+DEFTREECODE (SCEV_NOT_KNOWN, "scev_not_known", 'e', 0)
+
+/* Polynomial chains of recurrences.
+   Under the form: cr = {CHREC_LEFT (cr), +, CHREC_RIGHT (cr)}.  */
+DEFTREECODE (POLYNOMIAL_CHREC, "polynomial_chrec", 'e', 3)
+
 /* Used to chain children of container statements together.
    Use the interface in tree-iterator.h to access this node.  */
 DEFTREECODE (STATEMENT_LIST, "statement_list", 'x', 0)