diff mbox series

[v4,1/4] Match: Add interface match_cond_with_binary_phi for true/false arg

Message ID 20240912224139.801903-1-pan2.li@intel.com
State New
Headers show
Series [v4,1/4] Match: Add interface match_cond_with_binary_phi for true/false arg | expand

Commit Message

Li, Pan2 Sept. 12, 2024, 10:41 p.m. UTC
From: Pan Li <pan2.li@intel.com>

When matching the cond with 2 args phi node, we need to figure out
which arg of phi node comes from the true edge of cond block, as
well as the false edge.  This patch would like to add interface
to perform the action and return the true and false arg in TREE type.

There will be some additional handling if one of the arg is INTEGER_CST.
Because the INTEGER_CST args may have no source block, thus its' edge
source points to the condition block.  See below example in line 31,
the 255 INTEGER_CST has block 2 as source.  Thus, we need to find
the non-INTEGER_CST (aka _1) to tell which one is the true/false edge.
For example, the _1(3) takes block 3 as source, which is the dest
of false edge of the condition block.

   4   │ __attribute__((noinline))
   5   │ uint8_t sat_u_add_imm_type_check_uint8_t_fmt_2 (uint8_t x)
   6   │ {
   7   │   unsigned char _1;
   8   │   unsigned char _2;
   9   │   uint8_t _3;
  10   │   __complex__ unsigned char _5;
  11   │
  12   │ ;;   basic block 2, loop depth 0
  13   │ ;;    pred:       ENTRY
  14   │   _5 = .ADD_OVERFLOW (x_4(D), 9);
  15   │   _2 = IMAGPART_EXPR <_5>;
  16   │   if (_2 != 0)
  17   │     goto <bb 4>; [35.00%]
  18   │   else
  19   │     goto <bb 3>; [65.00%]
  20   │ ;;    succ:       3
  21   │ ;;                4
  22   │
  23   │ ;;   basic block 3, loop depth 0
  24   │ ;;    pred:       2
  25   │   _1 = REALPART_EXPR <_5>;
  26   │ ;;    succ:       4
  27   │
  28   │ ;;   basic block 4, loop depth 0
  29   │ ;;    pred:       2
  30   │ ;;                3
  31   │   # _3 = PHI <255(2), _1(3)>
  32   │   return _3;
  33   │ ;;    succ:       EXIT
  34   │
  35   │ }

The below test suites are passed for this patch.
* The rv64gcv fully regression test.
* The x86 bootstrap test.
* The x86 fully regression test.

gcc/ChangeLog:

	* gimple-match-head.cc (match_cond_with_binary_phi): Add new func
	impl to match binary phi for true and false arg.

Signed-off-by: Pan Li <pan2.li@intel.com>
---
 gcc/gimple-match-head.cc | 118 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)

Comments

