Add flags to dr_with_seg_len_pair_t
authorRichard Sandiford <richard.sandiford@arm.com>
Sat, 16 Nov 2019 11:40:22 +0000 (11:40 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 16 Nov 2019 11:40:22 +0000 (11:40 +0000)
This patch adds a bunch of flags to dr_with_seg_len_pair_t,
for use by later patches.  The update to tree-loop-distribution.c
is conservatively correct, but might be tweakable later.

2019-11-16  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
* tree-data-ref.h (DR_ALIAS_RAW, DR_ALIAS_WAR, DR_ALIAS_WAW)
(DR_ALIAS_ARBITRARY, DR_ALIAS_SWAPPED, DR_ALIAS_UNSWAPPED): New flags.
(dr_with_seg_len_pair_t::sequencing): New enum.
(dr_with_seg_len_pair_t::flags): New member variable.
(dr_with_seg_len_pair_t::dr_with_seg_len_pair_t): Take a sequencing
parameter and initialize the flags member variable.
* tree-loop-distribution.c (compute_alias_check_pairs): Update
call accordingly.
* tree-vect-data-refs.c (vect_prune_runtime_alias_test_list): Likewise.
Ensure the two data references in an alias pair are in statement
order, if there is a defined order.
* tree-data-ref.c (prune_runtime_alias_test_list): Use
DR_ALIAS_SWAPPED and DR_ALIAS_UNSWAPPED to record whether we've
swapped the references in a dr_with_seg_len_pair_t.  OR together
the flags when merging two dr_with_seg_len_pair_ts.  After merging,
try to restore the original dr_with_seg_len order, updating the
flags if that fails.

From-SVN: r278350

gcc/ChangeLog
gcc/tree-data-ref.c
gcc/tree-data-ref.h
gcc/tree-loop-distribution.c
gcc/tree-vect-data-refs.c

index 572eaeb3e249a062bd9f234bc768c3d5b7dc9bdd..e3ece31b2bf0343d9beeb80490fdb918f940fdbd 100644 (file)
@@ -1,3 +1,23 @@
+2019-11-16  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * tree-data-ref.h (DR_ALIAS_RAW, DR_ALIAS_WAR, DR_ALIAS_WAW)
+       (DR_ALIAS_ARBITRARY, DR_ALIAS_SWAPPED, DR_ALIAS_UNSWAPPED): New flags.
+       (dr_with_seg_len_pair_t::sequencing): New enum.
+       (dr_with_seg_len_pair_t::flags): New member variable.
+       (dr_with_seg_len_pair_t::dr_with_seg_len_pair_t): Take a sequencing
+       parameter and initialize the flags member variable.
+       * tree-loop-distribution.c (compute_alias_check_pairs): Update
+       call accordingly.
+       * tree-vect-data-refs.c (vect_prune_runtime_alias_test_list): Likewise.
+       Ensure the two data references in an alias pair are in statement
+       order, if there is a defined order.
+       * tree-data-ref.c (prune_runtime_alias_test_list): Use
+       DR_ALIAS_SWAPPED and DR_ALIAS_UNSWAPPED to record whether we've
+       swapped the references in a dr_with_seg_len_pair_t.  OR together
+       the flags when merging two dr_with_seg_len_pair_ts.  After merging,
+       try to restore the original dr_with_seg_len order, updating the
+       flags if that fails.
+
 2019-11-16  Richard Sandiford  <richard.sandiford@arm.com>
 
        * tree-data-ref.c (prune_runtime_alias_test_list): Delay
index cf4fb26e5375346b896d1bf8c25be6f8146e8262..21cd2ad7bb04b80e9ddd2820088bc3e5fff9febb 100644 (file)
@@ -1502,7 +1502,12 @@ prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *alias_pairs,
       if (comp_res == 0)
        comp_res = data_ref_compare_tree (DR_INIT (dr_a), DR_INIT (dr_b));
       if (comp_res > 0)
-       std::swap (alias_pair->first, alias_pair->second);
+       {
+         std::swap (alias_pair->first, alias_pair->second);
+         alias_pair->flags |= DR_ALIAS_SWAPPED;
+       }
+      else
+       alias_pair->flags |= DR_ALIAS_UNSWAPPED;
     }
 
   /* Sort the collected data ref pairs so that we can scan them once to
@@ -1514,10 +1519,13 @@ prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *alias_pairs,
   for (i = 1; i < alias_pairs->length (); ++i)
     {
       /* Deal with two ddrs (dr_a1, dr_b1) and (dr_a2, dr_b2).  */
