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. */
+ 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. */
+
+void
+rev (unsigned char *restrict d, const unsigned char *restrict s, int n)
+{
+ for (int i = 0; i < n; i++)
+ d[i] = __builtin_bitreverse8 (s[i]);
+}
+
diff --git a/gcc/testsuite/gcc.target/i386/avx512bmm-gfni-bitreverse.c
b/gcc/testsuite/gcc.target/i386/avx512bmm-gfni-bitreverse.c
new file mode 100644
index 00000000000..e7610a5cc80
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/avx512bmm-gfni-bitreverse.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-mavx512bmm -mno-avx512vl -mgfni -O2" } */
+/* { dg-final { scan-assembler-times "vbitrevb.*%zmm" 4 } } */
+/* { dg-final { scan-assembler-not "gf2p8affineqb" } } */
+
+/* BMM is preferred over GFNI; without VL it uses the zmm form. */
+unsigned char
+br8 (unsigned char x)
+{
+ return __builtin_bitreverse8 (x);
+}
+
+unsigned short
+br16 (unsigned short x)
+{
+ return __builtin_bitreverse16 (x);
+}
+
+unsigned int
+br32 (unsigned int x)
+{
+ return __builtin_bitreverse32 (x);
+}
+
+unsigned long long
+br64 (unsigned long long x)
+{
+ return __builtin_bitreverse64 (x);
+}
diff --git a/gcc/testsuite/gcc.target/i386/gfni-bitreverse-scalar.c
b/gcc/testsuite/gcc.target/i386/gfni-bitreverse-scalar.c
new file mode 100644
index 00000000000..94c032f27b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/gfni-bitreverse-scalar.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-mgfni -mno-avx512bmm -O2" } */
+/* { dg-final { scan-assembler-times "gf2p8affineqb" 4 } } */
+/* { dg-final { scan-assembler-not "vbitrevb" } } */
+/* { dg-final { scan-assembler-not "vgf2p8affineqb" } } */
+
+/* GFNI-only scalar bitreverse must retain the existing legacy GFNI path. */
+unsigned char
+br8 (unsigned char x)
+{
+ return __builtin_bitreverse8 (x);
+}
+
+unsigned short
+br16 (unsigned short x)
+{
+ return __builtin_bitreverse16 (x);
+}
+
+unsigned int
+br32 (unsigned int x)
+{
+ return __builtin_bitreverse32 (x);
+}
+
+unsigned long long
+br64 (unsigned long long x)
+{
+ return __builtin_bitreverse64 (x);
+}
--
2.34.1