Richard Biener Sept. 18, 2024, 12:02 p.m. UTC | #1
On Fri, Sep 13, 2024 at 12:42 AM <pan2.li@intel.com> wrote:
>
> From: Pan Li <pan2.li@intel.com>
>
> When matching the cond with 2 args phi node, we need to figure out
> which arg of phi node comes from the true edge of cond block, as
> well as the false edge.  This patch would like to add interface
> to perform the action and return the true and false arg in TREE type.
>
> There will be some additional handling if one of the arg is INTEGER_CST.
> Because the INTEGER_CST args may have no source block, thus its' edge
> source points to the condition block.  See below example in line 31,
> the 255 INTEGER_CST has block 2 as source.  Thus, we need to find
> the non-INTEGER_CST (aka _1) to tell which one is the true/false edge.
> For example, the _1(3) takes block 3 as source, which is the dest
> of false edge of the condition block.
>
>    4   │ __attribute__((noinline))
>    5   │ uint8_t sat_u_add_imm_type_check_uint8_t_fmt_2 (uint8_t x)
>    6   │ {
>    7   │   unsigned char _1;
>    8   │   unsigned char _2;
>    9   │   uint8_t _3;
>   10   │   __complex__ unsigned char _5;
>   11   │
>   12   │ ;;   basic block 2, loop depth 0
>   13   │ ;;    pred:       ENTRY
>   14   │   _5 = .ADD_OVERFLOW (x_4(D), 9);
>   15   │   _2 = IMAGPART_EXPR <_5>;
>   16   │   if (_2 != 0)
>   17   │     goto <bb 4>; [35.00%]
>   18   │   else
>   19   │     goto <bb 3>; [65.00%]
>   20   │ ;;    succ:       3
>   21   │ ;;                4
>   22   │
>   23   │ ;;   basic block 3, loop depth 0
>   24   │ ;;    pred:       2
>   25   │   _1 = REALPART_EXPR <_5>;
>   26   │ ;;    succ:       4
>   27   │
>   28   │ ;;   basic block 4, loop depth 0
>   29   │ ;;    pred:       2
>   30   │ ;;                3
>   31   │   # _3 = PHI <255(2), _1(3)>
>   32   │   return _3;
>   33   │ ;;    succ:       EXIT
>   34   │
>   35   │ }
>
> The below test suites are passed for this patch.
> * The rv64gcv fully regression test.
> * The x86 bootstrap test.
> * The x86 fully regression test.
>
> gcc/ChangeLog:
>
>         * gimple-match-head.cc (match_cond_with_binary_phi): Add new func
>         impl to match binary phi for true and false arg.
>
> Signed-off-by: Pan Li <pan2.li@intel.com>
> ---
>  gcc/gimple-match-head.cc | 118 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 118 insertions(+)
>
> diff --git a/gcc/gimple-match-head.cc b/gcc/gimple-match-head.cc
> index 924d3f1e710..6e7a3a0d62e 100644
> --- a/gcc/gimple-match-head.cc
> +++ b/gcc/gimple-match-head.cc
> @@ -375,3 +375,121 @@ gimple_bitwise_inverted_equal_p (tree expr1, tree expr2, bool &wascmp, tree (*va
>      return true;
>    return false;
>  }
> +
> +/*
> + * Return the relevant gcond * of the given phi, as well as the true
> + * and false TREE args of the phi.  Or return NULL.
> + *
> + * If matched the gcond *, the output argument TREE true_arg and false_arg
> + * will be updated to the relevant args of phi.
> + *
> + * If failed to match, NULL gcond * will be returned, as well as the output
> + * arguments will be set to NULL_TREE.
> + */
> +
> +static inline gcond *
> +match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
> +{
> +  *true_arg = *false_arg = NULL_TREE;
> +
> +  if (gimple_phi_num_args (phi) != 2
> +      || EDGE_COUNT (gimple_bb (phi)->preds) != 2)
> +    return NULL;
> +
> +  basic_block pred_0 = EDGE_PRED (gimple_bb (phi), 0)->src;
> +  basic_block pred_1 = EDGE_PRED (gimple_bb (phi), 1)->src;
> +  basic_block cond_block = NULL;
> +
> +  if ((EDGE_COUNT (pred_0->succs) == 2 && EDGE_COUNT (pred_1->succs) == 1)
> +     || (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_1->succs) == 2))
> +    {
> +      /* For below control flow graph:
> +       *    |
> +       *    v
> +       * +------+
> +       * | b0:  |
> +       * | def  |       +-----+
> +       * | ...  |       | b1: |
> +       * | cond |------>| def |
> +       * +------+       | ... |
> +       *    |           +-----+
> +       *    |              |
> +       *    v              |
> +       * +-----+           |
> +       * | b2: |           |
> +       * | def |<----------+
> +       * +-----+
> +       */
> +      basic_block b0 = EDGE_COUNT (pred_0->succs) == 2 ? pred_0 : pred_1;
> +      basic_block b1 = EDGE_COUNT (pred_0->succs) == 1 ? pred_0 : pred_1;
> +
> +      if (EDGE_COUNT (b1->preds) == 1 && EDGE_PRED (b1, 0)->src == b0)
> +       cond_block = b0;
> +    }
> +
> +  if (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_0->preds) == 1
> +      && EDGE_COUNT (pred_1->succs) == 1 && EDGE_COUNT (pred_1->preds) == 1)
> +    {
> +      /* For below control flow graph:
> +       *    |
> +       *    v
> +       * +------+
> +       * | b0:  |
> +       * | ...  |       +-----+
> +       * | cond |------>| b2: |
> +       * +------+       | ... |
> +       *    |           +-----+
> +       *    |              |
> +       *    v              |
> +       * +-----+           |
> +       * | b1: |           |
> +       * | ... |           |
> +       * +-----+           |
> +       *    |              |
> +       *    |              |
> +       *    v              |
> +       * +-----+           |
> +       * | b3: |<----------+
> +       * | ... |
> +       * +-----+
> +       */
> +      basic_block b0 = EDGE_PRED (pred_0, 0)->src;
> +
> +      if (EDGE_COUNT (b0->succs) == 2 && EDGE_PRED (pred_1, 0)->src == b0)
> +       cond_block = b0;
> +    }
> +
> +  if (cond_block == NULL)
> +    return NULL;

I think it's better to above compute the edge from the possible gcond block that
leads to PHI arg0, because the CFG matching easily identifies it and we
can take the edge flag from it.  Like maybe (untested):

static inline gcond *
match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
{
  *true_arg = *false_arg = NULL_TREE;

  if (gimple_phi_num_args (phi) != 2)
    return NULL;

 basic_block pred0 = EDGE_PRED (gimple_bb (phi), 0)->src;
 basic_block pred1 = EDGE_PRED (gimple_bb (phi), 1)->src;
 gcond *cond;
 edge edge_for_pred0;

 if (EDGE_COUNT (pred0->succs) == 2
     && EDGE_COUNT (pred1->succs) == 1
     && EDGE_COUNT (pred1->preds) == 1
     && pred0->src == EDGE_PRED (pred1->preds, 0)->src)
   edge_for_pred0 = EDGE_PRED (gimple_bb (phi), 0);

 else if (EDGE_COUNT (pred1->succs) == 2
          && EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_PRED (pred0->preds, 0)->src == pred1->src)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 else if (EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred1->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_COUNT (pred1->preds) == 1
          && EDGE_COUNT (EDGE_SUCC (EDGE_PRED (pred0, 0)->src)) == 2
          && EDGE_PRED (pred0, 0)->src == EDGE_PRED (pred1, 0)->src)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (edge_for_pred0->src));
 if (!cond)
   return nullptr;

 if (edge_for_pred0->flags & EDGE_TRUE_VALUE)
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }
 else /* edge_for_pred0->flags & EDGE_FALSE_VALUE */
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }

 return cond;
}