-      dr_with_seg_len *dr_a1 = &(*alias_pairs)[i-1].first,
-                     *dr_b1 = &(*alias_pairs)[i-1].second,
-                     *dr_a2 = &(*alias_pairs)[i].first,
-                     *dr_b2 = &(*alias_pairs)[i].second;
+      dr_with_seg_len_pair_t *alias_pair1 = &(*alias_pairs)[i - 1];
+      dr_with_seg_len_pair_t *alias_pair2 = &(*alias_pairs)[i];
+
+      dr_with_seg_len *dr_a1 = &alias_pair1->first;
+      dr_with_seg_len *dr_b1 = &alias_pair1->second;
+      dr_with_seg_len *dr_a2 = &alias_pair2->first;
+      dr_with_seg_len *dr_b2 = &alias_pair2->second;
 
       /* Remove duplicate data ref pairs.  */
       if (*dr_a1 == *dr_a2 && *dr_b1 == *dr_b2)
@@ -1526,6 +1534,7 @@ prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *alias_pairs,
            dump_printf (MSG_NOTE, "found equal ranges %T, %T and %T, %T\n",
                         DR_REF (dr_a1->dr), DR_REF (dr_b1->dr),
                         DR_REF (dr_a2->dr), DR_REF (dr_b2->dr));
+         alias_pair1->flags |= alias_pair2->flags;
          alias_pairs->ordered_remove (i--);
          continue;
        }
@@ -1631,10 +1640,26 @@ prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *alias_pairs,
            dump_printf (MSG_NOTE, "merging ranges for %T, %T and %T, %T\n",
                         DR_REF (dr_a1->dr), DR_REF (dr_b1->dr),
                         DR_REF (dr_a2->dr), DR_REF (dr_b2->dr));
+         alias_pair1->flags |= alias_pair2->flags;
          alias_pairs->ordered_remove (i);
          i--;
        }
     }
