https://gcc.gnu.org/g:9403b035befe3537c343f7430e321468c0f2c28b

commit r15-6105-g9403b035befe3537c343f7430e321468c0f2c28b
Author: Tamar Christina <tamar.christ...@arm.com>
Date:   Wed Dec 11 11:47:49 2024 +0000

    middle-end: use two's complement equality when comparing IVs during 
candidate selection  [PR114932]
    
    IVOPTS normally uses affine trees to perform comparisons between different 
IVs,
    but these seem to have been missing in two key spots and instead normal tree
    equivalencies used.
    
    In some cases where we have a two-complements equivalence but not a strict
    signedness equivalencies we end up generating both a signed and unsigned IV 
for
    the same candidate.
    
    This patch implements a new OEP flag called OEP_ASSUME_WRAPV.  This flag 
will
    check if the operands would produce the same bit values after the 
computations
    even if the final sign is different.
    
    This happens quite a lot with Fortran but can also happen in C because this 
came
    code is unable to figure out when one expression is a multiple of another.
    
    As an example in the attached testcase we get:
    
    Initial set of candidates:
      cost: 24 (complexity 3)
      reg_cost: 9
      cand_cost: 15
      cand_group_cost: 0 (complexity 3)
      candidates: 1, 6, 8
       group:0 --> iv_cand:6, cost=(0,1)
       group:1 --> iv_cand:1, cost=(0,0)
       group:2 --> iv_cand:8, cost=(0,1)
       group:3 --> iv_cand:8, cost=(0,1)
      invariant variables: 6
      invariant expressions: 1, 2
    
    <Invariant Expressions>:
    inv_expr 1:     stride.3_27 * 4
    inv_expr 2:     (unsigned long) stride.3_27 * 4
    
    These end up being used in the same group:
    
    Group 1:
    cand  cost    compl.  inv.expr.       inv.vars
    1     0       0       NIL;    6
    2     0       0       NIL;    6
    3     0       0       NIL;    6
    
    which ends up with IV opts picking the signed and unsigned IVs:
    
    Improved to:
      cost: 24 (complexity 3)
      reg_cost: 9
      cand_cost: 15
      cand_group_cost: 0 (complexity 3)
      candidates: 1, 6, 8
       group:0 --> iv_cand:6, cost=(0,1)
       group:1 --> iv_cand:1, cost=(0,0)
       group:2 --> iv_cand:8, cost=(0,1)
       group:3 --> iv_cand:8, cost=(0,1)
      invariant variables: 6
      invariant expressions: 1, 2
    
    and so generates the same IV as both signed and unsigned:
    
    ;;   basic block 21, loop depth 3, count 214748368 (estimated locally, freq 
58.2545), maybe hot
    ;;    prev block 28, next block 31, flags: (NEW, REACHABLE, VISITED)
    ;;    pred:       28 [always]  count:23622320 (estimated locally, freq 
6.4080) (FALLTHRU,EXECUTABLE)
    ;;                25 [always]  count:191126046 (estimated locally, freq 
51.8465) (FALLTHRU,DFS_BACK,EXECUTABLE)
      # .MEM_66 = PHI <.MEM_34(28), .MEM_22(25)>
      # ivtmp.22_41 = PHI <0(28), ivtmp.22_82(25)>
      # ivtmp.26_51 = PHI <ivtmp.26_55(28), ivtmp.26_72(25)>
      # ivtmp.28_90 = PHI <ivtmp.28_99(28), ivtmp.28_98(25)>
    
    ...
    
    ;;   basic block 24, loop depth 3, count 214748366 (estimated locally, freq 
58.2545), maybe hot
    ;;    prev block 22, next block 25, flags: (NEW, REACHABLE, VISITED)'
    ;;    pred:       22 [always]  count:95443719 (estimated locally, freq 
25.8909) (FALLTHRU)
    ;;                21 [33.3% (guessed)]  count:71582790 (estimated locally, 
freq 19.4182) (TRUE_VALUE,EXECUTABLE)
    ;;                31 [33.3% (guessed)]  count:47721860 (estimated locally, 
freq 12.9455) (TRUE_VALUE,EXECUTABLE)
    # .MEM_22 = PHI <.MEM_44(22), .MEM_31(21), .MEM_79(31)>
    ivtmp.22_82 = ivtmp.22_41 + 1;
    ivtmp.26_72 = ivtmp.26_51 + _80;
    ivtmp.28_98 = ivtmp.28_90 + _39;
    
    These two IVs are always used as unsigned, so IV ops generates:
    
      _73 = stride.3_27 * 4;
      _80 = (unsigned long) _73;
      _54 = (unsigned long) stride.3_27;
      _39 = _54 * 4;
    
    Which means that in e.g. exchange2 we generate a lot of duplicate code.
    
    This is because candidate 6 and 8 are equivalent under two's complement but 
