On Wed, Mar 16, 2011 at 7:49 AM, Ira Rosen <ira.ro...@linaro.org> wrote:
> Hi,
>
> This patch adds a support of conditional store sinking for cases with
> multiple data references in then and else basic blocks. The
> correctness of the transformation is checked by verifying that there
> are no read-after-write and write-after-write dependencies.
>
> Bootstrapped and tested on powerpc64-suse-linux.
> OK for trunk?

I will look at the patch later, but can you add a testcase that we don't sink

  if (..)
   {
      *a = i;
      *b = j;
   }
  else
   {
      *b = j;
      *a = i;
   }

if *a and *b may alias?

Thanks,
Richard.

> Thanks,
> Ira
>
> ChangeLog:
>
>     * tree-data-ref.c (dr_equal_offsets_p1): Moved and renamed from
>     tree-vect-data-refs.c vect_equal_offsets.
>     (dr_equal_offsets_p): New function.
>     * tree-data-ref.h (dr_equal_offsets_p): Declare.
>     * tree-vect-data-refs.c (vect_equal_offsets): Move to tree-data-ref.c.
>     (vect_drs_dependent_in_basic_block): Update calls to vect_equal_offsets.
>     (vect_check_interleaving): Likewise.
>     * tree-ssa-phiopt.c: Include cfgloop.h and tree-data-ref.h.
>     (cond_if_else_store_replacement): Rename to...
>     (cond_if_else_store_replacement_1): ... this. Change arguments and
>     documentation.
>     (cond_if_else_store_replacement): New function.
>     * Makefile.in (tree-ssa-phiopt.o): Adjust dependencies.
>
> testsuite/ChangeLog:
>
>     * gcc.dg/vect/vect-cselim-1.c: New test.
>
> Index: tree-data-ref.c
> ===================================================================
> --- tree-data-ref.c     (revision 170712)
> +++ tree-data-ref.c     (working copy)
> @@ -991,6 +991,48 @@ create_data_ref (loop_p nest, loop_p loop, tree me
>    return dr;
>  }
>
> +/* Check if OFFSET1 and OFFSET2 (DR_OFFSETs of some data-refs) are identical
> +   expressions.  */
> +static bool
> +dr_equal_offsets_p1 (tree offset1, tree offset2)
> +{
> +  bool res;
> +
> +  STRIP_NOPS (offset1);
> +  STRIP_NOPS (offset2);
> +
> +  if (offset1 == offset2)
> +    return true;
> +
> +  if (TREE_CODE (offset1) != TREE_CODE (offset2)
> +      || (!BINARY_CLASS_P (offset1) && !UNARY_CLASS_P (offset1)))
> +    return false;
> +
> +  res = dr_equal_offsets_p1 (TREE_OPERAND (offset1, 0),
> +                             TREE_OPERAND (offset2, 0));
> +
> +  if (!res || !BINARY_CLASS_P (offset1))
> +    return res;
> +
> +  res = dr_equal_offsets_p1 (TREE_OPERAND (offset1, 1),
> +                             TREE_OPERAND (offset2, 1));
> +
> +  return res;
> +}
> +
> +/* Check if DRA and DRB have equal offsets.  */
> +bool
> +dr_equal_offsets_p (struct data_reference *dra,
> +                    struct data_reference *drb)
> +{
> +  tree offset1, offset2;
> +
> +  offset1 = DR_OFFSET (dra);
> +  offset2 = DR_OFFSET (drb);
> +
> +  return dr_equal_offsets_p1 (offset1, offset2);
> +}
> +
>  /* Returns true if FNA == FNB.  */
>
>  static bool
> Index: tree-data-ref.h
> ===================================================================
> --- tree-data-ref.h     (revision 170712)
> +++ tree-data-ref.h     (working copy)
> @@ -430,6 +430,8 @@ extern void compute_all_dependences (VEC (data_ref
>  extern void create_rdg_vertices (struct graph *, VEC (gimple, heap) *);
>  extern bool dr_may_alias_p (const struct data_reference *,
>                             const struct data_reference *);
> +extern bool dr_equal_offsets_p (struct data_reference *,
> +                                struct data_reference *);
>
>
>  /* Return true when the base objects of data references A and B are
> @@ -755,5 +757,4 @@ lambda_matrix_new (int m, int n, struct obstack *l
>
>    return mat;
>  }
> -
>  #endif  /* GCC_TREE_DATA_REF_H  */
> Index: tree-vect-data-refs.c
> ===================================================================
> --- tree-vect-data-refs.c       (revision 170712)
> +++ tree-vect-data-refs.c       (working copy)
> @@ -289,39 +289,6 @@ vect_update_interleaving_chain (struct data_refere
>      }
>  }
>
> -
> -/* Function vect_equal_offsets.
> -
> -   Check if OFFSET1 and OFFSET2 are identical expressions.  */
> -
> -static bool
> -vect_equal_offsets (tree offset1, tree offset2)
> -{
> -  bool res;
> -
> -  STRIP_NOPS (offset1);
> -  STRIP_NOPS (offset2);
> -
> -  if (offset1 == offset2)
> -    return true;
> -
> -  if (TREE_CODE (offset1) != TREE_CODE (offset2)
> -      || (!BINARY_CLASS_P (offset1) && !UNARY_CLASS_P (offset1)))
> -    return false;
> -
> -  res = vect_equal_offsets (TREE_OPERAND (offset1, 0),
> -                           TREE_OPERAND (offset2, 0));
> -
> -  if (!res || !BINARY_CLASS_P (offset1))
> -    return res;
> -
> -  res = vect_equal_offsets (TREE_OPERAND (offset1, 1),
> -                           TREE_OPERAND (offset2, 1));
> -
> -  return res;
> -}
> -
> -
>  /* Check dependence between DRA and DRB for basic block vectorization.
>     If the accesses share same bases and offsets, we can compare their initial
>     constant offsets to decide whether they differ or not.  In case of a read-
> @@ -352,7 +319,7 @@ vect_drs_dependent_in_basic_block (struct data_ref
>             || TREE_CODE (DR_BASE_ADDRESS (drb)) != ADDR_EXPR
>             || TREE_OPERAND (DR_BASE_ADDRESS (dra), 0)
>             != TREE_OPERAND (DR_BASE_ADDRESS (drb),0)))
> -      || !vect_equal_offsets (DR_OFFSET (dra), DR_OFFSET (drb)))
> +      || !dr_equal_offsets_p (dra, drb))
>      return true;
>
>    /* Check the types.  */
> @@ -402,7 +369,7 @@ vect_check_interleaving (struct data_reference *dr
>            || TREE_CODE (DR_BASE_ADDRESS (drb)) != ADDR_EXPR
>            || TREE_OPERAND (DR_BASE_ADDRESS (dra), 0)
>            != TREE_OPERAND (DR_BASE_ADDRESS (drb),0)))
> -      || !vect_equal_offsets (DR_OFFSET (dra), DR_OFFSET (drb))
> +      || !dr_equal_offsets_p (dra, drb)
>        || !tree_int_cst_compare (DR_INIT (dra), DR_INIT (drb))
>        || DR_IS_READ (dra) != DR_IS_READ (drb))
>      return false;
> Index: tree-ssa-phiopt.c
> ===================================================================
> --- tree-ssa-phiopt.c   (revision 170734)
> +++ tree-ssa-phiopt.c   (working copy)
> @@ -34,6 +34,8 @@ along with GCC; see the file COPYING3.  If not see
>  #include "langhooks.h"
>  #include "pointer-set.h"
>  #include "domwalk.h"
> +#include "cfgloop.h"
> +#include "tree-data-ref.h"
>
>  static unsigned int tree_ssa_phiopt (void);
>  static unsigned int tree_ssa_phiopt_worker (bool);
> @@ -1292,35 +1294,18 @@ cond_store_replacement (basic_block middle_bb, bas
>    return true;
>  }
>
> -/* Do the main work of conditional store replacement.  We already know
> -   that the recognized pattern looks like so:
> +/* Do the main work of conditional store replacement.  */
>
> -   split:
> -     if (cond) goto THEN_BB; else goto ELSE_BB (edge E1)
> -   THEN_BB:
> -     X = Y;
> -     goto JOIN_BB;
> -   ELSE_BB:
> -     X = Z;
> -     fallthrough (edge E0)
> -   JOIN_BB:
> -     some more
> -
> -   We check that THEN_BB and ELSE_BB contain only one store
> -   that the stores have a "simple" RHS.  */
> -
>  static bool
> -cond_if_else_store_replacement (basic_block then_bb, basic_block else_bb,
> -                               basic_block join_bb)
> +cond_if_else_store_replacement_1 (basic_block then_bb, basic_block else_bb,
> +                                 basic_block join_bb, gimple then_assign,
> +                                 gimple else_assign)
>  {
> -  gimple then_assign = last_and_only_stmt (then_bb);
> -  gimple else_assign = last_and_only_stmt (else_bb);
>    tree lhs_base, lhs, then_rhs, else_rhs;
>    source_location then_locus, else_locus;
>    gimple_stmt_iterator gsi;
>    gimple newphi, new_stmt;
>
> -  /* Check if then_bb and else_bb contain only one store each.  */
>    if (then_assign == NULL
>        || !gimple_assign_single_p (then_assign)
>        || else_assign == NULL
> @@ -1385,6 +1370,151 @@ static bool
>    return true;
>  }
>
> +/* Conditional store replacement.  We already know
> +   that the recognized pattern looks like so:
> +
> +   split:
> +     if (cond) goto THEN_BB; else goto ELSE_BB (edge E1)
> +   THEN_BB:
> +     ...
> +     X = Y;
> +     ...
> +     goto JOIN_BB;
> +   ELSE_BB:
> +     ...
> +     X = Z;
> +     ...
> +     fallthrough (edge E0)
> +   JOIN_BB:
> +     some more
> +
> +   We check that it is safe to sink the store to JOIN_BB by verifying that
> +   there are no read-after-write or write-after-write dependencies in
> +   THEN_BB and ELSE_BB.  */
> +
> +static bool
> +cond_if_else_store_replacement (basic_block then_bb, basic_block else_bb,
> +                                basic_block join_bb)
> +{
> +  gimple then_assign = last_and_only_stmt (then_bb);
> +  gimple else_assign = last_and_only_stmt (else_bb);
> +  VEC (data_reference_p, heap) *then_datarefs, *else_datarefs;
> +  VEC (ddr_p, heap) *then_ddrs, *else_ddrs;
> +  gimple then_store, else_store;
> +  bool found, ok = false, res;
> +  struct data_dependence_relation *ddr;
> +  data_reference_p then_dr, else_dr;
> +  int i, j;
> +  tree then_lhs, else_lhs;
> +
> +  /* Handle the case with single statement in THEN_BB and ELSE_BB.  */
> +  if (then_assign && else_assign)
> +    return cond_if_else_store_replacement_1 (then_bb, else_bb, join_bb,
> +                                             then_assign, else_assign);
> +
> +  then_datarefs = VEC_alloc (data_reference_p, heap, 1);
> +  else_datarefs = VEC_alloc (data_reference_p, heap, 1);
> +  then_ddrs = VEC_alloc (ddr_p, heap, 1);
> +  else_ddrs = VEC_alloc (ddr_p, heap, 1);
> +  if (!compute_data_dependences_for_bb (then_bb, false, &then_datarefs,
> +                                        &then_ddrs)
> +      || !compute_data_dependences_for_bb (else_bb, false, &else_datarefs,
> +                                           &else_ddrs)
> +      || !VEC_length (data_reference_p, then_datarefs)
> +      || !VEC_length (data_reference_p, else_datarefs))
> +    {
> +      free_data_refs (then_datarefs);
> +      free_data_refs (else_datarefs);
> +      free_dependence_relations (then_ddrs);
> +      free_dependence_relations (else_ddrs);
> +      return false;
> +    }
> +
> +  /* Check that there are no read-after-write or write-after-write 
> dependencies
> +     in THEN_BB.  */
> +  FOR_EACH_VEC_ELT (ddr_p, then_ddrs, i, ddr)
> +    {
> +      struct data_reference *dra = DDR_A (ddr);
> +      struct data_reference *drb = DDR_B (ddr);
> +
> +      if (DDR_ARE_DEPENDENT (ddr) != chrec_known
> +          && ((DR_IS_READ (dra) && DR_IS_WRITE (drb)
> +               && gimple_uid (DR_STMT (dra)) > gimple_uid (DR_STMT (drb)))
> +              || (DR_IS_READ (drb) && DR_IS_WRITE (dra)
> +                  && gimple_uid (DR_STMT (drb)) > gimple_uid (DR_STMT (dra)))
> +              || (DR_IS_WRITE (dra) && DR_IS_WRITE (drb))))
> +        {
> +          free_data_refs (then_datarefs);
> +          free_data_refs (else_datarefs);
> +          free_dependence_relations (then_ddrs);
> +          free_dependence_relations (else_ddrs);
> +          return false;
> +        }
> +    }
> +
> +  /* Check that there are no read-after-write or write-after-write 
> dependencies
> +     in ELSE_BB.  */
> +  FOR_EACH_VEC_ELT (ddr_p, else_ddrs, i, ddr)
> +    {
> +      struct data_reference *dra = DDR_A (ddr);
> +      struct data_reference *drb = DDR_B (ddr);
> +
> +      if (DDR_ARE_DEPENDENT (ddr) != chrec_known
> +          && ((DR_IS_READ (dra) && DR_IS_WRITE (drb)
> +               && gimple_uid (DR_STMT (dra)) > gimple_uid (DR_STMT (drb)))
> +              || (DR_IS_READ (drb) && DR_IS_WRITE (dra)
> +                  && gimple_uid (DR_STMT (drb)) > gimple_uid (DR_STMT (dra)))
> +              || (DR_IS_WRITE (dra) && DR_IS_WRITE (drb))))
> +        {
> +          free_data_refs (then_datarefs);
> +          free_data_refs (else_datarefs);
> +          free_dependence_relations (then_ddrs);
> +          free_dependence_relations (else_ddrs);
> +          return false;
> +        }
> +    }
> +
> +  /* Found pairs of stores with equal LHS.  */
> +  FOR_EACH_VEC_ELT (data_reference_p, then_datarefs, i, then_dr)
> +    {
> +      if (DR_IS_READ (then_dr))
> +        continue;
> +
> +      then_store = DR_STMT (then_dr);
> +      then_lhs = gimple_assign_lhs (then_store);
> +      found = false;
> +
> +      FOR_EACH_VEC_ELT (data_reference_p, else_datarefs, j, else_dr)
> +        {
> +          if (DR_IS_READ (else_dr))
> +            continue;
> +
> +          else_store = DR_STMT (else_dr);
> +          else_lhs = gimple_assign_lhs (else_store);
> +
> +          if (operand_equal_p (then_lhs, else_lhs, 0))
> +            {
> +              found = true;
> +              break;
> +            }
> +        }
> +
> +      if (!found)
> +        continue;
> +
> +      res = cond_if_else_store_replacement_1 (then_bb, else_bb, join_bb,
> +                                              then_store, else_store);
> +      ok = ok || res;
> +    }
> +
> +  free_data_refs (then_datarefs);
> +  free_data_refs (else_datarefs);
> +  free_dependence_relations (then_ddrs);
> +  free_dependence_relations (else_ddrs);
> +
> +  return ok;
> +}
> +
>  /* Always do these optimizations if we have SSA
>     trees to work on.  */
>  static bool
> Index: Makefile.in
> ===================================================================
> --- Makefile.in (revision 170712)
> +++ Makefile.in (working copy)
> @@ -2422,7 +2422,8 @@ tree-ssa-ifcombine.o : tree-ssa-ifcombine.c $(CONF
>  tree-ssa-phiopt.o : tree-ssa-phiopt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
>     $(TM_H) $(GGC_H) $(TREE_H) $(TM_P_H) $(BASIC_BLOCK_H) \
>     $(TREE_FLOW_H) $(TREE_PASS_H) $(TREE_DUMP_H) langhooks.h $(FLAGS_H) \
> -   $(DIAGNOSTIC_H) $(TIMEVAR_H) pointer-set.h domwalk.h
> +   $(DIAGNOSTIC_H) $(TIMEVAR_H) pointer-set.h domwalk.h $(CFGLOOP_H) \
> +   $(TREE_DATA_REF_H)
>  tree-nrv.o : tree-nrv.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
>     $(TM_H) $(TREE_H) $(FUNCTION_H) $(BASIC_BLOCK_H) $(FLAGS_H) \
>     $(DIAGNOSTIC_H) $(TREE_FLOW_H) $(TIMEVAR_H) $(TREE_DUMP_H) $(TREE_PASS_H) 
> \
> Index: testsuite/gcc.dg/vect/vect-cselim-1.c
> ===================================================================
> --- testsuite/gcc.dg/vect/vect-cselim-1.c       (revision 0)
> +++ testsuite/gcc.dg/vect/vect-cselim-1.c       (revision 0)
> @@ -0,0 +1,86 @@
> +/* { dg-require-effective-target vect_int } */
> +
> +#include <stdarg.h>
> +#include "tree-vect.h"
> +
> +#define N 50
> +
> +typedef struct {
> +  short a;
> +  short b;
> +} data;
> +
> +data in1[N], in2[N], out[N];
> +short result[N*2] =
> {7,-7,9,-6,11,-5,13,-4,15,-3,17,-2,19,-1,21,0,23,1,25,2,27,3,29,4,31,5,33,6,35,7,37,8,39,9,41,10,43,11,45,12,47,13,49,14,51,15,53,16,55,17,57,18,59,19,61,20,63,21,65,22,67,23,69,24,71,25,73,26,75,27,77,28,79,29,81,30,83,31,85,32,87,33,89,34,91,35,93,36,95,37,97,38,99,39,101,40,103,41,105,42};
> +short out1[N], out2[N];
> +
> +__attribute__ ((noinline)) void
> +foo ()
> +{
> +  int i;
> +  short c, d;
> +
> +  /* Vectorizable with conditional store sinking.  */
> +  for (i = 0; i < N; i++)
> +    {
> +      c = in1[i].b;
> +      d = in2[i].b;
> +
> +      if (c >= d)
> +        {
> +          out[i].b = c;
> +          out[i].a = d + 5;
> +        }
> +      else
> +        {
> +          out[i].b = d - 12;
> +          out[i].a = c + d;
> +        }
> +    }
> +
> +  /* Not vectorizable.  */
> +  for (i = 0; i < N; i++)
> +    {
> +      c = in1[i].b;
> +      d = in2[i].b;
> +
> +      if (c >= d)
> +        {
> +          out1[i] = c;
> +        }
> +      else
> +        {
> +          out2[i] = c + d;
> +        }
> +    }
> +}
> +
> +int
> +main (void)
> +{
> +  int i;
> +
> +  check_vect ();
> +
> +  for (i = 0; i < N; i++)
> +    {
> +      in1[i].a = i;
> +      in1[i].b = i + 2;
> +      in2[i].a = 5;
> +      in2[i].b = i + 5;
> +      __asm__ volatile ("");
> +    }
> +
> +  foo ();
> +
> +  for (i = 0; i < N; i++)
> +    {
> +      if (out[i].a != result[2*i] || out[i].b != result[2*i+1])
> +        abort ();
> +    }
> +
> +  return 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect"  } } */
> +/* { dg-final { cleanup-tree-dump "vect" } } */
>

Reply via email to