> +  /* Now we locate the cond_block for phi node.  */
> +  tree t0 = gimple_phi_arg_def (phi, 0);
> +  tree t1 = gimple_phi_arg_def (phi, 1);
> +  edge e0 = gimple_phi_arg_edge (phi, 0);
> +  edge e1 = gimple_phi_arg_edge (phi, 1);
> +
> +  if (TREE_CODE (t0) == INTEGER_CST && TREE_CODE (t1) == INTEGER_CST)
> +    return NULL;
> +
> +  bool arg_0_cst_p = TREE_CODE (t0) == INTEGER_CST;
> +  edge arg_edge = arg_0_cst_p ? e1 : e0;
> +  tree arg = arg_0_cst_p ? t1 : t0;
> +  tree other_arg = arg_0_cst_p ? t0 : t1;
> +
> +  edge cond_e0 = EDGE_SUCC (cond_block, 0);
> +  edge cond_e1 = EDGE_SUCC (cond_block, 1);
> +  edge matched_edge = arg_edge->src == cond_e0->dest ? cond_e0 : cond_e1;
> +
> +  if (matched_edge->flags & EDGE_TRUE_VALUE)
> +    {
> +      *true_arg = arg;
> +      *false_arg = other_arg;
> +    }
> +  else
> +    {
> +      *false_arg = arg;
> +      *true_arg = other_arg;
> +    }
> +
> +  return safe_dyn_cast <gcond *> (*gsi_last_bb (cond_block));
> +}
> --
> 2.43.0
>
Richard Biener Sept. 18, 2024, 12:06 p.m. UTC | #2
On Wed, Sep 18, 2024 at 2:02 PM Richard Biener
<richard.guenther@gmail.com> wrote:
>
> On Fri, Sep 13, 2024 at 12:42 AM <pan2.li@intel.com> wrote:
> >
> > From: Pan Li <pan2.li@intel.com>
> >
> > When matching the cond with 2 args phi node, we need to figure out
> > which arg of phi node comes from the true edge of cond block, as
> > well as the false edge.  This patch would like to add interface
> > to perform the action and return the true and false arg in TREE type.
> >
> > There will be some additional handling if one of the arg is INTEGER_CST.
> > Because the INTEGER_CST args may have no source block, thus its' edge
> > source points to the condition block.  See below example in line 31,
> > the 255 INTEGER_CST has block 2 as source.  Thus, we need to find
> > the non-INTEGER_CST (aka _1) to tell which one is the true/false edge.
> > For example, the _1(3) takes block 3 as source, which is the dest
> > of false edge of the condition block.
> >
> >    4   │ __attribute__((noinline))
> >    5   │ uint8_t sat_u_add_imm_type_check_uint8_t_fmt_2 (uint8_t x)
> >    6   │ {
> >    7   │   unsigned char _1;
> >    8   │   unsigned char _2;
> >    9   │   uint8_t _3;
> >   10   │   __complex__ unsigned char _5;
> >   11   │
> >   12   │ ;;   basic block 2, loop depth 0
> >   13   │ ;;    pred:       ENTRY
> >   14   │   _5 = .ADD_OVERFLOW (x_4(D), 9);
> >   15   │   _2 = IMAGPART_EXPR <_5>;
> >   16   │   if (_2 != 0)
> >   17   │     goto <bb 4>; [35.00%]
> >   18   │   else
> >   19   │     goto <bb 3>; [65.00%]
> >   20   │ ;;    succ:       3
> >   21   │ ;;                4
> >   22   │
> >   23   │ ;;   basic block 3, loop depth 0
> >   24   │ ;;    pred:       2
> >   25   │   _1 = REALPART_EXPR <_5>;
> >   26   │ ;;    succ:       4
> >   27   │
> >   28   │ ;;   basic block 4, loop depth 0
> >   29   │ ;;    pred:       2
> >   30   │ ;;                3
> >   31   │   # _3 = PHI <255(2), _1(3)>
> >   32   │   return _3;
> >   33   │ ;;    succ:       EXIT
> >   34   │
> >   35   │ }
> >
> > The below test suites are passed for this patch.
> > * The rv64gcv fully regression test.
> > * The x86 bootstrap test.
> > * The x86 fully regression test.
> >
> > gcc/ChangeLog:
> >
> >         * gimple-match-head.cc (match_cond_with_binary_phi): Add new func
> >         impl to match binary phi for true and false arg.
> >
> > Signed-off-by: Pan Li <pan2.li@intel.com>
> > ---
> >  gcc/gimple-match-head.cc | 118 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 118 insertions(+)
> >
> > diff --git a/gcc/gimple-match-head.cc b/gcc/gimple-match-head.cc
> > index 924d3f1e710..6e7a3a0d62e 100644
> > --- a/gcc/gimple-match-head.cc
> > +++ b/gcc/gimple-match-head.cc
> > @@ -375,3 +375,121 @@ gimple_bitwise_inverted_equal_p (tree expr1, tree expr2, bool &wascmp, tree (*va
> >      return true;
> >    return false;
> >  }
> > +
> > +/*
> > + * Return the relevant gcond * of the given phi, as well as the true
> > + * and false TREE args of the phi.  Or return NULL.
> > + *
> > + * If matched the gcond *, the output argument TREE true_arg and false_arg
> > + * will be updated to the relevant args of phi.
> > + *
> > + * If failed to match, NULL gcond * will be returned, as well as the output
> > + * arguments will be set to NULL_TREE.
> > + */
> > +
> > +static inline gcond *
> > +match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
> > +{
> > +  *true_arg = *false_arg = NULL_TREE;
> > +
> > +  if (gimple_phi_num_args (phi) != 2
> > +      || EDGE_COUNT (gimple_bb (phi)->preds) != 2)
> > +    return NULL;
> > +
> > +  basic_block pred_0 = EDGE_PRED (gimple_bb (phi), 0)->src;
> > +  basic_block pred_1 = EDGE_PRED (gimple_bb (phi), 1)->src;
> > +  basic_block cond_block = NULL;
> > +
> > +  if ((EDGE_COUNT (pred_0->succs) == 2 && EDGE_COUNT (pred_1->succs) == 1)
> > +     || (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_1->succs) == 2))
> > +    {
> > +      /* For below control flow graph:
> > +       *    |
> > +       *    v
> > +       * +------+
> > +       * | b0:  |
> > +       * | def  |       +-----+
> > +       * | ...  |       | b1: |
> > +       * | cond |------>| def |
> > +       * +------+       | ... |
> > +       *    |           +-----+
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b2: |           |
> > +       * | def |<----------+
> > +       * +-----+
> > +       */
> > +      basic_block b0 = EDGE_COUNT (pred_0->succs) == 2 ? pred_0 : pred_1;
> > +      basic_block b1 = EDGE_COUNT (pred_0->succs) == 1 ? pred_0 : pred_1;
> > +
> > +      if (EDGE_COUNT (b1->preds) == 1 && EDGE_PRED (b1, 0)->src == b0)
> > +       cond_block = b0;
> > +    }
> > +
> > +  if (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_0->preds) == 1
> > +      && EDGE_COUNT (pred_1->succs) == 1 && EDGE_COUNT (pred_1->preds) == 1)
> > +    {
> > +      /* For below control flow graph:
> > +       *    |
> > +       *    v
> > +       * +------+
> > +       * | b0:  |
> > +       * | ...  |       +-----+
> > +       * | cond |------>| b2: |
> > +       * +------+       | ... |
> > +       *    |           +-----+
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b1: |           |
> > +       * | ... |           |
> > +       * +-----+           |
> > +       *    |              |
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b3: |<----------+
> > +       * | ... |
> > +       * +-----+
> > +       */
> > +      basic_block b0 = EDGE_PRED (pred_0, 0)->src;
> > +
> > +      if (EDGE_COUNT (b0->succs) == 2 && EDGE_PRED (pred_1, 0)->src == b0)
> > +       cond_block = b0;
> > +    }
> > +
> > +  if (cond_block == NULL)
> > +    return NULL;
>
> I think it's better to above compute the edge from the possible gcond block that
> leads to PHI arg0, because the CFG matching easily identifies it and we
> can take the edge flag from it.  Like maybe (untested):
>
> static inline gcond *
> match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
> {
>   *true_arg = *false_arg = NULL_TREE;
>
>   if (gimple_phi_num_args (phi) != 2)
>     return NULL;
>
>  basic_block pred0 = EDGE_PRED (gimple_bb (phi), 0)->src;
>  basic_block pred1 = EDGE_PRED (gimple_bb (phi), 1)->src;
>  gcond *cond;
>  edge edge_for_pred0;
>
>  if (EDGE_COUNT (pred0->succs) == 2
>      && EDGE_COUNT (pred1->succs) == 1
>      && EDGE_COUNT (pred1->preds) == 1
>      && pred0->src == EDGE_PRED (pred1->preds, 0)->src)
>    edge_for_pred0 = EDGE_PRED (gimple_bb (phi), 0);
>
>  else if (EDGE_COUNT (pred1->succs) == 2
>           && EDGE_COUNT (pred0->succs) == 1
>           && EDGE_COUNT (pred0->preds) == 1
>           && EDGE_PRED (pred0->preds, 0)->src == pred1->src)
>    edge_for_pred0 = EDGE_PRED (pred0, 0);
>
>  else if (EDGE_COUNT (pred0->succs) == 1
>           && EDGE_COUNT (pred1->succs) == 1
>           && EDGE_COUNT (pred0->preds) == 1
>           && EDGE_COUNT (pred1->preds) == 1
>           && EDGE_COUNT (EDGE_SUCC (EDGE_PRED (pred0, 0)->src)) == 2
>           && EDGE_PRED (pred0, 0)->src == EDGE_PRED (pred1, 0)->src)
>    edge_for_pred0 = EDGE_PRED (pred0, 0);
>
>  gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (edge_for_pred0->src));
>  if (!cond)
>    return nullptr;
>
>  if (edge_for_pred0->flags & EDGE_TRUE_VALUE)
>    {
>      *true_arg = gimple_phi_arg_def (phi, 0);
>      *false_arg = gimple_phi_arg_def (phi, 1);
>    }
>  else /* edge_for_pred0->flags & EDGE_FALSE_VALUE */
>    {
>      *true_arg = gimple_phi_arg_def (phi, 0);
>      *false_arg = gimple_phi_arg_def (phi, 1);
>    }
>
>  return cond;
> }

