https://gcc.gnu.org/g:a42bca3cec7e2cb8999de5cd43b21352a0445b81

commit a42bca3cec7e2cb8999de5cd43b21352a0445b81
Author: Mikael Morin <mik...@gcc.gnu.org>
Date:   Tue May 13 12:23:09 2025 +0200

    Correction régressions loop versioning

Diff:
---
 gcc/gimple-loop-versioning.cc                   | 243 +++++++++++++++++++++---
 gcc/testsuite/gfortran.dg/loop_versioning_9.f90 |   2 +-
 2 files changed, 222 insertions(+), 23 deletions(-)

diff --git a/gcc/gimple-loop-versioning.cc b/gcc/gimple-loop-versioning.cc
index 0c487d360ce8..6140972fcd0b 100644
--- a/gcc/gimple-loop-versioning.cc
+++ b/gcc/gimple-loop-versioning.cc
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-into-ssa.h"
 #include "gimple-range.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 
 namespace {
 
@@ -253,6 +254,9 @@ public:
   class loop *optimized_loop;
 };
 
+static void
+dump_inner_likelihood (address_info &, address_term_info &);
+
 /* The main pass structure.  */
 class loop_versioning
 {
@@ -300,7 +304,6 @@ private:
   bool acceptable_type_p (tree, unsigned HOST_WIDE_INT *);
   bool multiply_term_by (address_term_info &, tree);
   inner_likelihood get_inner_likelihood (tree, unsigned HOST_WIDE_INT);
-  void dump_inner_likelihood (address_info &, address_term_info &);
   void analyze_stride (address_info &, address_term_info &,
                       tree, class loop *);
   bool find_per_loop_multiplication (address_info &, address_term_info &);
@@ -837,9 +840,8 @@ loop_versioning::get_inner_likelihood (tree stride,
 /* Dump the likelihood that TERM's stride is for the innermost dimension.
    ADDRESS is the address that contains TERM.  */
 
-void
-loop_versioning::dump_inner_likelihood (address_info &address,
-                                       address_term_info &term)
+static void
+dump_inner_likelihood (address_info &address, address_term_info &term)
 {
   if (term.inner_likelihood == INNER_LIKELY)
     dump_printf_loc (MSG_NOTE, address.stmt, "%T is likely to be the"
@@ -1052,6 +1054,185 @@ loop_versioning::analyze_arbitrary_term (address_info 
&address,
     dump_inner_likelihood (address, term);
 }
 
+
+static bool
+tree_same_expr_p (tree t1, tree t2)
+{
+  if (t1 == t2)
+    return true;
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return false;
+
+  int len1 = TREE_OPERAND_LENGTH (t1);
+  int len2 = TREE_OPERAND_LENGTH (t2);
+  if (len1 != len2)
+    return false;
+
+  for (int i = 0; i < len1; ++i)
+    if (!tree_same_expr_p (TREE_OPERAND (t1, i),
+                          TREE_OPERAND (t2, i)))
+      return false;
+
+  return true;
+}
+
+
+static void
+set_unknown_likelihood (address_info &address, address_term_info &term_info,
+                       enum inner_likelihood value)
+  
+{
+  if (term_info.inner_likelihood == INNER_DONT_KNOW)
+    {
+      term_info.inner_likelihood = value;
+      if (dump_enabled_p ())
+       dump_inner_likelihood (address, term_info);
+    }
+}
+
+
+static void
+guess_likelihood_from_offsets (address_info &address)
+{
+  struct stride_info
+  {
+    tree data_ref;
+    tree base;
+    HOST_WIDE_INT offset;
+    unsigned int idx;
+  };
+
+  auto_vec<stride_info> strides;
+  auto_vec<tree> worklist;
+
+  for (unsigned int i = 0; i < address.terms.length (); ++i)
+    {
+      worklist.safe_push (address.terms[i].expr);
+
+      while (!worklist.is_empty ())
+       {
+         tree expr = worklist.pop ();
+         tree old_expr = NULL_TREE;
+
+         while (expr != old_expr)
+           {
+             old_expr = expr;
+
+             expr = strip_casts (expr);
+             if (TREE_CODE (expr) == SSA_NAME)
+               {
+                 gimple *stmt = SSA_NAME_DEF_STMT (expr);
+                 if (!is_gimple_assign (stmt))
+                   continue;
+
+                 if (gimple_assign_single_p (stmt))
+                   {
+                     gassign *assign = as_a <gassign *> (stmt);
+                     expr = gimple_assign_rhs1 (assign);
+                   }
+                 else if (gimple_assign_rhs_code (stmt) == MULT_EXPR)
+                   {
+                     worklist.safe_push (gimple_assign_rhs1 (stmt));
+                     worklist.safe_push (gimple_assign_rhs2 (stmt));
+                     expr = NULL_TREE;
+                     break;
+                   }
+               }
+           }
+
+         if (expr == NULL_TREE)
+           continue;
+
+         HOST_WIDE_INT offset;
+         HOST_WIDE_INT scratch_size;
+         bool scratch_reverse;
+         tree base = get_ref_base_and_extent_hwi (expr, &offset, &scratch_size,
+                                                  &scratch_reverse);
+         if (base == NULL_TREE || offset < 0)
+           continue;
+
+         stride_info si;
+         si.data_ref = expr;
+         si.base = base;
+         si.offset = offset;
+         si.idx = i;
+
+         strides.safe_push (si);
+       }
+    }
+
+  unsigned int i;
+  stride_info *pinfo;
+  FOR_EACH_VEC_ELT (strides, i, pinfo)
+    if (pinfo->data_ref
+       && TREE_CODE (pinfo->data_ref) == COMPONENT_REF)
+      {
+       tree comp_base = TREE_OPERAND (pinfo->data_ref, 0);
+       if (TREE_CODE (comp_base) == ARRAY_REF)
+         {
+           unsigned int idx = pinfo->idx;
+           if (integer_zerop (TREE_OPERAND (comp_base, 1)))
+             set_unknown_likelihood (address, address.terms[idx],
+                                     INNER_LIKELY);
+           else if (TREE_CODE (TREE_OPERAND (comp_base, 1)) == INTEGER_CST)
+             set_unknown_likelihood (address, address.terms[idx],
+                                     INNER_UNLIKELY);
+         }
+      }
+
+  while (strides.length () > 0)
+    {
+      int j = strides.length () - 1;
+
+      stride_info min_offset_info = strides[j];
+      bool has_zero_offset = min_offset_info.offset == 0;
+      int count_non_zero_offset = min_offset_info.offset != 0;
+      strides.unordered_remove (j);
+
+      for (int i = j - 1; i >= 0; --i)
+       {
+         if (!tree_same_expr_p (strides[i].base, min_offset_info.base))
+           continue;
+
+         if (strides[i].offset == 0)
+           has_zero_offset = true;
+         else
+           {
+             if (min_offset_info.offset == 0)
+               min_offset_info = strides[i];
+             else
+               {
+                 ++count_non_zero_offset;
+                 if (strides[i].offset < min_offset_info.offset)
+                   {
+                     int idx = min_offset_info.idx;
+                     set_unknown_likelihood (address, address.terms[idx],
+                                             INNER_UNLIKELY);
+
+                     min_offset_info = strides[i];
+                   }
+                 else if (strides[i].offset > min_offset_info.offset)
+                   {
+                     int idx = strides[i].idx;
+                     set_unknown_likelihood (address, address.terms[idx],
+                                             INNER_UNLIKELY);
+                   }
+               }
+           }
+
+         strides.unordered_remove (i);
+       }
+
+      if (has_zero_offset
+         && count_non_zero_offset > 1
+         && min_offset_info.offset != 0)
+       set_unknown_likelihood (address, address.terms[min_offset_info.idx],
+                               INNER_LIKELY);
+    }
+}
+
+
 /* Try to identify loop strides in ADDRESS and try to choose realistic
    versioning opportunities based on these strides.
 
@@ -1137,6 +1318,8 @@ loop_versioning::analyze_address_fragment (address_info 
&address)
        && !POINTER_TYPE_P (TREE_TYPE (address.terms[i].expr)))
       analyze_arbitrary_term (address, address.terms[i]);
 
+  guess_likelihood_from_offsets (address);
+
   /* Check for strides that are likely to be for the innermost dimension.
 
      1. If there is a single likely inner stride, if it is an SSA name,
@@ -1189,15 +1372,16 @@ loop_versioning::analyze_address_fragment (address_info 
&address)
   address_term_info *chosen_term = nullptr;
   address_term_info *version_term = nullptr;
   for (unsigned int i = 0; i < address.terms.length (); ++i)
-    if ((chosen_term == nullptr
-        || chosen_term->stride != address.terms[i].stride)
-       && address.terms[i].inner_likelihood == INNER_LIKELY)
+    if (address.terms[i].inner_likelihood == INNER_LIKELY)
       {
-       if (chosen_term)
+       if (chosen_term == nullptr)
+         {
+           chosen_term = &address.terms[i];
+           if (address.terms[i].versioning_opportunity_p)
+             version_term = chosen_term;
+         }
+       else if (chosen_term->stride != address.terms[i].stride)
          return;
-       chosen_term = &address.terms[i];
-       if (address.terms[i].versioning_opportunity_p)
-         version_term = chosen_term;
       }
 
   /* If there are no likely inner strides, see if there is a single
@@ -1206,14 +1390,13 @@ loop_versioning::analyze_address_fragment (address_info 
&address)
      handles.  */
   if (!chosen_term)
     for (unsigned int i = 0; i < address.terms.length (); ++i)
-      if ((version_term == nullptr
-          || version_term->stride != address.terms[i].stride)
-         && address.terms[i].inner_likelihood == INNER_DONT_KNOW
+      if (address.terms[i].inner_likelihood == INNER_DONT_KNOW
          && address.terms[i].versioning_opportunity_p)
        {
-         if (version_term)
+         if (version_term == nullptr)
+           version_term = &address.terms[i];
+         else if (version_term->stride != address.terms[i].stride)
            return;
-         version_term = &address.terms[i];
        }
 
   if (version_term)
@@ -1611,19 +1794,31 @@ loop_versioning::prune_conditions ()
   return m_num_conditions != 0;
 }
 
+struct copy_info
+{
+  loop_info::name_value_map_t &dest_map;
+  class loop *inner_loop;
+};
+
 
 static bool
 copy_versioning_value (const unsigned &id_name,
                       const unsigned HOST_WIDE_INT &version_value,
-                      loop_info::name_value_map_t &dest_map)
+                      copy_info &info)
 {
   bool existed = false;
-  unsigned HOST_WIDE_INT &copied_value = dest_map.get_or_insert (
+  unsigned HOST_WIDE_INT &copied_value = info.dest_map.get_or_insert (
                                    id_name, &existed);
   if (!existed)
-    copied_value = version_value;
+    {
+      copied_value = version_value;
+      if (dump_enabled_p ())
+       dump_printf_loc (MSG_NOTE, find_loop_location (info.inner_loop),
+                        "hoisting check that %T == " HOST_WIDE_INT_PRINT_DEC
+                        " to outer loop\n", ssa_name (id_name), version_value);
+    }
   else if (copied_value != version_value)
-    dest_map.remove (id_name);
+    info.dest_map.remove (id_name);
 
   return true;
 }
@@ -1650,9 +1845,13 @@ loop_versioning::merge_loop_info (class loop *outer, 
class loop *inner)
 
   bitmap_ior_into (&outer_li.unity_names, &inner_li.unity_names);
 
+  copy_info info {
+    outer_li.non_unity_versioning_values,
+    inner
+  };
+
   inner_li.non_unity_versioning_values
-       .traverse<loop_info::name_value_map_t &, copy_versioning_value> (
-                   outer_li.non_unity_versioning_values);
+       .traverse<copy_info &, copy_versioning_value> (info);
 
   if (loop_depth (outer_li.outermost) < loop_depth (inner_li.outermost))
     outer_li.outermost = inner_li.outermost;
diff --git a/gcc/testsuite/gfortran.dg/loop_versioning_9.f90 
b/gcc/testsuite/gfortran.dg/loop_versioning_9.f90
index 7a0fd55eaca6..4f752cfaed6c 100644
--- a/gcc/testsuite/gfortran.dg/loop_versioning_9.f90
+++ b/gcc/testsuite/gfortran.dg/loop_versioning_9.f90
@@ -26,6 +26,6 @@ subroutine f4(x, i)
   end do
 end subroutine f4
 
-! { dg-final { scan-tree-dump-times {likely to be the innermost dimension} 4 
"lversion" } }
+! { dg-final { scan-tree-dump-times {likely to be the innermost dimension} 3 
"lversion" } }
 ! { dg-final { scan-tree-dump-not {want to version} "lversion" } }
 ! { dg-final { scan-tree-dump-not {versioned} "lversion" } }

Reply via email to