> -----Original Message-----
> From: Richard Sandiford <[email protected]>
> Sent: 21 September 2026 17:17
> To: Tamar Christina <[email protected]>
> Cc: [email protected]; nd <[email protected]>; [email protected]
> Subject: Re: [patch v2 2/3]middle-end fold VEC_UNPACK_LO/_HI over poly
> constants
> 
> Tamar Christina <[email protected]> writes:
> > Fold integer VEC_UNPACK_LO_EXPR and VEC_UNPACK_HI_EXPR for suitable
> > variable-length VECTOR_CSTs.
> >
> > The patch adds support for other _LO, _HI expressions but it's expected
> > that those be almost never use but the code unifies the VLA and VLS
> > paths.
> >
> > The fixed-length case already folds.  This extends the same idea to VLA
> > constants when the input has twice as many elements as the output and the
> > selected half can be represented as a valid VECTOR_CST.  This helps fold
> > known predicate constants before RTL expansion, avoiding unnecessary
> > predicate unpack instructions.
> >
> > The idea here is that the VEC_CST should be a repeating pattern with a
> > fill above the minimum VL.
> >
> > i.e. {a, b, a, b, ..., c, c, c} has the requirement that the minimum
> > vector length consume the entire repeating pattern such that the _hi
> > folds contain only {c,c,..}.
> >
> > This show up often with known iteration loops and folding these in gimple
> > allows us to fold away unrolled masks which end up being empty.
> >
> > The selftests cover the foldable low/high cases and cases that should not
> > fold.
> >
> >
> > Bootstrapped Regtested on aarch64-none-linux-gnu,
> > arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
> > -m32, -m64 and no issues.
> >
> > OK for master?
> >
> > Thanks,
> > Tamar
> >
> > gcc/ChangeLog:
> >
> >     * fold-const.cc (const_unop): Fold VEC_UNPACK_LO_EXPR and
> >     VEC_UNPACK_HI_EXPR for suitable variable-length VECTOR_CSTs.
> >     (selftest::test_vec_unpack_folding): New function.
> >     (selftest::fold_const_cc_tests): Call it.
> >
> > ---
> >
> > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > index
> b9262b34f76683fa75e4c5f7ce938753e9c695a2..f919dbab4390127b60430
> 2fad06c7a672e7f9876 100644
> > --- a/gcc/fold-const.cc
> > +++ b/gcc/fold-const.cc
> > @@ -1948,22 +1948,16 @@ const_unop (enum tree_code code, tree type,
> tree arg0)
> >      case VEC_UNPACK_FIX_TRUNC_LO_EXPR:
> >      case VEC_UNPACK_FIX_TRUNC_HI_EXPR:
> >        {
> > -   unsigned HOST_WIDE_INT out_nelts, in_nelts, i;
> > +   poly_uint64 out_nelts = TYPE_VECTOR_SUBPARTS (type);
> > +   unsigned HOST_WIDE_INT const_out_nelts;
> > +   unsigned int encoded_nelts, i, offset = 0;
> >     enum tree_code subcode;
> >
> >     if (TREE_CODE (arg0) != VECTOR_CST)
> >       return NULL_TREE;
> >
> > -   if (!VECTOR_CST_NELTS (arg0).is_constant (&in_nelts))
> > +   if (!known_eq (VECTOR_CST_NELTS (arg0), out_nelts * 2))
> >       return NULL_TREE;
> > -   out_nelts = in_nelts / 2;
> > -   gcc_assert (known_eq (out_nelts, TYPE_VECTOR_SUBPARTS (type)));
> 
> I'm not sure why the assert becomes an early exit.  Shouldn't the
> condition hold for VLA too?
> 

I wasn't actually sure if this would hold for all VLA targets, so I was being
conservative.

> > -
> > -   unsigned int offset = 0;
> > -   if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_UNPACK_LO_EXPR
> > -                              || code == VEC_UNPACK_FLOAT_LO_EXPR
> > -                              || code ==
> VEC_UNPACK_FIX_TRUNC_LO_EXPR))
> > -     offset = out_nelts;
> >
> >     if (code == VEC_UNPACK_LO_EXPR || code ==
> VEC_UNPACK_HI_EXPR)
> >       subcode = NOP_EXPR;
> > @@ -1973,8 +1967,57 @@ const_unop (enum tree_code code, tree type,
> tree arg0)
> >     else
> >       subcode = FIX_TRUNC_EXPR;
> >
> > -   tree_vector_builder elts (type, out_nelts, 1);
> > -   for (i = 0; i < out_nelts; i++)
> > +   bool low_p = (code == VEC_UNPACK_LO_EXPR
> > +                 || code == VEC_UNPACK_FLOAT_LO_EXPR
> > +                 || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR);
> > +   bool use_second_encoded_half_p = (!BYTES_BIG_ENDIAN) ^ low_p;
> > +
> > +   unsigned int nelts_per_pattern = 1;
> > +   tree fill = NULL_TREE;
> > +   if (out_nelts.is_constant (&const_out_nelts))
> > +     {
> > +       encoded_nelts = const_out_nelts;
> > +       if (use_second_encoded_half_p)
> > +         offset = encoded_nelts;
> > +     }
> > +   else
> > +     {
> > +       if (VECTOR_CST_STEPPED_P (arg0))
> > +         return NULL_TREE;
> > +
> > +       unsigned int min_out_nelts = constant_lower_bound (out_nelts);
> > +       encoded_nelts = min_out_nelts;
> > +       fill = fold_convert_const (subcode, TREE_TYPE (type),
> > +                                  vector_cst_elt (arg0, encoded_nelts));
> > +       if (fill == NULL_TREE || !CONSTANT_CLASS_P (fill))
> > +         return NULL_TREE;
> > +
> > +       /* Check that the selected high half is a single repeated
> > +          value.  Since the input is not stepped, checking one full
> > +          pattern is enough to prove the scalable tail.  */
> > +       for (i = 1; i < VECTOR_CST_NPATTERNS (arg0); ++i)
> > +         {
> > +           tree elt
> > +             = fold_convert_const (subcode, TREE_TYPE (type),
> > +                                   vector_cst_elt (arg0,
> > +                                                   encoded_nelts + i));
> > +           if (elt == NULL_TREE
> > +               || !CONSTANT_CLASS_P (elt)
> > +               || !operand_equal_p (elt, fill))
> > +             return NULL_TREE;
> > +         }
> > +
> > +       if (use_second_encoded_half_p)
> > +         {
> > +           encoded_nelts = 1;
> > +           offset = min_out_nelts;
> > +         }
> > +       else
> > +         nelts_per_pattern = 2;
> > +     }
> 
> I don't think the fold needs to be this specific.  If:
> 
> - the input constant isn't stepped, and
> - out_nelts is a multiple of the input's npatterns ("n" below)
> 
> then we know that the input contains n "foreground" elements followed by
> repeats of n "background" elements (which may or may not be the same as
> the foreground elements).  That is:
> 
>   { a1,...,an,b1,....bn,b1,....,bn,b1,...,bn,... }
> 
> If we're unpacking the first half, this is also the shape of the output
> (although the elements are wider).  If we're unpacking the second half,
> the shape is:
> 
>   { b1,...,bn,b1,....bn,b1,....,bn,b1,...,bn,... }
> 
> And this should always be at least as efficient as the status quo for
> VLS as well as VLA.
> 
> So I'd suggest the attached (untested!) patch on top of this one.