Sorry, that doesn't evne build, the following does:

static inline gcond *
match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
{
  *true_arg = *false_arg = NULL_TREE;

  if (gimple_phi_num_args (phi) != 2)
    return NULL;

 basic_block pred0 = EDGE_PRED (gimple_bb (phi), 0)->src;
 basic_block pred1 = EDGE_PRED (gimple_bb (phi), 1)->src;
 edge edge_for_pred0;

 if (EDGE_COUNT (pred0->succs) == 2
     && EDGE_COUNT (pred1->succs) == 1
     && EDGE_COUNT (pred1->preds) == 1
     && pred0 == EDGE_PRED (pred1, 0)->src)
   edge_for_pred0 = EDGE_PRED (gimple_bb (phi), 0);

 else if (EDGE_COUNT (pred1->succs) == 2
          && EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_PRED (pred0, 0)->src == pred1)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 else if (EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred1->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_COUNT (pred1->preds) == 1
          && EDGE_COUNT (EDGE_PRED (pred0, 0)->src->succs) == 2
          && EDGE_PRED (pred0, 0)->src == EDGE_PRED (pred1, 0)->src)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (edge_for_pred0->src));
 if (!cond)
   return nullptr;

 if (edge_for_pred0->flags & EDGE_TRUE_VALUE)
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }
 else /* edge_for_pred0->flags & EDGE_FALSE_VALUE */
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }

 return cond;
}