have
    different signs.
    
    This patch changes it so that if you have two IVs that are affine 
equivalent to
    just pick one over the other.  IV already has code for this, so the patch 
just
    uses affine trees instead of tree for the check.
    
    With it we get:
    
    <Invariant Expressions>:
    inv_expr 1:     stride.3_27 * 4
    
    <Group-candidate Costs>:
    Group 0:
      cand  cost    compl.  inv.expr.       inv.vars
      5     0       2       NIL;    NIL;
      6     0       3       NIL;    NIL;
    
    Group 1:
      cand  cost    compl.  inv.expr.       inv.vars
      1     0       0       NIL;    6
      2     0       0       NIL;    6
      3     0       0       NIL;    6
      4     0       0       NIL;    6
    
    Initial set of candidates:
      cost: 16 (complexity 3)
      reg_cost: 6
      cand_cost: 10
      cand_group_cost: 0 (complexity 3)
      candidates: 1, 6
       group:0 --> iv_cand:6, cost=(0,3)
       group:1 --> iv_cand:1, cost=(0,0)
      invariant variables: 6
      invariant expressions: 1
    
    gcc/ChangeLog:
    
            PR tree-optimization/114932
            * fold-const.cc (operand_compare::operand_equal_p): Use it.
            (operand_compare::verify_hash_value): Likewise.
            (operand_compare::hash_operand): Likewise.
            (test_operand_equality::test): New.
            (fold_const_cc_tests): Use it.
            * tree-core.h (enum operand_equal_flag): Add OEP_ASSUME_WRAPV.
            * tree-ssa-loop-ivopts.cc (record_group_use): Check for structural 
eq.
    
    gcc/testsuite/ChangeLog:
    
            PR tree-optimization/114932
            * gfortran.dg/addressing-modes_2.f90: New test.