+
+  /* Try to restore the original dr_with_seg_len order within each
+     dr_with_seg_len_pair_t.  If we ended up combining swapped and
+     unswapped pairs into the same check, we have to invalidate any
+     RAW, WAR and WAW information for it.  */
+  FOR_EACH_VEC_ELT (*alias_pairs, i, alias_pair)
+    {
+      unsigned int swap_mask = (DR_ALIAS_SWAPPED | DR_ALIAS_UNSWAPPED);
+      unsigned int swapped = (alias_pair->flags & swap_mask);
+      if (swapped == DR_ALIAS_SWAPPED)
+       std::swap (alias_pair->first, alias_pair->second);
+      else if (swapped != DR_ALIAS_UNSWAPPED)
+       alias_pair->flags |= DR_ALIAS_ARBITRARY;
+      alias_pair->flags &= ~swap_mask;
+    }
 }
 
 /* Given LOOP's two data references and segment lengths described by DR_A
index 998937fef6844c9e495b33e345311589c8427e79..284672425f690e2f98aa4126bee1f824e741f0ba 100644 (file)
@@ -222,20 +222,107 @@ public:
   unsigned int align;
 };
 
+/* Flags that describe a potential alias between two dr_with_seg_lens.
+   In general, each pair of dr_with_seg_lens represents a composite of
+   multiple access pairs P, so testing flags like DR_IS_READ on the DRs
+   does not give meaningful information.
+
+   DR_ALIAS_RAW:
+       There is a pair in P for which the second reference is a read
+       and the first is a write.
+
+   DR_ALIAS_WAR:
+       There is a pair in P for which the second reference is a write
+       and the first is a read.
+
+   DR_ALIAS_WAW:
+       There is a pair in P for which both references are writes.
+
+   DR_ALIAS_ARBITRARY:
+       Either
+       (a) it isn't possible to classify one pair in P as RAW, WAW or WAR; or
+       (b) there is a pair in P that breaks the ordering assumption below.
+
+       This flag overrides the RAW, WAR and WAW flags above.
+
+   DR_ALIAS_UNSWAPPED:
+   DR_ALIAS_SWAPPED:
+       Temporary flags that indicate whether there is a pair P whose
+       DRs have or haven't been swapped around.
+
+   The ordering assumption mentioned above is that for every pair
+   (DR_A, DR_B) in P:
+
+   (1) The original code accesses n elements for DR_A and n elements for DR_B,
+       interleaved as follows:
+
+        one access of size DR_A.access_size at DR_A.dr
+        one access of size DR_B.access_size at DR_B.dr
+        one access of size DR_A.access_size at DR_A.dr + STEP_A
+        one access of size DR_B.access_size at DR_B.dr + STEP_B
+        one access of size DR_A.access_size at DR_A.dr + STEP_A * 2
+        one access of size DR_B.access_size at DR_B.dr + STEP_B * 2
+        ...
+
+   (2) The new code accesses the same data in exactly two chunks:
+
+        one group of accesses spanning |DR_A.seg_len| + DR_A.access_size
+        one group of accesses spanning |DR_B.seg_len| + DR_B.access_size
+
+   A pair might break this assumption if the DR_A and DR_B accesses
+   in the original or the new code are mingled in some way.  For example,
+   if DR_A.access_size represents the effect of two individual writes
+   to nearby locations, the pair breaks the assumption if those writes
+   occur either side of the access for DR_B.
+
+   Note that DR_ALIAS_ARBITRARY describes whether the ordering assumption
+   fails to hold for any individual pair in P.  If the assumption *does*
+   hold for every pair in P, it doesn't matter whether it holds for the
+   composite pair or not.  In other words, P should represent the complete
+   set of pairs that the composite pair is testing, so only the ordering
+   of two accesses in the same member of P matters.  */
+const unsigned int DR_ALIAS_RAW = 1U << 0;
+const unsigned int DR_ALIAS_WAR = 1U << 1;
+const unsigned int DR_ALIAS_WAW = 1U << 2;
+const unsigned int DR_ALIAS_ARBITRARY = 1U << 3;
+const unsigned int DR_ALIAS_SWAPPED = 1U << 4;
+const unsigned int DR_ALIAS_UNSWAPPED = 1U << 5;
+
 /* This struct contains two dr_with_seg_len objects with aliasing data
    refs.  Two comparisons are generated from them.  */
 
 class dr_with_seg_len_pair_t
 {
 public:
-  dr_with_seg_len_pair_t (const dr_with_seg_len& d1,
-                              const dr_with_seg_len& d2)
-    : first (d1), second (d2) {}
+  /* WELL_ORDERED indicates that the ordering assumption described above
+     DR_ALIAS_ARBITRARY holds.  REORDERED indicates that it doesn't.  */
+  enum sequencing { WELL_ORDERED, REORDERED };
+
+  dr_with_seg_len_pair_t (const dr_with_seg_len &,
+                         const dr_with_seg_len &, sequencing);
 
   dr_with_seg_len first;
   dr_with_seg_len second;
+  unsigned int flags;
 };
 
