Re: [PATCH] [v2] vect: Recognise high-part multiply when the wide type has no vector form

Richard Biener <[email protected]> Thu, 6 Aug 2026 12:46:55 +0200 (CEST)
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
On Thu, 6 Aug 2026, Richard Biener wrote:

> On Thu, 6 Aug 2026, [email protected] wrote:
> 
> > From: Kyrylo Tkachov <[email protected]>
> > 
> > vect_recog_mulhs_pattern turns (a * b) >> N into IFN_MULH and casts the
> > result back to the type of the shift, which the over-widening machinery
> > then drops.  Building that cast needs a vector type for the wide type, and
> > the pattern gives up when there is none.
> > 
> > For a 64-bit high-part multiply the wide type is 128 bits, and no target
> > has a vector of 128-bit integers, so on AArch64 the pattern never fires
> > for scalable SVE and the loop stays scalar.  It does fire under
> > -msve-vector-bits=128, where the prevailing vector size is 128 bits and a
> > one-element TImode vector exists, which is what makes the failure specific
> > to variable-length vectors.
> > 
> > The pattern has already established that the users of the result need at
> > most min_output_precision bits and that NEW_TYPE holds at least that many,
> > so when the wide vector type is unavailable we can simply leave the result
> > at NEW_TYPE precision and skip the cast.
> > 
> > 	for (int i = 0; i < n; i++)
> > 	  d[i] = (uint64_t) (((unsigned __int128) a[i] * b[i]) >> 64);
> > 
> > on -march=armv8.2-a+sve2 -msve-vector-bits=scalable now gives:
> > 
> > 	ld1d	z31.d, p7/z, [x1, x4, lsl 3]
> > 	ld1d	z30.d, p7/z, [x2, x4, lsl 3]
> > 	umulh	z30.d, z30.d, z31.d
> > 	st1d	z30.d, p7, [x0, x4, lsl 3]
> > 
> > rather than:
> > 
> > 	ldr	x5, [x1, x4]
> > 	ldr	x6, [x2, x4]
> > 	umulh	x5, x5, x6
> > 	str	x5, [x0, x4]
> > 
> > Added a gcc.dg/vect/ test. I've added a new vect_mulh_di effective
> > target and added what targets I could find that support the relevant
> > optab at DImode but I've only tested aarch64 myself.
> > 
> > Bootstrapped and regression-tested on aarch64-unknown-linux-gnu.
> > Ok for trunk?
> > Thanks,
> > Kyrill
> > 
> > gcc/ChangeLog:
> > 
> > 	* tree-vect-patterns.cc (vect_recog_mulhs_pattern): Keep the
> > 	pattern result at NEW_TYPE precision when LHS_TYPE has no vector
> > 	type.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* lib/target-supports.exp (check_effective_target_vect_mulh_di):
> > 	New procedure.
> > 	* gcc.dg/vect/vect-mulh-1.c: New test.
> > 	* gcc.dg/vect/vect-mulh-2.c: New test.
> > 	* gcc.target/aarch64/sve/mul_highpart_scalable_1.c: New test.
> > 
> > Signed-off-by: Kyrylo Tkachov <[email protected]>
> > ---
> >  gcc/testsuite/gcc.dg/vect/vect-mulh-1.c       | 52 +++++++++++++++++++
> >  gcc/testsuite/gcc.dg/vect/vect-mulh-2.c       |  9 ++++
> >  .../aarch64/sve/mul_highpart_scalable_1.c     | 36 +++++++++++++
> >  gcc/testsuite/lib/target-supports.exp         | 14 +++++
> >  gcc/tree-vect-patterns.cc                     | 15 ++++--
> >  5 files changed, 122 insertions(+), 4 deletions(-)
> >  create mode 100644 gcc/testsuite/gcc.dg/vect/vect-mulh-1.c
> >  create mode 100644 gcc/testsuite/gcc.dg/vect/vect-mulh-2.c
> >  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/mul_highpart_scalable_1.c
> > 
> > diff --git a/gcc/testsuite/gcc.dg/vect/vect-mulh-1.c b/gcc/testsuite/gcc.dg/vect/vect-mulh-1.c
> > new file mode 100644
> > index 00000000000..06330cc0890
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vect/vect-mulh-1.c
> > @@ -0,0 +1,52 @@
> > +/* { dg-require-effective-target vect_int } */
> > +/* { dg-require-effective-target int128 } */
> > +
> > +#include "tree-vect.h"
> > +
> > +#ifndef SIGNEDNESS
> > +#define SIGNEDNESS signed
> > +#endif
> > +
> > +void __attribute__ ((noipa))
> > +f (SIGNEDNESS long long *restrict a, SIGNEDNESS long long *restrict b,
> > +   SIGNEDNESS long long *restrict c, __INTPTR_TYPE__ n)
> > +{
> > +  for (__INTPTR_TYPE__ i = 0; i < n; ++i)
> > +    a[i] = ((SIGNEDNESS __int128) b[i] * c[i]) >> 64;
> > +}
> > +
> > +#define N 50
> > +#define BASE1 0x1234567890abcdefULL
> > +#define BASE2 0x0fedcba098765432ULL
> > +#define CONST1 0x0123456789abcdefULL
> > +#define CONST2 0x0f0e0d0c0b0a0908ULL
> > +
> > +int
> > +main (void)
> > +{
> > +  check_vect ();
> > +
> > +  SIGNEDNESS long long a[N], b[N], c[N];
> > +  /* Compute the inputs with wrapping unsigned arithmetic so that they cover
> > +     the whole 64-bit range without overflowing a signed type.  */
> > +  for (int i = 0; i < N; ++i)
> > +    {
> > +      b[i] = (SIGNEDNESS long long) (BASE1 + (unsigned long long) i * CONST1);
> > +      c[i] = (SIGNEDNESS long long) (BASE2 + (unsigned long long) i * CONST2);
> > +      asm volatile ("" ::: "memory");
> > +    }
> > +  b[0] = 0;
> > +  c[0] = -1;
> > +  b[1] = -1;
> > +  c[1] = -1;
> > +  f (a, b, c, N);
> > +#pragma GCC novector
> > +  for (int i = 0; i < N; ++i)
> > +    if (a[i] != (SIGNEDNESS long long) (((SIGNEDNESS __int128) b[i] * c[i])
> > +					>> 64))
> > +      __builtin_abort ();
> > +  return 0;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump {\.MULH} "vect" { target vect_mulh_di } } } */
> > +/* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" { target vect_mulh_di } } } */
> > diff --git a/gcc/testsuite/gcc.dg/vect/vect-mulh-2.c b/gcc/testsuite/gcc.dg/vect/vect-mulh-2.c
> > new file mode 100644
> > index 00000000000..0249caea3de
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vect/vect-mulh-2.c
> > @@ -0,0 +1,9 @@
> > +/* { dg-require-effective-target vect_int } */
> > +/* { dg-require-effective-target int128 } */
> > +
> > +#define SIGNEDNESS unsigned
> > +
> > +#include "vect-mulh-1.c"
> > +
> > +/* { dg-final { scan-tree-dump {\.MULH} "vect" { target vect_mulh_di } } } */
> > +/* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" { target vect_mulh_di } } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/mul_highpart_scalable_1.c b/gcc/testsuite/gcc.target/aarch64/sve/mul_highpart_scalable_1.c
> > new file mode 100644
> > index 00000000000..2d19cde259e
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/sve/mul_highpart_scalable_1.c
> > @@ -0,0 +1,36 @@
> > +/* A 64-bit high-part multiply has a 128-bit product type, and there is no
> > +   scalable vector of 128-bit integers.  Check that the vectoriser recognises
> > +   the high-part multiply anyway.  */
> > +/* { dg-do compile } */
> > +/* { dg-options "-O2 -ftree-vectorize -march=armv8.2-a+sve2 -mautovec-preference=sve-only -msve-vector-bits=scalable" } */
> > +/* { dg-final { check-function-bodies "**" "" } } */
> > +
> > +#include <stdint.h>
> > +
> > +/*
> > +** mulh_s64:
> > +** ...
> > +**	smulh	z[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d
> > +** ...
> > +*/
> > +void __attribute__ ((noipa))
> > +mulh_s64 (int64_t *restrict dst, int64_t *restrict a, int64_t *restrict b,
> > +	  int count)
> > +{
> > +  for (int i = 0; i < count; ++i)
> > +    dst[i] = (int64_t) (((__int128) a[i] * b[i]) >> 64);
> > +}
> > +
> > +/*
> > +** mulh_u64:
> > +** ...
> > +**	umulh	z[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d
> > +** ...
> > +*/
> > +void __attribute__ ((noipa))
> > +mulh_u64 (uint64_t *restrict dst, uint64_t *restrict a, uint64_t *restrict b,
> > +	  int count)
> > +{
> > +  for (int i = 0; i < count; ++i)
> > +    dst[i] = (uint64_t) (((unsigned __int128) a[i] * b[i]) >> 64);
> > +}
> > diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
> > index 42cbb5ce3df..89784ca68cd 100644
> > --- a/gcc/testsuite/lib/target-supports.exp
> > +++ b/gcc/testsuite/lib/target-supports.exp
> > @@ -9302,6 +9302,20 @@ proc check_effective_target_vect_mulhrs_hi {} {
> >  		   && [check_effective_target_aarch64_sve2] }]
> >  }
> >  
> > +# Return 1 if the target plus current options supports both signed
> > +# and unsigned high-part multiplication on vectors of 8-byte integers.
> > +
> > +proc check_effective_target_vect_mulh_di {} {
> > +    return [expr { ([istarget aarch64*-*-*]
> > +		    && [check_effective_target_aarch64_sve])
> > +		   || ([istarget riscv*-*-*]
> > +		       && [check_effective_target_riscv_v])
> > +		   || ([istarget powerpc*-*-*]
> > +		       && [check_effective_target_has_arch_pwr10])
> > +		   || ([istarget loongarch*-*-*]
> > +		       && [check_effective_target_loongarch_sx]) }]
> > +}
> > +
> >  # Return 1 if the target plus current options supports signed division
> >  # by power-of-2 operations on vectors of 4-byte integers.
> >  
> > diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
> > index c57e215be57..185b28302d4 100644
> > --- a/gcc/tree-vect-patterns.cc
> > +++ b/gcc/tree-vect-patterns.cc
> > @@ -3412,11 +3412,18 @@ vect_recog_mulhs_pattern (vec_info *vinfo,
> >  	    (ifn, new_vectype, OPTIMIZE_FOR_SPEED))
> >      return NULL;
> >  
> > -  /* The IR requires a valid vector type for the cast result, even though
> > -     it's likely to be discarded.  */
> > +  /* *TYPE_OUT is the vector type of the trailing cast back to LHS_TYPE, which
> > +     the over-widening machinery removes anyway.  Drop the cast when LHS_TYPE
> > +     has no vector type, as for the 128-bit product of a 64-bit high-part
> > +     multiply.  The uses need at most TARGET_PRECISION bits, which NEW_TYPE
> > +     has.  */
> > +  tree out_type = lhs_type;
> >    *type_out = get_vectype_for_scalar_type (vinfo, lhs_type);
> >    if (!*type_out)
> > -    return NULL;
> > +    {
> > +      out_type = new_type;
> > +      *type_out = new_vectype;
> > +    }
> >  
> >    /* Generate the IFN_MULHRS call.  */
> >    tree new_var = vect_recog_temp_ssa_var (new_type, NULL);
> > @@ -3432,7 +3439,7 @@ vect_recog_mulhs_pattern (vec_info *vinfo,
> >      dump_printf_loc (MSG_NOTE, vect_location,
> >  		     "created pattern stmt: %G", (gimple *) mulhrs_stmt);
> >  
> > -  return vect_convert_output (vinfo, last_stmt_info, lhs_type,
> > +  return vect_convert_output (vinfo, last_stmt_info, out_type,
> >  			      mulhrs_stmt, new_vectype);
> 
> So as I understand this replaces a stmt (aka record a pattern stmt)
> with a pattern stmt with a different LHS type?
> 
> That's unexpected and will cause interesting effects downstream.

So instead do the conversion but leave new_vectype NULL.

Richard.

> Richard.
> 
> >  }
> >  
> > 
> 
> 

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