On Tue, 29 Sep 2026, Tamar Christina wrote:

> 
> Hi Ezra,
> 
> > -----Original Message-----
> > From: [email protected] <[email protected]>
> > Sent: 09 September 2026 20:12
> > To: [email protected]
> > Cc: Tamar Christina <[email protected]>; [email protected];
> > [email protected]; Ezra Sitorus <[email protected]>
> > Subject: [RFC PATCH 5/5] gcc: add complex dot prod SLP pattern
> > 
> > From: Ezra Sitorus <[email protected]>
> > 
> > Recognise complex integer dot product patterns and try to emit complex
> > dot product instructions if supported. In particular, recognise loops
> > like this:
> > 
> > struct cint16_t
> > {
> >   int8_t re;
> >   int8_t im;
> > };
> > 
> > for (int i = 0; i < N; i++)
> >   res += (a[i].re * b[i].re - a[i].im * b[i].im);
> > 
> > which would be transformed into this from previous patches:
> > 
> > acc_1 = DOT_PROD<a_re, b_re, acc_0>;
> > acc_2 = 0 - acc_1;
> > acc_3 = DOT_PROD<a_im, b_im, acc_2>;
> > acc_4 = 0 - acc_3;
> > 
> > and then try to emit:
> > 
> > CDOT (a, b, acc)
> > 
> > in the case of AArch64 with SVE2 support.
> > 
> > gcc/ChangeLog:
> >     * tree-vect-slp-patterns.cc
> >       (slp_tree_zero_p, vect_build_complex_dot_prod_load,
> > complex_re_im_pair_p,
> >       class complex_dot_prod_pattern): New.
> >       (complex_operations_pattern::recognize): Add support for
> >       complex_dot_prod_pattern::matches.
> > 
> > gcc/testsuite/ChangeLog:
> >     * testsuite/gcc.dg/vect/complex/bb-slp-complex-dot-int8.c: New
> >       test.
> > 
> > Here are some questions I have:
> > 
> > * Why (not) not use a VEC_PERM_EXPR for
> > vect_build_complex_dot_prod_load?
> > 
> > Something like this?
> > static slp_tree
> > vect_build_complex_dot_prod_load (slp_tree re, slp_tree im)
> > {
> >   lane_permutation_t perm;
> >   perm.create (2);
> >   perm.quick_push (std::make_pair (0, 0));
> >   perm.quick_push (std::make_pair (1, 0));
> > 
> >   slp_tree complex = vect_create_new_slp_node (2, VEC_PERM_EXPR);
> >   SLP_TREE_LANE_PERMUTATION (complex) = perm;
> > 
> >   SLP_TREE_VECTYPE (complex) = SLP_TREE_VECTYPE (re);
> >   SLP_TREE_REF_COUNT (complex) = 1;
> >   SLP_TREE_LANES (complex) = 2;
> 
> This isn't correct, in a repeating sequence, i.e. [r0,i0,r1,r1,..] which 
> happens
> when e.g. you unroll LANES > 2. You need to maintain the same number of
> lanes as the children.  This also assumes 1 "scalar" statement is 1 lane, 
> which
> isn't always the case.
> 
> >   SLP_TREE_REPRESENTATIVE (complex) = NULL;
> > 
> >   SLP_TREE_CHILDREN (complex).quick_push (re);
> >   SLP_TREE_CHILDREN (complex).quick_push (im);
> >   SLP_TREE_REF_COUNT (re)++;
> >   SLP_TREE_REF_COUNT (im)++;
> > 
> >   gcc_assert (STMT_VINFO_GROUPED_ACCESS (SLP_TREE_REPRESENTATIVE
> > (re)));
> >   gcc_assert (STMT_VINFO_GROUPED_ACCESS (SLP_TREE_REPRESENTATIVE
> > (im)));
> >   SLP_TREE_SCALAR_STMTS (complex).safe_push (SLP_TREE_SCALAR_STMTS
> > (re)[0]);
> >   SLP_TREE_SCALAR_STMTS (complex).safe_push (SLP_TREE_SCALAR_STMTS
> > (im)[0]);
> > 
> 
> VEC_PERM_EXPR don't have scalar statements normally.

If they are available and correct they should be put in though.

> >   return complex;
> > }
> 
> What's the difference between this and vect_build_combine_node? Guess I'll see
> when I get to the relevant part :)
> 
> > 
> > It seems to generate the same codegen as the current patch.
> > ---
> >  .../vect/complex/bb-slp-complex-dot-int8.c    |  25 ++
> >  gcc/tree-vect-slp-patterns.cc                 | 358 ++++++++++++++++++
> >  2 files changed, 383 insertions(+)
> >  create mode 100644 gcc/testsuite/gcc.dg/vect/complex/bb-slp-complex-
> > dot-int8.c
> > 
> > diff --git a/gcc/testsuite/gcc.dg/vect/complex/bb-slp-complex-dot-int8.c
> > b/gcc/testsuite/gcc.dg/vect/complex/bb-slp-complex-dot-int8.c
> > new file mode 100644
> > index 000000000000..861b790158f6
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vect/complex/bb-slp-complex-dot-int8.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do compile { target aarch64-*-* } } */
> > +/* { dg-additional-options "-O3 -fdump-tree-vect-details -march=armv8.2-
> > a+sve2" } */
> > +
> > +#include <stdint.h>
> > +
> > +#define N 512
> > +
> > +struct cint16_t
> > +{
> > +  int8_t re;
> > +  int8_t im;
> > +};
> > +
> > +int32_t
> > +complex_dot_prod_rot0 (const struct cint16_t *restrict a,
> > +             const struct cint16_t *restrict b)
> > +{
> > +  int32_t res = 0;
> > +  for (int i = 0; i < N; i++)
> > +    res += (a[i].re * b[i].re - a[i].im * b[i].im);
> > +  return res;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump "Found COMPLEX_DOT_PROD_ROT0" "vect" }
> > } */
> > +/* { dg-final { scan-tree-dump "add new stmt:
> > .*COMPLEX_DOT_PROD_ROT0" "vect" } } */
> > diff --git a/gcc/tree-vect-slp-patterns.cc b/gcc/tree-vect-slp-patterns.cc
> > index c53325fecdc7..582d0473f3ca 100644
> > --- a/gcc/tree-vect-slp-patterns.cc
> > +++ b/gcc/tree-vect-slp-patterns.cc
> > @@ -108,6 +108,40 @@ vect_pattern_validate_optab (internal_fn ifn,
> > slp_tree node)
> >    return true;
> >  }
> > 
> > +static bool
> > +vect_pattern_validate_optab (internal_fn ifn, slp_tree, tree_pair types)
> > +{
> > +  tree type0 = types.first;
> > +  tree type1 = types.second;
> > +  if (ifn == IFN_LAST || !type0 || !type1)
> > +    return false;
> 
> I think this should be an assert, since something has gone wrong then.
> But isn't type1 the vectype of the slp_tree? So what you really want is an
> optional result type to check?
> 
> Could we not then add an optional tree result_type = NULL_TREE to the existing
> vect_pattern_validate_optab and have one implementation?
> 
> > +
> > +  if (dump_enabled_p ())
> > +    dump_printf_loc (MSG_NOTE, vect_location, "Found %s pattern in SLP
> > tree\n",
> > +                internal_fn_name (ifn));
> > +
> > +  if (direct_internal_fn_supported_p (ifn, types, OPTIMIZE_FOR_SPEED))
> > +    {
> > +      if (dump_enabled_p ())
> > +   dump_printf_loc (
> > +       MSG_NOTE, vect_location,
> > +       "Target supports %s vectorization with types %T and %T\n",
> > +       internal_fn_name (ifn), type0, type1);
> > +    }
> > +  else
> > +    {
> > +      if (dump_enabled_p ())
> > +   {
> > +     dump_printf_loc (MSG_NOTE, vect_location,
> > +                      "Target does not support %s for vector types "
> > +                      "%T and %T\n",
> > +                      internal_fn_name (ifn), type0, type1);
> > +   }
> > +      return false;
> > +    }
> > +  return true;
> > +}
> > +
> > 
> > /******************************************************************
> > *************
> >   * General helper types
> > 
> > *******************************************************************
> > ***********/
> > @@ -1508,6 +1542,326 @@ complex_fms_pattern::build (vec_info *vinfo)
> >    build_common (vinfo);
> >  }
> > 
> > +/*****************************************************************
> > **************
> > + * complex_dot_prod_pattern class
> > +
> > *******************************************************************
> > ***********/
> > +
> > +static bool
> > +slp_tree_zero_p (slp_tree node)
> > +{
> > +  if (!node)
> > +    return false;
> 
> Should be an assert.
> 
> > +
> > +  if (SLP_TREE_SCALAR_OPS (node).is_empty ())
> > +    return false;
> > +
> 
> Should check that the SLP_TREE_DEF_TYPE is a vect_constant_def.       
> 
> > +  tree op;
> > +  unsigned i;
> > +
> > +  FOR_EACH_VEC_ELT (SLP_TREE_SCALAR_OPS (node), i, op)
> > +    if (!zerop (op))
> > +      return false;
> > +
> 
> Just use a normal for each iterator
> 
> for (tree op : SLP_TREE_SCALAR_OPS (node))
> 
> > +  return true;
> > +}
> > +
> > +/*  Build a 2-lane SLP load node for the consecutive members represented by
> > RE
> > +    and IM. This is used in complex_dot_prod_pattern::build, and
> > +    complex_dot_prod_pattern::matches verifies using complex_re_im_pair_p
> > that
> > +    they are the adjacent members of the same load group. See below.
> > + */
> > +static slp_tree
> > +vect_build_complex_dot_prod_load (slp_tree re, slp_tree im)
> > +{
> > +  slp_tree complex = vect_create_new_slp_node (0, SLP_TREE_CODE (re));
> > +  SLP_TREE_VECTYPE (complex) = SLP_TREE_VECTYPE (re);
> > +  SLP_TREE_REF_COUNT (complex) = 1;
> > +  SLP_TREE_LANES (complex) = 2;
> 
> Don't build new load nodes from inside the pattern matcher.  We want to share
> loads as much as possible and the pattern matcher has no context of this load.
> vect_build_combine_node deals with this already.
> 
> Add some testcases where you increase the number of lanes and you'll see where
> your assumptions fall apart, e.g.
> 
> int32_t
> complex_dot_prod_rot0 (const struct cint16_t *restrict a,
>                 const struct cint16_t *restrict b)
> {
>   int32_t res = 0;
>   for (int i = 0; i < N; i+=2)
>     {
>        res += (a[i].re * b[i].re - a[i].im * b[i].im);
>        res += (a[i+1].re * b[i+1].re - a[i+1].im * b[i+1].im);
>      }
>   return res;
> }
> 
> > +  SLP_TREE_REPRESENTATIVE (complex) = SLP_TREE_REPRESENTATIVE (re);
> > +
> > +  auto rep = SLP_TREE_REPRESENTATIVE (re);
> > +  gcc_assert (STMT_VINFO_GROUPED_ACCESS (rep));
> > +  SLP_TREE_SCALAR_STMTS (complex).safe_push (SLP_TREE_SCALAR_STMTS
> > (re)[0]);
> > +  SLP_TREE_SCALAR_STMTS (complex).safe_push (SLP_TREE_SCALAR_STMTS
> > (im)[0]);
> > +
> > +  return complex;
> > +}
> > +
> > +/*  Returns true if RE and IM are one-lane load nodes for consecutive
> > members
> > +   of the same { real, imaginary } load group.  */
> > +static bool
> > +complex_re_im_pair_p (slp_tree re, slp_tree im)
> > +{
> > +  if (!re || !im || SLP_TREE_PERMUTE_P (re) || SLP_TREE_PERMUTE_P (im)
> > +      || SLP_TREE_SCALAR_STMTS (re).length () != 1
> > +      || SLP_TREE_SCALAR_STMTS (im).length () != 1)
> > +    return false;
> > +
> > +  stmt_vec_info re_rep = SLP_TREE_REPRESENTATIVE (re);
> > +  stmt_vec_info im_rep = SLP_TREE_REPRESENTATIVE (im);
> > +
> > +  if (!re_rep || !im_rep || !STMT_VINFO_GROUPED_ACCESS (re_rep)
> > +      || !STMT_VINFO_GROUPED_ACCESS (im_rep))
> > +    return false;
> > +
> > +  stmt_vec_info first = DR_GROUP_FIRST_ELEMENT (re_rep);
> > +  if (first != re_rep || DR_GROUP_SIZE (first) != 2
> > +      || DR_GROUP_NEXT_ELEMENT (first) != im_rep || DR_GROUP_GAP
> > (im_rep) != 1)
> > +    return false;
> > +
> > +  return true;
> > +}
> 
> This looks like it just needs to extend compatible_complex_nodes_p
> for the group load case, this also caches the result so we don't
> repeatedly ask.
> 
> > +
> > +class complex_dot_prod_pattern : public complex_pattern
> > +{
> > +protected:
> > +  complex_dot_prod_pattern (slp_tree *node, vec<slp_tree> *m_ops,
> > internal_fn ifn)
> > +      : complex_pattern (node, m_ops, ifn)
> > +  {
> > +    this->m_num_args = 3;
> > +  }
> > +
> > +public:
> > +  void build (vec_info *) final override;
> > +  static internal_fn matches (complex_operation_t op,
> > +                         slp_tree_to_load_perm_map_t *,
> > +                         slp_compat_nodes_map_t *, slp_tree *,
> > +                         vec<slp_tree> *);
> > +
> > +  static vect_pattern *recognize (slp_tree_to_load_perm_map_t *,
> > +                             slp_compat_nodes_map_t *, slp_tree *);
> > +
> > +  static vect_pattern *
> > +  mkInstance (slp_tree *node, vec<slp_tree> *m_ops, internal_fn ifn)
> > +  {
> > +    return new complex_dot_prod_pattern (node, m_ops, ifn);
> > +  }
> > +};
> > +
> > +void
> > +complex_dot_prod_pattern::build (vec_info *vinfo)
> > +{
> > +  slp_tree node;
> > +  slp_tree a_re = this->m_ops[0];
> > +  slp_tree a_im = this->m_ops[1];
> > +  slp_tree b_re = this->m_ops[2];
> > +  slp_tree b_im = this->m_ops[3];
> > +  slp_tree acc = this->m_ops[4];
> > +
> > +  slp_tree a_complex = vect_build_complex_dot_prod_load (a_re, a_im);
> > +  slp_tree b_complex = vect_build_complex_dot_prod_load (b_re, b_im);
> > +  SLP_TREE_REF_COUNT (acc)++;
> > +
> > +  unsigned i;
> > +  FOR_EACH_VEC_ELT (SLP_TREE_CHILDREN (*this->m_node), i, node)
> > +    vect_free_slp_tree (node);
> > +
> > +  SLP_TREE_CHILDREN (*this->m_node).release ();
> > +  SLP_TREE_CHILDREN (*this->m_node).safe_grow (3);
> > +
> > +  SLP_TREE_CHILDREN (*this->m_node)[0] = a_complex;
> > +  SLP_TREE_CHILDREN (*this->m_node)[1] = b_complex;
> > +  SLP_TREE_CHILDREN (*this->m_node)[2] = acc;
> > +  SLP_TREE_REDUC_IDX (*this->m_node) = 2;
> > +
> > +  /* This is taken from complex_pattern::build_common. There are some
> > changes
> > +     which are required for this specific case:
> > +
> > +     * input operands 0 and 1 have the same type, operand 2 (acc) has a
> > different type.
> > +     * operand 2 is the reduction cycle operand.
> > +     * the live reduction status needs to be preserved.
> > +     * loop relevant classification should be preserved 
> > (vect_used_only_live).
> > +
> > +     For diagnostics only, the temp_ssa_name is cdot_slp_patt.  */
> > +
> > +  stmt_vec_info stmt_info;
> > +  auto_vec<tree> args;
> > +  args.create (this->m_num_args);
> > +  args.quick_grow_cleared (this->m_num_args);
> > +  unsigned ix;
> > +  stmt_vec_info call_stmt_info;
> > +  gcall *call_stmt = NULL;
> > +
> > +  /* Now modify the nodes themselves.  */
> > +  FOR_EACH_VEC_ELT (this->m_workset, ix, node)
> > +  {
> > +    /* Calculate the location of the statement in NODE to replace.  */
> > +    stmt_info = SLP_TREE_REPRESENTATIVE (node);
> > +    gimple *old_stmt = STMT_VINFO_STMT (stmt_info);
> > +    tree lhs_old_stmt = gimple_get_lhs (old_stmt);
> > +    tree type = TREE_TYPE (lhs_old_stmt);
> > +
> > +    /* Create the argument set for use by gimple_build_call_internal_vec.  
> > */
> > +    for (unsigned i = 0; i < this->m_num_args; i++)
> > +      args[i] = gimple_get_lhs (STMT_VINFO_STMT (
> > +     SLP_TREE_REPRESENTATIVE (SLP_TREE_CHILDREN (node)[i])));
> > +
> > +    /* Create the new pattern statements.  */
> > +    call_stmt = gimple_build_call_internal_vec (this->m_ifn, args);
> > +    tree var = make_temp_ssa_name (type, call_stmt, "cdot_slp_patt");
> > +    gimple_call_set_lhs (call_stmt, var);
> > +    gimple_set_location (call_stmt, gimple_location (old_stmt));
> > +    gimple_call_set_nothrow (call_stmt, true);
> > +
> > +    /* Adjust the book-keeping for the new and old statements for use 
> > during
> > +       SLP.  This is required to get the right VF and statement during SLP
> > +       analysis.  These changes are created after relevancy has been set 
> > for
> > +       the nodes as such we need to manually update them.  Any changes will
> > be
> > +       undone if SLP is cancelled.  */
> > +    call_stmt_info
> > +   = vinfo->add_pattern_stmt (call_stmt, vect_orig_stmt (stmt_info));
> > +
> > +    STMT_VINFO_REDUC_IDX (call_stmt_info) = 2;
> > +    STMT_VINFO_LIVE_P (call_stmt_info) = STMT_VINFO_LIVE_P (stmt_info);
> > +
> > +    /* Make sure to mark the representative statement pure_slp and
> > +       relevant and transfer reduction info. */
> > +    STMT_VINFO_RELEVANT (call_stmt_info) = STMT_VINFO_RELEVANT
> > (stmt_info);
> > +    STMT_SLP_TYPE (call_stmt_info) = pure_slp;
> > +
> > +    gimple_set_bb (call_stmt, gimple_bb (stmt_info->stmt));
> > +    STMT_VINFO_VECTYPE (call_stmt_info) = SLP_TREE_VECTYPE (node);
> > +
> > +    /* Since we are replacing all the statements in the group with the same
> > +       thing it doesn't really matter.  So just set it every time a new 
> > stmt
> > +       is created.  */
> > +    SLP_TREE_REPRESENTATIVE (node) = call_stmt_info;
> > +    SLP_TREE_LANE_PERMUTATION (node).release ();
> > +    SLP_TREE_CODE (node) = ERROR_MARK;
> > +  }
> > +}
> 
> This seems to be doing a lot of the same work complex_pattern::build_common
> with the exception of the STMT_VINFO_REDUC_IDX and copying of the
> liveness for the reduction. I think build_common should be extended instead,
> it would save a lot on duplication.
> 
> > +
> > +/* Pattern matcher for trying to match complex integer dot product pattern
> > +   in SLP tree.  If the operation matches then IFN is set to the operation
> > +   IFN_COMPLEX_DOT_PROD_ROT0 and the arguments to the replacement
> > statement are
> > +   put in m_ops.
> > +
> > +   If no match is found then IFN is set to IFN_LAST and m_ops is unchanged.
> > +
> > +   This function matches the patterns shaped as:
> > +
> > +   acc_1 = DOT_PROD_EXPR (a_re, b_re, acc_0);
> > +   acc_2 = 0 - acc_1;
> > +   acc_3 = DOT_PROD_EXPR (a_im, b_im, acc_2);
> > +   acc_4 = 0 - acc_3;
> > +
> > +   which comes from the dot_prod and neg_dot_prod pattern that comes
> > from statements
> > +   like this:
> > +
> > +   acc = a[i] * b[i] - a[i + 1] * b[i + 1] + acc;
> > +
> > +   On success, store { A_RE, A_IM, B_RE, B_IM, ACC } in OPS and return
> > +   IFN_COMPLEX_DOT_PROD_ROT0. On failure, return IFN_LAST with OPS
> > unmodified.
> > +    */
> > +
> > +internal_fn
> > +complex_dot_prod_pattern::matches (complex_operation_t op,
> > +                         slp_tree_to_load_perm_map_t *,
> > +                         slp_compat_nodes_map_t *,
> > +                         slp_tree *ref_node, vec<slp_tree> *ops)
> > +{
> > +  /* We need to ignore the two_operands nodes that may also match,
> > +     for that we can check if they have any scalar statements and also
> > +     check that it's not a permute node as we're looking for a normal
> > +     MINUS_EXPR operation.  */
> > +  if (op != CMPLX_NONE)
> > +    return IFN_LAST;
> > +
> > +  slp_tree root = *ref_node;
> > +  if (!vect_match_expression_p (root, MINUS_EXPR))
> > +    return IFN_LAST;
> > +
> > +  /* Check that root is of the form 0 - root1.  */
> > +  auto root_children = SLP_TREE_CHILDREN (root);
> > +  if (root_children.length () != 2)
> > +    return IFN_LAST;
> > +  slp_tree zero1 = root_children[0];
> > +  slp_tree root1 = root_children[1];
> > +
> > +  if (!slp_tree_zero_p (zero1)
> > +      || !vect_match_expression_p (root1, DOT_PROD_EXPR))
> > +    return IFN_LAST;
> > +
> > +  auto root1_children = SLP_TREE_CHILDREN (root1);
> > +  if (root1_children.length () != 3)
> > +    return IFN_LAST;
> > +  slp_tree a_im = root1_children[0];
> > +  slp_tree b_im = root1_children[1];
> > +  slp_tree acc_1 = root1_children[2];
> > +
> > +  /* Check that the imaginary dot product accumulates 0 - root2.  */
> > +  if (!vect_match_expression_p (acc_1, MINUS_EXPR))
> > +    return IFN_LAST;
> > +
> > +  auto acc1_children = SLP_TREE_CHILDREN (acc_1);
> > +  if (acc1_children.length () != 2)
> > +    return IFN_LAST;
> > +  slp_tree zero2 = acc1_children[0];
> > +  slp_tree root2 = acc1_children[1];
> > +
> > +  if (!slp_tree_zero_p (zero2)
> > +      || !vect_match_expression_p (root2, DOT_PROD_EXPR))
> > +    return IFN_LAST;
> > +
> > +  auto root2_children = SLP_TREE_CHILDREN (root2);
> > +  if (root2_children.length () != 3)
> > +    return IFN_LAST;
> > +  slp_tree a_re = root2_children[0];
> > +  slp_tree b_re = root2_children[1];
> > +  slp_tree acc = root2_children[2];
> > +
> > +  /* It's only valid to form complex dot product with integer types.  */
> > +  tree acc_vectype = SLP_TREE_VECTYPE (root);
> > +  if (!acc_vectype || !INTEGRAL_TYPE_P (TREE_TYPE (acc_vectype)))
> > +    return IFN_LAST;
> > +
> > +  tree input_vectype = SLP_TREE_VECTYPE (a_re);
> > +  if (!input_vectype || !INTEGRAL_TYPE_P (TREE_TYPE (input_vectype)))
> > +    return IFN_LAST;
> 
> Move these checks up, as they're cheaper to check.
> 
> > +
> > +  /* Check that it is widening: 8 -> 32 bit.  */
> > +  if (!(TYPE_PRECISION (TREE_TYPE (acc_vectype)) == 32
> > +   && TYPE_PRECISION (TREE_TYPE (input_vectype)) == 8))
> > +    return IFN_LAST;
> 
> Seems odd, in principal 16 -> 64 is possible to, and 8 > 16.
> e.g. for normal dotprod we have two way dot products too.
> 
> So I think this check just needs to enforce that recison of acc_vectype > 
> input_vectype.
> 
> Also it's simpler to use element_precision here.
> 
> > +
> > +  if (TYPE_UNSIGNED (TREE_TYPE (input_vectype)))
> > +    return IFN_LAST;
> > +
> > +  /* The two dot products and the original accumulator must have the same
> > +     result type.  */
> > +  if (SLP_TREE_VECTYPE (root1) != acc_vectype
> > +      || SLP_TREE_VECTYPE (root2) != acc_vectype
> > +      || !acc || SLP_TREE_VECTYPE (acc) != acc_vectype)
> > +    return IFN_LAST;
> > +
> 
> Do we need strict equality or equality in GIMPLE? i.e. can you use 
> types_match (ty1, ty2, ty3)?
> 
> Thanks,
> Tamar
> 
> > +  /* If we got here, it means we've matched the pattern.
> > +     Check that a_re and a_im are adjacent. Likewise for b_re and b_im.  */
> > +  if (!complex_re_im_pair_p (a_re, a_im))
> > +    return IFN_LAST;
> > +
> > +  if (!complex_re_im_pair_p (b_re, b_im))
> > +    return IFN_LAST;
> > +
> > +  if (input_vectype != SLP_TREE_VECTYPE (a_im)
> > +      || input_vectype != SLP_TREE_VECTYPE (b_re)
> > +      || SLP_TREE_VECTYPE (b_re) != SLP_TREE_VECTYPE (b_im))
> > +    return IFN_LAST;
> > +
> > +  if (!vect_pattern_validate_optab (IFN_COMPLEX_DOT_PROD_ROT0,
> > *ref_node,
> > +                               tree_pair (acc_vectype, input_vectype)))
> > +    return IFN_LAST;
> > +
> > +  ops->truncate (0);
> > +  ops->create (5);
> > +  ops->quick_push (a_re);
> > +  ops->quick_push (a_im);
> > +  ops->quick_push (b_re);
> > +  ops->quick_push (b_im);
> > +  ops->quick_push (acc);
> > +
> > +  return IFN_COMPLEX_DOT_PROD_ROT0;
> > +}
> > +
> > 
> > /******************************************************************
> > *************
> >   * complex_operations_pattern class
> > 
> > *******************************************************************
> > ***********/
> > @@ -1573,6 +1927,10 @@ complex_operations_pattern::recognize
> > (slp_tree_to_load_perm_map_t *perm_cache,
> >    if (ifn != IFN_LAST)
> >      return complex_add_pattern::mkInstance (node, &ops, ifn);
> > 
> > +  ifn  = complex_dot_prod_pattern::matches (op, perm_cache, ccache, node,
> > &ops);
> > +  if (ifn != IFN_LAST)
> > +    return complex_dot_prod_pattern::mkInstance (node, &ops, ifn);
> > +
> >    return NULL;
> >  }
> > 
> > --
> > 2.53.0
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to