+inline dr_with_seg_len_pair_t::
+dr_with_seg_len_pair_t (const dr_with_seg_len &d1, const dr_with_seg_len &d2,
+                       sequencing seq)
+  : first (d1), second (d2), flags (0)
+{
+  if (DR_IS_READ (d1.dr) && DR_IS_WRITE (d2.dr))
+    flags |= DR_ALIAS_WAR;
+  else if (DR_IS_WRITE (d1.dr) && DR_IS_READ (d2.dr))
+    flags |= DR_ALIAS_RAW;
+  else if (DR_IS_WRITE (d1.dr) && DR_IS_WRITE (d2.dr))
+    flags |= DR_ALIAS_WAW;
+  else
+    gcc_unreachable ();
+  if (seq == REORDERED)
+    flags |= DR_ALIAS_ARBITRARY;
+}
+
 enum data_dependence_direction {
   dir_positive,
   dir_negative,
index 29f8eab59a9e68fb364b03a2d1c9a5cdfd279690..28eeeb93174b9fafbfb7d24fb1007237cbd553bc 100644 (file)
@@ -2476,7 +2476,9 @@ compute_alias_check_pairs (class loop *loop, vec<ddr_p> *alias_ddrs,
 
       dr_with_seg_len_pair_t dr_with_seg_len_pair
        (dr_with_seg_len (dr_a, seg_length_a, access_size_a, align_a),
-        dr_with_seg_len (dr_b, seg_length_b, access_size_b, align_b));
+        dr_with_seg_len (dr_b, seg_length_b, access_size_b, align_b),
+        /* ??? Would WELL_ORDERED be safe?  */
+        dr_with_seg_len_pair_t::REORDERED);
 
       comp_alias_pairs->safe_push (dr_with_seg_len_pair);
     }
index c4d627acb2e5ce1c838e52d4ba111d0466f0979d..72a70945f086191673175cd8384d23405b20fbd7 100644 (file)
@@ -3508,10 +3508,13 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
       dr_vec_info *dr_info_b = loop_vinfo->lookup_dr (DDR_B (ddr));
       stmt_vec_info stmt_info_b = dr_info_b->stmt;
 
+      bool preserves_scalar_order_p
+       = vect_preserves_scalar_order_p (dr_info_a, dr_info_b);
+
       /* Skip the pair if inter-iteration dependencies are irrelevant
         and intra-iteration dependencies are guaranteed to be honored.  */
       if (ignore_step_p
-         && (vect_preserves_scalar_order_p (dr_info_a, dr_info_b)
+         && (preserves_scalar_order_p
              || vectorizable_with_step_bound_p (dr_info_a, dr_info_b,
                                                 &lower_bound)))
        {
@@ -3629,11 +3632,21 @@ vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
                                           stmt_info_b->stmt);
        }
 
+      dr_with_seg_len dr_a (dr_info_a->dr, segment_length_a,
+                           access_size_a, align_a);
+      dr_with_seg_len dr_b (dr_info_b->dr, segment_length_b,
+                           access_size_b, align_b);
+      /* Canonicalize the order to be the one that's needed for accurate
+        RAW, WAR and WAW flags, in cases where the data references are
+        well-ordered.  The order doesn't really matter otherwise,
+        but we might as well be consistent.  */
+      if (get_later_stmt (stmt_info_a, stmt_info_b) == stmt_info_a)
+       std::swap (dr_a, dr_b);
+
       dr_with_seg_len_pair_t dr_with_seg_len_pair
-       (dr_with_seg_len (dr_info_a->dr, segment_length_a,
-                         access_size_a, align_a),
-        dr_with_seg_len (dr_info_b->dr, segment_length_b,
-                         access_size_b, align_b));
+       (dr_a, dr_b, (preserves_scalar_order_p
+                     ? dr_with_seg_len_pair_t::WELL_ORDERED
+                     : dr_with_seg_len_pair_t::REORDERED));
 
       comp_alias_ddrs.safe_push (dr_with_seg_len_pair);
     }