Thanks,

I'll give it a try.  Truth be told I don't expect this to really be used all
that much aside from the VECTOR_BOOLEAN_P case. So my patch was
focused on that and so I guess I got tunnel visioned.

You're right that in general as long as the repreating pattern is a multiple
of the minimum VL it should be ok, though just not convinced useful.

I'll throw it in the CI and send version 3....

Tamar.

> 
> Thanks,
> Richard
> 
> 
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index f919dbab439..414a712b936 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -1949,15 +1949,13 @@ const_unop (enum tree_code code, tree type,
> tree arg0)
>      case VEC_UNPACK_FIX_TRUNC_HI_EXPR:
>        {
>       poly_uint64 out_nelts = TYPE_VECTOR_SUBPARTS (type);
> -     unsigned HOST_WIDE_INT const_out_nelts;
> -     unsigned int encoded_nelts, i, offset = 0;
> +     unsigned HOST_WIDE_INT npatterns, nelts_per_pattern, i, offset = 0;
>       enum tree_code subcode;
> 
>       if (TREE_CODE (arg0) != VECTOR_CST)
>         return NULL_TREE;
> 
> -     if (!known_eq (VECTOR_CST_NELTS (arg0), out_nelts * 2))
> -       return NULL_TREE;
> +     gcc_assert (known_eq (VECTOR_CST_NELTS (arg0), out_nelts * 2));
> 
>       if (code == VEC_UNPACK_LO_EXPR || code ==
> VEC_UNPACK_HI_EXPR)
>         subcode = NOP_EXPR;
> @@ -1970,54 +1968,37 @@ const_unop (enum tree_code code, tree type,
> tree arg0)
>       bool low_p = (code == VEC_UNPACK_LO_EXPR
>                     || code == VEC_UNPACK_FLOAT_LO_EXPR
>                     || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR);
> -     bool use_second_encoded_half_p = (!BYTES_BIG_ENDIAN) ^ low_p;
> 
> -     unsigned int nelts_per_pattern = 1;
> -     tree fill = NULL_TREE;
> -     if (out_nelts.is_constant (&const_out_nelts))
> -       {
> -         encoded_nelts = const_out_nelts;
> -         if (use_second_encoded_half_p)
> -           offset = encoded_nelts;
> -       }
> +     npatterns = VECTOR_CST_NPATTERNS (arg0);
> +     nelts_per_pattern = VECTOR_CST_NELTS_PER_PATTERN (arg0);
> +     if (nelts_per_pattern <= 2 && multiple_p (out_nelts, npatterns))
> +       /* The input is:
> +
> +             { a1,...,an,b1,...,bn,b1,...,bn,b1,...,bn,... }
> +
> +          where N == NPATTERNS and where ai == bi for NELTS_PER_PATTERN
> == 1
> +          but not necessarily for NELTS_PER_PATTERN == 2.
> +
> +          When unpacking the first half, the shape of the output is the same
> +          as the input.  That shape would also work for the second half,
> +          but setting the output nelts_per_patterns to 1 is always enough
> +          in that case (since the output always starts on b1).  */
> +       ;
>       else
>         {
> -         if (VECTOR_CST_STEPPED_P (arg0))
> -           return NULL_TREE;
> -
> -         unsigned int min_out_nelts = constant_lower_bound (out_nelts);
> -         encoded_nelts = min_out_nelts;
> -         fill = fold_convert_const (subcode, TREE_TYPE (type),
> -                                    vector_cst_elt (arg0, encoded_nelts));
> -         if (fill == NULL_TREE || !CONSTANT_CLASS_P (fill))
> +         if (!out_nelts.is_constant (&npatterns))
>             return NULL_TREE;
> +         nelts_per_pattern = 1;
> +       }
> 
> -         /* Check that the selected high half is a single repeated
> -            value.  Since the input is not stepped, checking one full
> -            pattern is enough to prove the scalable tail.  */
> -         for (i = 1; i < VECTOR_CST_NPATTERNS (arg0); ++i)
> -           {
> -             tree elt
> -               = fold_convert_const (subcode, TREE_TYPE (type),
> -                                     vector_cst_elt (arg0,
> -                                                     encoded_nelts + i));
> -             if (elt == NULL_TREE
> -                 || !CONSTANT_CLASS_P (elt)
> -                 || !operand_equal_p (elt, fill))
> -               return NULL_TREE;
> -           }
> -
> -         if (use_second_encoded_half_p)
> -           {
> -             encoded_nelts = 1;
> -             offset = min_out_nelts;
> -           }
> -         else
> -           nelts_per_pattern = 2;
> +     if ((!BYTES_BIG_ENDIAN) ^ low_p)
> +       {
> +         offset = npatterns;
> +         nelts_per_pattern = 1;
>         }
> 
> -     tree_vector_builder elts (type, encoded_nelts, nelts_per_pattern);
> -     for (i = 0; i < encoded_nelts; i++)
> +     tree_vector_builder elts (type, npatterns, nelts_per_pattern);
> +     for (i = 0; i < npatterns * nelts_per_pattern; i++)
>         {
>           tree elt = fold_convert_const (subcode, TREE_TYPE (type),
>                                          VECTOR_CST_ELT (arg0, i + offset));
> @@ -2026,10 +2007,6 @@ const_unop (enum tree_code code, tree type,
> tree arg0)
>           elts.quick_push (elt);
>         }
> 
> -     if (nelts_per_pattern == 2)
> -       for (i = 0; i < encoded_nelts; ++i)
> -         elts.quick_push (fill);
> -
>       return elts.build ();
>        }
> 

Reply via email to