Diff:
---
 gcc/fold-const.cc                                | 202 ++++++++++++++++++++---
 gcc/testsuite/gfortran.dg/addressing-modes_2.f90 |  20 +++
 gcc/tree-core.h                                  |   9 +-
 gcc/tree-ssa-loop-ivopts.cc                      |   4 +-
 4 files changed, 213 insertions(+), 22 deletions(-)

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 33dc3a731e45..98e6d41dc32a 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -3158,6 +3158,19 @@ combine_comparisons (location_t loc,
    like math-related flags or undefined behavior; only return true for
    values that are provably bitwise identical in all circumstances.
 
+   If OEP_ASSUME_WRAPV is set, then require the values to be bitwise identical
+   under two's compliment arithmetic (ignoring any possible Undefined 
Behaviour)
+   rather than just numerically equivalent.  The compared expressions must
+   however perform the same operations but may do intermediate computations in
+   differing signs.  Because this comparison ignores any possible UB it cannot
+   be used blindly without ensuring that the context you are using it in itself
+   doesn't guarantee that there will be no UB.  Conditional expressions are
+   excluded from this relaxation.
+
+   When OEP_ASSUME_WRAPV is used operand_compare::hash_operand may return
+   differing hashes even for cases where operand_compare::operand_equal_p
+   compares equal.
+
    Unless OEP_MATCH_SIDE_EFFECTS is set, the function returns false on
    any operand with side effect.  This is unnecesarily conservative in the
    case we know that arg0 and arg1 are in disjoint code paths (such as in
@@ -3172,8 +3185,10 @@ operand_compare::operand_equal_p (const_tree arg0, 
const_tree arg1,
   return operand_equal_p (TREE_TYPE (arg0), arg0, TREE_TYPE (arg1), arg1, 
flags);
 }
 
-/* The same as operand_equal_p however the type of ARG0 and ARG1 are assumed 
to be
-   the TYPE0 and TYPE1 respectively.  */
+/* The same as operand_equal_p however the type of ARG0 and ARG1 are assumed to
+   be the TYPE0 and TYPE1 respectively.  TYPE0 and TYPE1 represent the type the
+   expression is being compared under for equality.  This means that they can
+   differ from the actual TREE_TYPE (..) value of ARG0 and ARG1.  */
 
 bool
 operand_compare::operand_equal_p (tree type0, const_tree arg0,
@@ -3220,15 +3235,58 @@ operand_compare::operand_equal_p (tree type0, 
const_tree arg0,
       return tree_int_cst_equal (arg0, arg1);
     }
 
+  if ((flags & OEP_ASSUME_WRAPV)
+      && (CONVERT_EXPR_P (arg0) || CONVERT_EXPR_P (arg1)))
+    {
+      const_tree t_arg0 = arg0;
+      const_tree t_arg1 = arg1;
+      STRIP_NOPS (arg0);
+      STRIP_NOPS (arg1);
+      /* Only recurse if the conversion was one that was valid to strip.  */
+      if (t_arg0 != arg0 || t_arg1 != arg1)
+       return operand_equal_p (type0, arg0, type1, arg1, flags);
+    }
+
   if (!(flags & OEP_ADDRESS_OF))
     {
+      /* Check if we are checking an operation where the two's compliment
+        bitwise representation of the result is not the same between signed and
+        unsigned arithmetic.  */
+      bool enforce_signedness = true;
+      if (flags & OEP_ASSUME_WRAPV)
+       {
+         switch (TREE_CODE (arg0))
+           {
+           case PLUS_EXPR:
+           case MINUS_EXPR:
+           case MULT_EXPR:
+           case BIT_IOR_EXPR:
+           case BIT_XOR_EXPR:
+           case BIT_AND_EXPR:
+           case BIT_NOT_EXPR:
+           case ABS_EXPR:
+           CASE_CONVERT:
+           case SSA_NAME:
+           case INTEGER_CST:
+           case VAR_DECL:
+           case PARM_DECL:
+           case RESULT_DECL:
+             enforce_signedness = false;
+             break;
+
+           default:
+             break;
+           }
+       }
+
       /* If both types don't have the same signedness, then we can't consider
         them equal.  We must check this before the STRIP_NOPS calls
         because they may change the signedness of the arguments.  As pointers
         strictly don't have a signedness, require either two pointers or
         two non-pointers as well.  */
-      if (TYPE_UNSIGNED (type0) != TYPE_UNSIGNED (type1)
-         || POINTER_TYPE_P (type0) != POINTER_TYPE_P (type1))
+      if (POINTER_TYPE_P (type0) != POINTER_TYPE_P (type1)
+         || (TYPE_UNSIGNED (type0) != TYPE_UNSIGNED (type1)
+             && enforce_signedness))
        return false;
 
       /* If both types don't have the same precision, then it is not safe
@@ -3415,7 +3473,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
 
          /* Address of CONSTRUCTOR is defined in GENERIC to mean the value
             of the CONSTRUCTOR referenced indirectly.  */
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
 
          for (unsigned idx = 0; idx < vec_safe_length (v0); ++idx)
            {
@@ -3513,14 +3571,14 @@ operand_compare::operand_equal_p (tree type0, 
const_tree arg0,
              if (TYPE_MAIN_VARIANT (type0) != TYPE_MAIN_VARIANT (type1))
                return false;
            }
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
          return OP_SAME (0);
 
        case IMAGPART_EXPR:
          /* Require the same offset.  */
          if (!operand_equal_p (TYPE_SIZE (type0),
                                TYPE_SIZE (type1),
-                               flags & ~OEP_ADDRESS_OF))
+                               flags & ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV)))
            return false;
 
        /* Fallthru.  */
@@ -3556,7 +3614,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
             if (TYPE_ALIGN (type0) != TYPE_ALIGN (type1))
                return false;
            }
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
          return (OP_SAME (0) && OP_SAME (1)
                  /* TARGET_MEM_REF require equal extra operands.  */
                  && (TREE_CODE (arg0) != TARGET_MEM_REF
@@ -3568,7 +3626,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
        case ARRAY_RANGE_REF:
          if (!OP_SAME (0))
            return false;
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
          /* Compare the array index by value if it is constant first as we
             may have different types but same value here.  */
          return ((tree_int_cst_equal (TREE_OPERAND (arg0, 1),
@@ -3601,7 +3659,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
            /* Most of time we only need to compare FIELD_DECLs for equality.
               However when determining address look into actual offsets.
               These may match for unions and unshared record types.  */
-           flags &= ~OEP_ADDRESS_OF;
+           flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
            if (!OP_SAME (1))
              {
                if (compare_address
@@ -3635,7 +3693,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
        case BIT_FIELD_REF:
          if (!OP_SAME (0))
            return false;
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
          return OP_SAME (1) && OP_SAME (2);
 
        default:
@@ -3681,7 +3739,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
        case COND_EXPR:
          if (! OP_SAME (1) || ! OP_SAME_WITH_NULL (2))
            return false;
-         flags &= ~OEP_ADDRESS_OF;
+         flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
          return OP_SAME (0);
 
        case BIT_INSERT_EXPR:
@@ -3720,7 +3778,7 @@ operand_compare::operand_equal_p (tree type0, const_tree 
arg0,
        if (!operand_equal_p (OBJ_TYPE_REF_EXPR (arg0),
                              OBJ_TYPE_REF_EXPR (arg1), flags))
          return false;
-       flags &= ~OEP_ADDRESS_OF;
+       flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
        if (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg0))
            != tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg1)))
          return false;
@@ -4007,7 +4065,7 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
       {
        unsigned HOST_WIDE_INT idx;
        tree field, value;
-       flags &= ~OEP_ADDRESS_OF;
+       flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
        hstate.add_int (CONSTRUCTOR_NO_CLEARING (t));
        FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), idx, field, value)
          {
@@ -4121,7 +4179,7 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
            case INDIRECT_REF:
            case MEM_REF:
            case TARGET_MEM_REF:
-             flags &= ~OEP_ADDRESS_OF;
+             flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
              sflags = flags;
              break;
 
@@ -4139,11 +4197,11 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
            case ARRAY_REF:
            case ARRAY_RANGE_REF:
            case BIT_FIELD_REF:
-             sflags &= ~OEP_ADDRESS_OF;
+             sflags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
              break;
 
            case COND_EXPR:
-             flags &= ~OEP_ADDRESS_OF;
+             flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
              break;
 
            case WIDEN_MULT_PLUS_EXPR:
@@ -4173,7 +4231,7 @@ operand_compare::hash_operand (const_tree t, 
inchash::hash &hstate,
            case OBJ_TYPE_REF:
            /* Virtual table reference.  */
              inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags);
-             flags &= ~OEP_ADDRESS_OF;
+             flags &= ~(OEP_ADDRESS_OF | OEP_ASSUME_WRAPV);
              inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags);
              inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags);
              if (!virtual_method_call_p (t))
@@ -4235,7 +4293,7 @@ operand_compare::verify_hash_value (const_tree arg0, 
const_tree arg1,
     {
       if (operand_equal_p (arg0, arg1, flags | OEP_NO_HASH_CHECK))
        {
-         if (arg0 != arg1 && !(flags & OEP_DECL_NAME))
+         if (arg0 != arg1 && !(flags & (OEP_DECL_NAME | OEP_ASSUME_WRAPV)))
            {
              inchash::hash hstate0 (0), hstate1 (0);
              hash_operand (arg0, hstate0, flags | OEP_HASH_CHECK);
@@ -17474,6 +17532,111 @@ test_arithmetic_folding ()
                                   x);
 }
 
+namespace test_operand_equality {
+
+/* Verify structural equality.  */
+
+/* Execute fold_vec_perm_cst unit tests.  */
+
+static void
+test ()
+{
+  tree stype = integer_type_node;
+  tree utype = unsigned_type_node;
+  tree x = create_tmp_var_raw (stype, "x");
+  tree y = create_tmp_var_raw (stype, "y");
+  tree z = create_tmp_var_raw (stype, "z");
+  tree four = build_int_cst (stype, 4);
+  tree lhs1 = fold_build2 (PLUS_EXPR, stype, x, y);
+  tree rhs1 = fold_convert (stype,
+               fold_build2 (PLUS_EXPR, utype,
+                            fold_convert (utype, x),
+                            fold_convert (utype, y)));
+
+  /* (int)((unsigned x) + (unsigned y)) == x + y.  */
+  ASSERT_TRUE (operand_equal_p (lhs1, rhs1, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs1, rhs1, 0));
+
+  /* (int)(unsigned) x == x.  */
+  tree lhs2 = build1 (NOP_EXPR, stype,
+               build1 (NOP_EXPR, utype, x));
+  tree rhs2 = x;
+  ASSERT_TRUE (operand_equal_p (lhs2, rhs2, OEP_ASSUME_WRAPV));
+  ASSERT_TRUE (operand_equal_p (lhs2, rhs2, 0));
+
+  /* (unsigned x) + (unsigned y) == x + y.  */
+  tree lhs3 = lhs1;
+  tree rhs3 = fold_build2 (PLUS_EXPR, utype,
+                          fold_convert (utype, x),
+                          fold_convert (utype, y));
+  ASSERT_TRUE (operand_equal_p (lhs3, rhs3, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs3, rhs3, 0));
+
+  /* (unsigned x) / (unsigned y) == x / y.  */
+  tree lhs4 = fold_build2 (TRUNC_DIV_EXPR, stype, x, y);;
+  tree rhs4 = fold_build2 (TRUNC_DIV_EXPR, utype,
+                          fold_convert (utype, x),
+                          fold_convert (utype, y));
+  ASSERT_FALSE (operand_equal_p (lhs4, rhs4, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs4, rhs4, 0));
+
+  /* (long x) / 4 == (long)(x / 4).  */
+  tree lstype = long_long_integer_type_node;
+  tree lfour = build_int_cst (lstype, 4);
+  tree lhs5 = fold_build2 (TRUNC_DIV_EXPR, lstype,
+                          fold_build1 (VIEW_CONVERT_EXPR, lstype, x), lfour);
+  tree rhs5 = fold_build1 (VIEW_CONVERT_EXPR, lstype,
+                          fold_build2 (TRUNC_DIV_EXPR, stype, x, four));
+  ASSERT_FALSE (operand_equal_p (lhs5, rhs5, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs5, rhs5, 0));
+
+  /* (unsigned x) / 4 == x / 4.  */
+  tree lhs6 = fold_build2 (TRUNC_DIV_EXPR, stype, x, four);;
+  tree rhs6 = fold_build2 (TRUNC_DIV_EXPR, utype,
+                          fold_convert (utype, x),
+                          fold_convert (utype, four));
+  ASSERT_FALSE (operand_equal_p (lhs6, rhs6, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs6, rhs6, 0));
+
+  /* a / (int)((unsigned)b - (unsigned)c)) == a / (b - c).  */
+  tree lhs7 = fold_build2 (TRUNC_DIV_EXPR, stype, x, lhs1);
+  tree rhs7 = fold_build2 (TRUNC_DIV_EXPR, stype, x, rhs1);
+  ASSERT_TRUE (operand_equal_p (lhs7, rhs7, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs7, rhs7, 0));
+
+  /* (unsigned x) + 4 == x + 4.  */
+  tree lhs8 = fold_build2 (PLUS_EXPR, stype, x, four);
+  tree rhs8 = fold_build2 (PLUS_EXPR, utype,
+                          fold_convert (utype, x),
+                          fold_convert (utype, four));
+  ASSERT_TRUE (operand_equal_p (lhs8, rhs8, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs8, rhs8, 0));
+
+  /* (unsigned x) + 4 == 4 + x.  */
+  tree lhs9 = fold_build2 (PLUS_EXPR, stype, four, x);
+  tree rhs9 = fold_build2 (PLUS_EXPR, utype,
+                          fold_convert (utype, x),
+                          fold_convert (utype, four));
+  ASSERT_TRUE (operand_equal_p (lhs9, rhs9, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs9, rhs9, 0));
+
+  /* ((unsigned x) + 4) * (unsigned y)) + z == ((4 + x) * y) + z.  */
+  tree lhs10 = fold_build2 (PLUS_EXPR, stype,
+                          fold_build2 (MULT_EXPR, stype,
+                                       fold_build2 (PLUS_EXPR, stype, four, x),
+                                       y),
+                          z);
+  tree rhs10 = fold_build2 (MULT_EXPR, utype,
+                          fold_build2 (PLUS_EXPR, utype,
+                                       fold_convert (utype, x),
+                                       fold_convert (utype, four)),
+                          fold_convert (utype, y));
+  rhs10 = fold_build2 (PLUS_EXPR, stype, fold_convert (stype, rhs10), z);
+  ASSERT_TRUE (operand_equal_p (lhs10, rhs10, OEP_ASSUME_WRAPV));
+  ASSERT_FALSE (operand_equal_p (lhs10, rhs10, 0));
+}
+}
+
 namespace test_fold_vec_perm_cst {
 
 /* Build a VECTOR_CST corresponding to VMODE, and has
@@ -18267,6 +18430,7 @@ fold_const_cc_tests ()
   test_vector_folding ();
   test_vec_duplicate_folding ();
   test_fold_vec_perm_cst::test ();
+  test_operand_equality::test ();
 }
 
 } // namespace selftest
diff --git a/gcc/testsuite/gfortran.dg/addressing-modes_2.f90 
b/gcc/testsuite/gfortran.dg/addressing-modes_2.f90
new file mode 100644
index 000000000000..8eee4be3dc4d
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/addressing-modes_2.f90
@@ -0,0 +1,20 @@
+! { dg-do compile { target aarch64-*-* } }
+! { dg-additional-options "-w -Ofast -fdump-tree-ivopts-all" }
+
+module a
+integer, parameter :: b=3, c=b
+contains
+subroutine d(block)
+integer f, col   , block(:, :, :), e
+do f = 1, c
+   do col = 1, c
+             block(:f,                          :, e()) = do
+     end do
+  end do
+  end
+  end
+
+! { dg-final { scan-tree-dump-not {Selected IV set for loop .+ niters, 3 IVs:} 
ivopts } }
+! { dg-final { scan-tree-dump-times {Selected IV set for loop .+ niters, 2 
IVs:} 1 ivopts } }
+! { dg-final { scan-tree-dump-times {Selected IV set for loop .+ niters, 1 
IVs:} 1 ivopts } }
+
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 1776c154d94e..30bada4776d8 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1007,7 +1007,14 @@ enum operand_equal_flag {
   /* In conjunction with OEP_LEXICOGRAPHIC considers names of declarations
      of the same kind.  Used to compare VLA bounds involving parameters
      across redeclarations of the same function.  */
-  OEP_DECL_NAME = 512
+  OEP_DECL_NAME = 512,
+  /* Check if two expressions result in the same bit values while possibly
+     ignoring the sign of the expressions and any differences in undefined
+     behaviour.  The compared expressions must however perform the same
+     operations.  Because this comparison ignores any possible UB it cannot
+     be used blindly without ensuring that the context you are using it in
+     itself doesn't guarantee that there will be no UB.  */
+  OEP_ASSUME_WRAPV = 1024
 };
 
 /* Enum and arrays used for tree allocation stats.
diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc
index b38bc6971a8e..0f5dd089a0e6 100644
--- a/gcc/tree-ssa-loop-ivopts.cc
+++ b/gcc/tree-ssa-loop-ivopts.cc
@@ -1624,8 +1624,8 @@ record_group_use (struct ivopts_data *data, tree *use_p,
 
          /* Check if it has the same stripped base and step.  */
          if (operand_equal_p (iv->base_object, use->iv->base_object, 0)
-             && operand_equal_p (iv->step, use->iv->step, 0)
-             && operand_equal_p (addr_base, use->addr_base, 0))
+             && operand_equal_p (iv->step, use->iv->step, OEP_ASSUME_WRAPV)
+             && operand_equal_p (addr_base, use->addr_base, OEP_ASSUME_WRAPV))
            break;
        }
       if (i == data->vgroups.length ())

Reply via email to