>
> > +  /* Now we locate the cond_block for phi node.  */
> > +  tree t0 = gimple_phi_arg_def (phi, 0);
> > +  tree t1 = gimple_phi_arg_def (phi, 1);
> > +  edge e0 = gimple_phi_arg_edge (phi, 0);
> > +  edge e1 = gimple_phi_arg_edge (phi, 1);
> > +
> > +  if (TREE_CODE (t0) == INTEGER_CST && TREE_CODE (t1) == INTEGER_CST)
> > +    return NULL;
> > +
> > +  bool arg_0_cst_p = TREE_CODE (t0) == INTEGER_CST;
> > +  edge arg_edge = arg_0_cst_p ? e1 : e0;
> > +  tree arg = arg_0_cst_p ? t1 : t0;
> > +  tree other_arg = arg_0_cst_p ? t0 : t1;
> > +
> > +  edge cond_e0 = EDGE_SUCC (cond_block, 0);
> > +  edge cond_e1 = EDGE_SUCC (cond_block, 1);
> > +  edge matched_edge = arg_edge->src == cond_e0->dest ? cond_e0 : cond_e1;
> > +
> > +  if (matched_edge->flags & EDGE_TRUE_VALUE)
> > +    {
> > +      *true_arg = arg;
> > +      *false_arg = other_arg;
> > +    }
> > +  else
> > +    {
> > +      *false_arg = arg;
> > +      *true_arg = other_arg;
> > +    }
> > +
> > +  return safe_dyn_cast <gcond *> (*gsi_last_bb (cond_block));
> > +}
> > --
> > 2.43.0
> >
Li, Pan2 Sept. 18, 2024, 11:27 p.m. UTC | #3
Got, thanks Richard and will have a try in v5.

Pan

-----Original Message-----
From: Richard Biener <richard.guenther@gmail.com> 
Sent: Wednesday, September 18, 2024 8:06 PM
To: Li, Pan2 <pan2.li@intel.com>
Cc: gcc-patches@gcc.gnu.org; Tamar.Christina@arm.com; juzhe.zhong@rivai.ai; kito.cheng@gmail.com; jeffreyalaw@gmail.com; rdapp.gcc@gmail.com
Subject: Re: [PATCH v4 1/4] Match: Add interface match_cond_with_binary_phi for true/false arg

