OSDN Git Service

* gcc-interface/ada-tree.def (LOOP_STMT): Change to 4-operand nodes.
[pf3gnuchains/gcc-fork.git] / gcc / ada / gcc-interface / trans.c
index cbe625c..710887b 100644 (file)
@@ -2099,8 +2099,8 @@ static tree
 Loop_Statement_to_gnu (Node_Id gnat_node)
 {
   const Node_Id gnat_iter_scheme = Iteration_Scheme (gnat_node);
-  tree gnu_loop_stmt = build5 (LOOP_STMT, void_type_node, NULL_TREE,
-                              NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
+  tree gnu_loop_stmt = build4 (LOOP_STMT, void_type_node, NULL_TREE,
+                              NULL_TREE, NULL_TREE, NULL_TREE);
   tree gnu_loop_label = create_artificial_label (input_location);
   tree gnu_loop_var = NULL_TREE, gnu_cond_expr = NULL_TREE;
   tree gnu_result;
@@ -2136,8 +2136,10 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
       tree gnu_low = TYPE_MIN_VALUE (gnu_type);
       tree gnu_high = TYPE_MAX_VALUE (gnu_type);
       tree gnu_base_type = get_base_type (gnu_type);
-      tree gnu_first, gnu_last, gnu_limit, gnu_test;
-      enum tree_code update_code, test_code;
+      tree gnu_one_node = convert (gnu_base_type, integer_one_node);
+      tree gnu_first, gnu_last;
+      enum tree_code update_code, test_code, shift_code;
+      bool reverse = Reverse_Present (gnat_loop_spec), fallback = false;
 
       /* We must disable modulo reduction for the iteration variable, if any,
         in order for the loop comparison to be effective.  */
@@ -2147,7 +2149,7 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
          gnu_last = gnu_low;
          update_code = MINUS_NOMOD_EXPR;
          test_code = GE_EXPR;
-         gnu_limit = TYPE_MIN_VALUE (gnu_base_type);
+         shift_code = PLUS_NOMOD_EXPR;
        }
       else
        {
@@ -2155,17 +2157,110 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
          gnu_last = gnu_high;
          update_code = PLUS_NOMOD_EXPR;
          test_code = LE_EXPR;
-         gnu_limit = TYPE_MAX_VALUE (gnu_base_type);
+         shift_code = MINUS_NOMOD_EXPR;
        }
 
-      /* We know that the iteration variable will not overflow if GNU_LAST is
-        a constant and is not equal to GNU_LIMIT.  If it might overflow, we
-        have to turn the limit test into an inequality test and move it to
-        the end of the loop; as a consequence, we also have to test for an
-        empty loop before entering it.  */
-      if (TREE_CODE (gnu_last) != INTEGER_CST
-         || TREE_CODE (gnu_limit) != INTEGER_CST
-         || tree_int_cst_equal (gnu_last, gnu_limit))
+      /* We use two different strategies to translate the loop, depending on
+        whether optimization is enabled.
+
+        If it is, we try to generate the canonical form of loop expected by
+        the loop optimizer, which is the do-while form:
+
+            ENTRY_COND
+          loop:
+            TOP_UPDATE
+            BODY
+            BOTTOM_COND
+            GOTO loop
+
+        This makes it possible to bypass loop header copying and to turn the
+        BOTTOM_COND into an inequality test.  This should catch (almost) all
+        loops with constant starting point.  If we cannot, we try to generate
+        the default form, which is:
+
+          loop:
+            TOP_COND
+            BODY
+            BOTTOM_UPDATE
+            GOTO loop
+
+        It will be rotated during loop header copying and an entry test added
+        to yield the do-while form.  This should catch (almost) all loops with
+        constant ending point.  If we cannot, we generate the fallback form:
+
+            ENTRY_COND
+          loop:
+            BODY
+            BOTTOM_COND
+            BOTTOM_UPDATE
+            GOTO loop
+
+        which works in all cases but for which loop header copying will copy
+        the BOTTOM_COND, thus adding a third conditional branch.
+
+        If optimization is disabled, loop header copying doesn't come into
+        play and we try to generate the loop forms with the less conditional
+        branches directly.  First, the default form, it should catch (almost)
+        all loops with constant ending point.  Then, if we cannot, we try to
+        generate the shifted form:
+
+          loop:
+            TOP_COND
+            TOP_UPDATE
+            BODY
+            GOTO loop
+
+        which should catch loops with constant starting point.  Otherwise, if
+        we cannot, we generate the fallback form.  */
+
+      if (optimize)
+       {
+         /* We can use the do-while form if GNU_FIRST-1 doesn't overflow.  */
+         if (!can_equal_min_val_p (gnu_first, gnu_base_type, reverse))
+           {
+             gnu_first = build_binary_op (shift_code, gnu_base_type,
+                                          gnu_first, gnu_one_node);
+             LOOP_STMT_TOP_UPDATE_P (gnu_loop_stmt) = 1;
+             LOOP_STMT_BOTTOM_COND_P (gnu_loop_stmt) = 1;
+           }
+
+         /* Otherwise, we can use the default form if GNU_LAST+1 doesn't.  */
+         else if (!can_equal_max_val_p (gnu_last, gnu_base_type, reverse))
+           ;
+
+         /* Otherwise, use the fallback form.  */
+         else
+           fallback = true;
+       }
+      else
+       {
+         /* We can use the default form if GNU_LAST+1 doesn't overflow.  */
+         if (!can_equal_max_val_p (gnu_last, gnu_base_type, reverse))
+           ;
+
+         /* Otherwise, we can use the shifted form if neither GNU_FIRST-1 nor
+            GNU_LAST-1 does.  */
+         else if (!can_equal_min_val_p (gnu_first, gnu_base_type, reverse)
+                  && !can_equal_min_val_p (gnu_last, gnu_base_type, reverse))
+           {
+             gnu_first = build_binary_op (shift_code, gnu_base_type,
+                                          gnu_first, gnu_one_node);
+             gnu_last = build_binary_op (shift_code, gnu_base_type,
+                                         gnu_last, gnu_one_node);
+             LOOP_STMT_TOP_UPDATE_P (gnu_loop_stmt) = 1;
+           }
+
+         /* Otherwise, use the fallback form.  */
+         else
+           fallback = true;
+       }
+
+      if (fallback)
+       LOOP_STMT_BOTTOM_COND_P (gnu_loop_stmt) = 1;
+
+      /* If we use the BOTTOM_COND, we can turn the test into an inequality
+        test but we have to add an ENTRY_COND to protect the empty loop.  */
+      if (LOOP_STMT_BOTTOM_COND_P (gnu_loop_stmt))
        {
          test_code = NE_EXPR;
          gnu_cond_expr
@@ -2174,7 +2269,6 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
                                       gnu_low, gnu_high),
                      NULL_TREE, alloc_stmt_list ());
          set_expr_location_from_node (gnu_cond_expr, gnat_loop_spec);
-         test_code = NE_EXPR;
        }
 
       /* Open a new nesting level that will surround the loop to declare the
@@ -2190,14 +2284,10 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
       /* Do all the arithmetics in the base type.  */
       gnu_loop_var = convert (gnu_base_type, gnu_loop_var);
 
-      /* Set either the top or bottom exit condition as appropriate depending
-        on whether or not we know an overflow cannot occur.  */
-      gnu_test = build_binary_op (test_code, integer_type_node, gnu_loop_var,
-                                 gnu_last);
-      if (gnu_cond_expr)
-       LOOP_STMT_BOT_COND (gnu_loop_stmt) = gnu_test;
-      else
-       LOOP_STMT_TOP_COND (gnu_loop_stmt) = gnu_test;
+      /* Set either the top or bottom exit condition.  */
+      LOOP_STMT_COND (gnu_loop_stmt)
+       = build_binary_op (test_code, integer_type_node, gnu_loop_var,
+                          gnu_last);
 
       /* Set either the top or bottom update statement and give it the source
         location of the iteration for better coverage info.  */