> -----Original Message-----
> From: Sarvesh Chandra <[email protected]>
> Sent: Saturday, October 24, 2026 7:30 AM
> To: [email protected]
> Cc: [email protected]; Liu, Hongtao <[email protected]>
> Subject: [PATCH] i386: Add AVX512BMM bitreverse lowering
> 
> Add vbitrevb lowering for scalar and generic vector bitreverse operations when
> AVX512BMM is enabled. Prefer BMM over GFNI whenever BMM is available:
> use narrow xmm/ymm forms with AVX512VL, and widen scalar temporaries to
> zmm without AVX512VL. Use GFNI only when AVX512BMM is unavailable.
> 
> Bootstrapped and regression tested on x86_64-pc-linux-gnu.
> 
> gcc/
>       * config/i386/i386-expand.cc (ix86_expand_bitreverse): Prefer BMM
> over
>       GFNI; use zmm vbitrevb when AVX512VL is unavailable.
>       * config/i386/i386-protos.h (ix86_expand_bitreverse): Update the
>       expander declaration.
>       * config/i386/i386.md (bitreverse<mode>2): Enable AVX512BMM and
> call
>       the shared expander.
>       * config/i386/sse.md (bitreverse<mode>2): Reuse
>       avx512bmm_vbitrevb_<mode>.
> 
> gcc/testsuite/
>       * gcc.target/i386/avx512bmm-bitrev-novl-scalar.c: Test BMM zmm
>       fallback and runtime correctness.
>       * gcc.target/i386/avx512bmm-bitreverse-2.c: Test generic vectorizer
>       lowering to vbitrevb.
>       * gcc.target/i386/avx512bmm-gfni-bitreverse.c: Test BMM precedence
>       over GFNI.
>       * gcc.target/i386/gfni-bitreverse-scalar.c: Test the GFNI-only path.
> 
> Co-authored-by: Venkataramanan Kumar <[email protected]>
> Signed-off-by: Sarvesh Chandra <[email protected]>
> ---
>  gcc/config/i386/i386-expand.cc                | 44 +++++++++++++------
>  gcc/config/i386/i386-protos.h                 |  2 +-
>  gcc/config/i386/i386.md                       |  4 +-
>  gcc/config/i386/sse.md                        | 10 +++++
>  .../i386/avx512bmm-bitrev-novl-scalar.c       | 18 ++++++++
>  .../gcc.target/i386/avx512bmm-bitreverse-2.c  | 13 ++++++
>  .../i386/avx512bmm-gfni-bitreverse.c          | 29 ++++++++++++
>  .../gcc.target/i386/gfni-bitreverse-scalar.c  | 30 +++++++++++++
>  8 files changed, 134 insertions(+), 16 deletions(-)  create mode 100644
> gcc/testsuite/gcc.target/i386/avx512bmm-bitrev-novl-scalar.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/avx512bmm-bitreverse-2.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/avx512bmm-gfni-
> bitreverse.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/gfni-bitreverse-scalar.c
> 
> diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
> index 7cc210f8e68..bec00478308 100644
> --- a/gcc/config/i386/i386-expand.cc
> +++ b/gcc/config/i386/i386-expand.cc
> @@ -28151,10 +28151,10 @@ ix86_expand_vector_bf2sf_with_vec_perm (rtx
> dest, rtx src)
>    emit_move_insn (dest, lowpart_subreg (GET_MODE (dest), target,
> vperm_mode));  }
> 
> -/* Implement bitreverse<mode>2 using gf2p8affineqb.  */
> +/* Implement bitreverse<mode>2 using vbitrevb or gf2p8affineqb.  */
> 
>  void
> -ix86_expand_gfni_bitreverse (rtx dest, rtx src)
> +ix86_expand_bitreverse (rtx dest, rtx src)
>  {
>    machine_mode mode = GET_MODE (dest);
>    rtx temp;
> @@ -28201,18 +28201,36 @@ ix86_expand_gfni_bitreverse (rtx dest, rtx src)
>      }
>    src = temp;
>    temp = gen_reg_rtx (V16QImode);
> -  rtx src2 = gen_rtx_CONST_VECTOR (V16QImode,
> -                                gen_rtvec (16, GEN_INT (1), GEN_INT (2),
> -                                           GEN_INT (4), GEN_INT (8),
> -                                           GEN_INT (16), GEN_INT (32),
> -                                           GEN_INT (64), GEN_INT (-128),
> -                                           GEN_INT (1), GEN_INT (2),
> -                                           GEN_INT (4), GEN_INT (8),
> -                                           GEN_INT (16), GEN_INT (32),
> -                                           GEN_INT (64), GEN_INT (-128)));
> -  src2 = validize_mem (force_const_mem (V16QImode, src2));
>    src = lowpart_subreg (V16QImode, src, GET_MODE (src));
> -  emit_insn (gen_vgf2p8affineqb_v16qi (temp, src, src2, const0_rtx));
> +  if (TARGET_AVX512BMM)
> +    {
> +      if (TARGET_AVX512VL)
> +     emit_insn (gen_avx512bmm_vbitrevb_v16qi (temp, src));
> +      else
> +     {
> +       /* Without AVX512VL, use the zmm form.  The upper bytes are
> +          unspecified, but vbitrevb processes each byte independently.  */
It bypasses avx256_optimal/prefer-vector-width=256 and unconditionally 
generates zmm even with -mavx512bmm + mprefer-vector-width=256.

So, maybe it's better to have something like:

 if (TARGET_AVX512BMM && TARGET_AVX512VL)
  {
      AVX512BMM bitreverse.
  }
 Else
  {
     Gfni bitreverse;
  }

> +       rtx srcz = lowpart_subreg (V64QImode, force_reg (V16QImode, src),
> +                                  V16QImode);
> +       rtx tmpz = gen_reg_rtx (V64QImode);
> +       emit_insn (gen_avx512bmm_vbitrevb_v64qi (tmpz, srcz));
> +       emit_move_insn (temp, lowpart_subreg (V16QImode, tmpz,
> V64QImode));
> +     }
> +    }
> +  else if (TARGET_GFNI)
> +    {
> +      rtx src2 = gen_rtx_CONST_VECTOR (V16QImode,
> +                                    gen_rtvec (16, GEN_INT (1), GEN_INT (2),
> +                                               GEN_INT (4), GEN_INT (8),
> +                                               GEN_INT (16), GEN_INT (32),
> +                                               GEN_INT (64), GEN_INT (-
> 128),
> +                                               GEN_INT (1), GEN_INT (2),
> +                                               GEN_INT (4), GEN_INT (8),
> +                                               GEN_INT (16), GEN_INT (32),
> +                                               GEN_INT (64), GEN_INT (-
> 128)));
> +      src2 = validize_mem (force_const_mem (V16QImode, src2));
> +      emit_insn (gen_vgf2p8affineqb_v16qi (temp, src, src2, const0_rtx));
> +    }
>    if (mode == QImode)
>      {
>        rtx temp1 = gen_reg_rtx (SImode); diff --git 
> a/gcc/config/i386/i386-protos.h
> b/gcc/config/i386/i386-protos.h index 106a79dd41e..99814eb5bc1 100644
> --- a/gcc/config/i386/i386-protos.h
> +++ b/gcc/config/i386/i386-protos.h
> @@ -263,7 +263,7 @@ extern rtx ix86_expand_ternlog (machine_mode mode,
> rtx op0, rtx op1, rtx op2,
>                               int idx, rtx target);
>  extern void ix86_expand_vector_sf2bf_with_vec_perm (rtx, rtx);  extern void
> ix86_expand_vector_bf2sf_with_vec_perm (rtx, rtx); -extern void
> ix86_expand_gfni_bitreverse (rtx, rtx);
> +extern void ix86_expand_bitreverse (rtx, rtx);
> 
> 
>  #ifdef TREE_CODE
> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index
> 063e14cb2c1..618813fe69f 100644
> --- a/gcc/config/i386/i386.md
> +++ b/gcc/config/i386/i386.md
> @@ -22967,9 +22967,9 @@
>  (define_expand "bitreverse<mode>2"
>    [(set (match_operand:SWIDWI 0 "register_operand")
>       (bitreverse:SWIDWI (match_operand:SWIDWI 1
> "nonimmediate_operand")))]
> -  "TARGET_GFNI && TARGET_SSE2"
> +  "(TARGET_GFNI || TARGET_AVX512BMM) && TARGET_SSE2"
>  {
> -  ix86_expand_gfni_bitreverse (operands[0], operands[1]);
> +  ix86_expand_bitreverse (operands[0], operands[1]);
>    DONE;
>  })
> 
> diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index
> de092f4b9ae..6cb2a8ba36c 100644
> --- a/gcc/config/i386/sse.md
> +++ b/gcc/config/i386/sse.md
> @@ -33668,3 +33668,13 @@
>    "vbitrevb\t{%1, %0|%0, %1}"
>    [(set_attr "prefix" "evex")
>     (set_attr "mode" "<sseinsnmode>")])
> +
> +(define_expand "bitreverse<mode>2"
> +  [(set (match_operand:VI1_AVX512VL 0 "register_operand")
> +     (bitreverse:VI1_AVX512VL
> +       (match_operand:VI1_AVX512VL 1 "nonimmediate_operand")))]
> +  "TARGET_AVX512BMM"
> +{
> +  emit_insn (gen_avx512bmm_vbitrevb_<mode> (operands[0], operands[1]));
> +  DONE;
> +})
> diff --git a/gcc/testsuite/gcc.target/i386/avx512bmm-bitrev-novl-scalar.c
> b/gcc/testsuite/gcc.target/i386/avx512bmm-bitrev-novl-scalar.c
> new file mode 100644
> index 00000000000..dfd9412a501
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/avx512bmm-bitrev-novl-scalar.c
> @@ -0,0 +1,18 @@
> +/* { dg-do run { target avx512bmm } } */
> +/* { dg-options "-mavx512bmm -mno-avx512vl -mno-gfni -O2" } */
> +/* { dg-final { scan-assembler-times "vbitrevb.*%zmm" 4 } } */
> +/* { dg-final { scan-assembler-not "gf2p8affineqb" } } */
> +/* { dg-final { scan-assembler-not "vgf2p8affineqb" } } */
> +
> +/* BMM without VL must use the zmm form for scalar bitreverse.  */
> +
> +#define main do_test
> +#include "../../gcc.dg/builtin-bitreverse-1.c"
> +#undef main
> +
> +int
> +main ()
> +{
> +  do_test ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/avx512bmm-bitreverse-2.c
> b/gcc/testsuite/gcc.target/i386/avx512bmm-bitreverse-2.c
> new file mode 100644
> index 00000000000..9c82671107d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/avx512bmm-bitreverse-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mavx512bmm -mavx512vl -O3" } */
> +/* { dg-final { scan-assembler "vbitrevb" } } */
> +/* The vectorizer should use vbitrevb for a vector bitreverse rather than
> +   scalarizing it.  */


It appears that even with the vector optab added, the loop is still not being 
vectorized. Should we also enhance the middle-end to support this, potentially 
as a separate follow-up commit? If so, the test should then be updated to 
verify that vectorization is actually taking place.

Reply via email to