On Wed, Sep 18, 2024 at 2:02 PM Richard Biener
<richard.guenther@gmail.com> wrote:
>
> On Fri, Sep 13, 2024 at 12:42 AM <pan2.li@intel.com> wrote:
> >
> > From: Pan Li <pan2.li@intel.com>
> >
> > When matching the cond with 2 args phi node, we need to figure out
> > which arg of phi node comes from the true edge of cond block, as
> > well as the false edge.  This patch would like to add interface
> > to perform the action and return the true and false arg in TREE type.
> >
> > There will be some additional handling if one of the arg is INTEGER_CST.
> > Because the INTEGER_CST args may have no source block, thus its' edge
> > source points to the condition block.  See below example in line 31,
> > the 255 INTEGER_CST has block 2 as source.  Thus, we need to find
> > the non-INTEGER_CST (aka _1) to tell which one is the true/false edge.
> > For example, the _1(3) takes block 3 as source, which is the dest
> > of false edge of the condition block.
> >
> >    4   │ __attribute__((noinline))
> >    5   │ uint8_t sat_u_add_imm_type_check_uint8_t_fmt_2 (uint8_t x)
> >    6   │ {
> >    7   │   unsigned char _1;
> >    8   │   unsigned char _2;
> >    9   │   uint8_t _3;
> >   10   │   __complex__ unsigned char _5;
> >   11   │
> >   12   │ ;;   basic block 2, loop depth 0
> >   13   │ ;;    pred:       ENTRY
> >   14   │   _5 = .ADD_OVERFLOW (x_4(D), 9);
> >   15   │   _2 = IMAGPART_EXPR <_5>;
> >   16   │   if (_2 != 0)
> >   17   │     goto <bb 4>; [35.00%]
> >   18   │   else
> >   19   │     goto <bb 3>; [65.00%]
> >   20   │ ;;    succ:       3
> >   21   │ ;;                4
> >   22   │
> >   23   │ ;;   basic block 3, loop depth 0
> >   24   │ ;;    pred:       2
> >   25   │   _1 = REALPART_EXPR <_5>;
> >   26   │ ;;    succ:       4
> >   27   │
> >   28   │ ;;   basic block 4, loop depth 0
> >   29   │ ;;    pred:       2
> >   30   │ ;;                3
> >   31   │   # _3 = PHI <255(2), _1(3)>
> >   32   │   return _3;
> >   33   │ ;;    succ:       EXIT
> >   34   │
> >   35   │ }
> >
> > The below test suites are passed for this patch.
> > * The rv64gcv fully regression test.
> > * The x86 bootstrap test.
> > * The x86 fully regression test.
> >
> > gcc/ChangeLog:
> >
> >         * gimple-match-head.cc (match_cond_with_binary_phi): Add new func
> >         impl to match binary phi for true and false arg.
> >
> > Signed-off-by: Pan Li <pan2.li@intel.com>
> > ---
> >  gcc/gimple-match-head.cc | 118 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 118 insertions(+)
> >
> > diff --git a/gcc/gimple-match-head.cc b/gcc/gimple-match-head.cc
> > index 924d3f1e710..6e7a3a0d62e 100644
> > --- a/gcc/gimple-match-head.cc
> > +++ b/gcc/gimple-match-head.cc
> > @@ -375,3 +375,121 @@ gimple_bitwise_inverted_equal_p (tree expr1, tree expr2, bool &wascmp, tree (*va
> >      return true;
> >    return false;
> >  }
> > +
> > +/*
> > + * Return the relevant gcond * of the given phi, as well as the true
> > + * and false TREE args of the phi.  Or return NULL.
> > + *
> > + * If matched the gcond *, the output argument TREE true_arg and false_arg
> > + * will be updated to the relevant args of phi.
> > + *
> > + * If failed to match, NULL gcond * will be returned, as well as the output
> > + * arguments will be set to NULL_TREE.
> > + */
> > +
> > +static inline gcond *
> > +match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
> > +{
> > +  *true_arg = *false_arg = NULL_TREE;
> > +
> > +  if (gimple_phi_num_args (phi) != 2
> > +      || EDGE_COUNT (gimple_bb (phi)->preds) != 2)
> > +    return NULL;
> > +
> > +  basic_block pred_0 = EDGE_PRED (gimple_bb (phi), 0)->src;
> > +  basic_block pred_1 = EDGE_PRED (gimple_bb (phi), 1)->src;
> > +  basic_block cond_block = NULL;
> > +
> > +  if ((EDGE_COUNT (pred_0->succs) == 2 && EDGE_COUNT (pred_1->succs) == 1)
> > +     || (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_1->succs) == 2))
> > +    {
> > +      /* For below control flow graph:
> > +       *    |
> > +       *    v
> > +       * +------+
> > +       * | b0:  |
> > +       * | def  |       +-----+
> > +       * | ...  |       | b1: |
> > +       * | cond |------>| def |
> > +       * +------+       | ... |
> > +       *    |           +-----+
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b2: |           |
> > +       * | def |<----------+
> > +       * +-----+
> > +       */
> > +      basic_block b0 = EDGE_COUNT (pred_0->succs) == 2 ? pred_0 : pred_1;
> > +      basic_block b1 = EDGE_COUNT (pred_0->succs) == 1 ? pred_0 : pred_1;
> > +
> > +      if (EDGE_COUNT (b1->preds) == 1 && EDGE_PRED (b1, 0)->src == b0)
> > +       cond_block = b0;
> > +    }
> > +
> > +  if (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_0->preds) == 1
> > +      && EDGE_COUNT (pred_1->succs) == 1 && EDGE_COUNT (pred_1->preds) == 1)
> > +    {
> > +      /* For below control flow graph:
> > +       *    |
> > +       *    v
> > +       * +------+
> > +       * | b0:  |
> > +       * | ...  |       +-----+
> > +       * | cond |------>| b2: |
> > +       * +------+       | ... |
> > +       *    |           +-----+
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b1: |           |
> > +       * | ... |           |
> > +       * +-----+           |
> > +       *    |              |
> > +       *    |              |
> > +       *    v              |
> > +       * +-----+           |
> > +       * | b3: |<----------+
> > +       * | ... |
> > +       * +-----+
> > +       */
> > +      basic_block b0 = EDGE_PRED (pred_0, 0)->src;
> > +
> > +      if (EDGE_COUNT (b0->succs) == 2 && EDGE_PRED (pred_1, 0)->src == b0)
> > +       cond_block = b0;
> > +    }
> > +
> > +  if (cond_block == NULL)
> > +    return NULL;
>
> I think it's better to above compute the edge from the possible gcond block that
> leads to PHI arg0, because the CFG matching easily identifies it and we
> can take the edge flag from it.  Like maybe (untested):
>
> static inline gcond *
> match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
> {
>   *true_arg = *false_arg = NULL_TREE;
>
>   if (gimple_phi_num_args (phi) != 2)
>     return NULL;
>
>  basic_block pred0 = EDGE_PRED (gimple_bb (phi), 0)->src;
>  basic_block pred1 = EDGE_PRED (gimple_bb (phi), 1)->src;
>  gcond *cond;
>  edge edge_for_pred0;
>
>  if (EDGE_COUNT (pred0->succs) == 2
>      && EDGE_COUNT (pred1->succs) == 1
>      && EDGE_COUNT (pred1->preds) == 1
>      && pred0->src == EDGE_PRED (pred1->preds, 0)->src)
>    edge_for_pred0 = EDGE_PRED (gimple_bb (phi), 0);
>
>  else if (EDGE_COUNT (pred1->succs) == 2
>           && EDGE_COUNT (pred0->succs) == 1
>           && EDGE_COUNT (pred0->preds) == 1
>           && EDGE_PRED (pred0->preds, 0)->src == pred1->src)
>    edge_for_pred0 = EDGE_PRED (pred0, 0);
>
>  else if (EDGE_COUNT (pred0->succs) == 1
>           && EDGE_COUNT (pred1->succs) == 1
>           && EDGE_COUNT (pred0->preds) == 1
>           && EDGE_COUNT (pred1->preds) == 1
>           && EDGE_COUNT (EDGE_SUCC (EDGE_PRED (pred0, 0)->src)) == 2
>           && EDGE_PRED (pred0, 0)->src == EDGE_PRED (pred1, 0)->src)
>    edge_for_pred0 = EDGE_PRED (pred0, 0);
>
>  gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (edge_for_pred0->src));
>  if (!cond)
>    return nullptr;
>
>  if (edge_for_pred0->flags & EDGE_TRUE_VALUE)
>    {
>      *true_arg = gimple_phi_arg_def (phi, 0);
>      *false_arg = gimple_phi_arg_def (phi, 1);
>    }
>  else /* edge_for_pred0->flags & EDGE_FALSE_VALUE */
>    {
>      *true_arg = gimple_phi_arg_def (phi, 0);
>      *false_arg = gimple_phi_arg_def (phi, 1);
>    }
>
>  return cond;
> }

Sorry, that doesn't evne build, the following does:

static inline gcond *
match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
{
  *true_arg = *false_arg = NULL_TREE;

  if (gimple_phi_num_args (phi) != 2)
    return NULL;

 basic_block pred0 = EDGE_PRED (gimple_bb (phi), 0)->src;
 basic_block pred1 = EDGE_PRED (gimple_bb (phi), 1)->src;
 edge edge_for_pred0;

 if (EDGE_COUNT (pred0->succs) == 2
     && EDGE_COUNT (pred1->succs) == 1
     && EDGE_COUNT (pred1->preds) == 1
     && pred0 == EDGE_PRED (pred1, 0)->src)
   edge_for_pred0 = EDGE_PRED (gimple_bb (phi), 0);

 else if (EDGE_COUNT (pred1->succs) == 2
          && EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_PRED (pred0, 0)->src == pred1)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 else if (EDGE_COUNT (pred0->succs) == 1
          && EDGE_COUNT (pred1->succs) == 1
          && EDGE_COUNT (pred0->preds) == 1
          && EDGE_COUNT (pred1->preds) == 1
          && EDGE_COUNT (EDGE_PRED (pred0, 0)->src->succs) == 2
          && EDGE_PRED (pred0, 0)->src == EDGE_PRED (pred1, 0)->src)
   edge_for_pred0 = EDGE_PRED (pred0, 0);

 gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (edge_for_pred0->src));
 if (!cond)
   return nullptr;

 if (edge_for_pred0->flags & EDGE_TRUE_VALUE)
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }
 else /* edge_for_pred0->flags & EDGE_FALSE_VALUE */
   {
     *true_arg = gimple_phi_arg_def (phi, 0);
     *false_arg = gimple_phi_arg_def (phi, 1);
   }

 return cond;
}


>
> > +  /* Now we locate the cond_block for phi node.  */
> > +  tree t0 = gimple_phi_arg_def (phi, 0);
> > +  tree t1 = gimple_phi_arg_def (phi, 1);
> > +  edge e0 = gimple_phi_arg_edge (phi, 0);
> > +  edge e1 = gimple_phi_arg_edge (phi, 1);
> > +
> > +  if (TREE_CODE (t0) == INTEGER_CST && TREE_CODE (t1) == INTEGER_CST)
> > +    return NULL;
> > +
> > +  bool arg_0_cst_p = TREE_CODE (t0) == INTEGER_CST;
> > +  edge arg_edge = arg_0_cst_p ? e1 : e0;
> > +  tree arg = arg_0_cst_p ? t1 : t0;
> > +  tree other_arg = arg_0_cst_p ? t0 : t1;
> > +
> > +  edge cond_e0 = EDGE_SUCC (cond_block, 0);
> > +  edge cond_e1 = EDGE_SUCC (cond_block, 1);
> > +  edge matched_edge = arg_edge->src == cond_e0->dest ? cond_e0 : cond_e1;
> > +
> > +  if (matched_edge->flags & EDGE_TRUE_VALUE)
> > +    {
> > +      *true_arg = arg;
> > +      *false_arg = other_arg;
> > +    }
> > +  else
> > +    {
> > +      *false_arg = arg;
> > +      *true_arg = other_arg;
> > +    }
> > +
> > +  return safe_dyn_cast <gcond *> (*gsi_last_bb (cond_block));
> > +}
> > --
> > 2.43.0
> >
diff mbox series

Patch

diff --git a/gcc/gimple-match-head.cc b/gcc/gimple-match-head.cc
index 924d3f1e710..6e7a3a0d62e 100644
--- a/gcc/gimple-match-head.cc
+++ b/gcc/gimple-match-head.cc
@@ -375,3 +375,121 @@  gimple_bitwise_inverted_equal_p (tree expr1, tree expr2, bool &wascmp, tree (*va
     return true;
   return false;
 }
+
+/*
+ * Return the relevant gcond * of the given phi, as well as the true
+ * and false TREE args of the phi.  Or return NULL.
+ *
+ * If matched the gcond *, the output argument TREE true_arg and false_arg
+ * will be updated to the relevant args of phi.
+ *
+ * If failed to match, NULL gcond * will be returned, as well as the output
+ * arguments will be set to NULL_TREE.
+ */
+
+static inline gcond *
+match_cond_with_binary_phi (gphi *phi, tree *true_arg, tree *false_arg)
+{
+  *true_arg = *false_arg = NULL_TREE;
+
+  if (gimple_phi_num_args (phi) != 2
+      || EDGE_COUNT (gimple_bb (phi)->preds) != 2)
+    return NULL;
+
+  basic_block pred_0 = EDGE_PRED (gimple_bb (phi), 0)->src;
+  basic_block pred_1 = EDGE_PRED (gimple_bb (phi), 1)->src;
+  basic_block cond_block = NULL;
+
+  if ((EDGE_COUNT (pred_0->succs) == 2 && EDGE_COUNT (pred_1->succs) == 1)
+     || (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_1->succs) == 2))
+    {
+      /* For below control flow graph:
+       *    |
+       *    v
+       * +------+
+       * | b0:  |
+       * | def  |       +-----+
+       * | ...  |       | b1: |
+       * | cond |------>| def |
+       * +------+       | ... |
+       *    |           +-----+
+       *    |              |
+       *    v              |
+       * +-----+           |
+       * | b2: |           |
+       * | def |<----------+
+       * +-----+
+       */
+      basic_block b0 = EDGE_COUNT (pred_0->succs) == 2 ? pred_0 : pred_1;
+      basic_block b1 = EDGE_COUNT (pred_0->succs) == 1 ? pred_0 : pred_1;
+
+      if (EDGE_COUNT (b1->preds) == 1 && EDGE_PRED (b1, 0)->src == b0)
+	cond_block = b0;
+    }
+
+  if (EDGE_COUNT (pred_0->succs) == 1 && EDGE_COUNT (pred_0->preds) == 1
+      && EDGE_COUNT (pred_1->succs) == 1 && EDGE_COUNT (pred_1->preds) == 1)
+    {
+      /* For below control flow graph:
+       *    |
+       *    v
+       * +------+
+       * | b0:  |
+       * | ...  |       +-----+
+       * | cond |------>| b2: |
+       * +------+       | ... |
+       *    |           +-----+
+       *    |              |
+       *    v              |
+       * +-----+           |
+       * | b1: |           |
+       * | ... |           |
+       * +-----+           |
+       *    |              |
+       *    |              |
+       *    v              |
+       * +-----+           |
+       * | b3: |<----------+
+       * | ... |
+       * +-----+
+       */
+      basic_block b0 = EDGE_PRED (pred_0, 0)->src;
+
+      if (EDGE_COUNT (b0->succs) == 2 && EDGE_PRED (pred_1, 0)->src == b0)
+	cond_block = b0;
+    }
+
+  if (cond_block == NULL)
+    return NULL;
+
+  /* Now we locate the cond_block for phi node.  */
+  tree t0 = gimple_phi_arg_def (phi, 0);
+  tree t1 = gimple_phi_arg_def (phi, 1);
+  edge e0 = gimple_phi_arg_edge (phi, 0);
+  edge e1 = gimple_phi_arg_edge (phi, 1);
+
+  if (TREE_CODE (t0) == INTEGER_CST && TREE_CODE (t1) == INTEGER_CST)
+    return NULL;
+
+  bool arg_0_cst_p = TREE_CODE (t0) == INTEGER_CST;
+  edge arg_edge = arg_0_cst_p ? e1 : e0;
+  tree arg = arg_0_cst_p ? t1 : t0;
+  tree other_arg = arg_0_cst_p ? t0 : t1;
+
+  edge cond_e0 = EDGE_SUCC (cond_block, 0);
+  edge cond_e1 = EDGE_SUCC (cond_block, 1);
+  edge matched_edge = arg_edge->src == cond_e0->dest ? cond_e0 : cond_e1;
+
+  if (matched_edge->flags & EDGE_TRUE_VALUE)
+    {
+      *true_arg = arg;
+      *false_arg = other_arg;
+    }
+  else
+    {
+      *false_arg = arg;
+      *true_arg = other_arg;
+    }
+
+  return safe_dyn_cast <gcond *> (*gsi_last_bb (cond_block));
+}