[1/4] Optimize vec_splats of constant vec_extract for V2DI/V2DF, PR target 99293.
Commit Message
Optimize vec_splats of constant vec_extract for V2DI/V2DF, PR target 99293.
In PR target/99293, it was pointed out that doing:
vector long long dest0, dest1, src;
/* ... */
dest0 = vec_splats (vec_extract (src, 0));
dest1 = vec_splats (vec_extract (src, 1));
would generate slower code.
It generates the following code on power8:
;; vec_splats (vec_extract (src, 0))
xxpermdi 0,34,34,3
xxpermdi 34,0,0,0
;; vec_splats (vec_extract (src, 1))
xxlor 0,34,34
xxpermdi 34,0,0,0
However on power9 and power10 it generates:
;; vec_splats (vec_extract (src, 0))
mfvsld 3,34
mtvsrdd 34,9,9
;; vec_splats (vec_extract (src, 1))
mfvsrd 9,34
mtvsrdd 34,9,9
This is due to the power9 having the mfvsrld instruction which can extract
either 64-bit element into a GPR. While there are alternatives for both
vector registers and GPR registers, the register allocator prefers to put
DImode into GPR registers.
However in this case, it is better to have a single combiner pattern that
can generate a single xxpermdi, instead of doing 2 insnsns (the extract
and then the concat). This is particularly true if the two operations are
move from vector register and move to vector register.
I have built Spec 2017 with this patch installed, and the cam4_r benchmark
is the only benchmark that generated different code. On a power9, I did
not notice any significant changes in the runtime of cam4_r.
I have built bootstrap versions on the following systems. There were no
regressions in the runs:
Power9 little endian, --with-cpu=power9
Power10 little endian, --with-cpu=power10
Power8 big endian, --with-cpu=power8 (both 32-bit & 64-bit tests)
Can I install this into the trunk? After a burn-in period, can I backport
and install this into GCC 11 and GCC 10 branches?
2022-03-28 Michael Meissner <meissner@linux.ibm.com>
gcc/
PR target/99293
* config/rs6000/vsx.md (vsx_splat_const_extract_<mode>): New
combiner insn.
gcc/testsuite:
PR target/99293
* gcc.target/powerpc/pr99293.c: New test.
---
gcc/config/rs6000/vsx.md | 19 ++++++++++++
gcc/testsuite/gcc.target/powerpc/pr99293.c | 36 ++++++++++++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/powerpc/pr99293.c
Comments
On Mon, Mar 28, 2022 at 12:26:02PM -0400, Michael Meissner wrote:
> However on power9 and power10 it generates:
>
> ;; vec_splats (vec_extract (src, 0))
> mfvsld 3,34
> mtvsrdd 34,9,9
>
> ;; vec_splats (vec_extract (src, 1))
> mfvsrd 9,34
> mtvsrdd 34,9,9
>
> This is due to the power9 having the mfvsrld instruction which can extract
> either 64-bit element into a GPR. While there are alternatives for both
> vector registers and GPR registers, the register allocator prefers to put
> DImode into GPR registers.
As I said in comment 2 in the PR, it is because we do not have this
pattern yet, we only have vec_concat. The instruction combiner tries
to use this pattern, but it doesn't exist :-)
> +;; Optimize SPLAT of an extract from a V2DF/V2DI vector with a constant element
> +;; PR target/99293
> +(define_insn "*vsx_splat_const_extract_<mode>"
> + [(set (match_operand:VSX_D 0 "vsx_register_operand" "=wa")
> + (vec_duplicate:VSX_D
> + (vec_select:<VS_scalar>
> + (match_operand:VSX_D 1 "vsx_register_operand" "wa")
> + (parallel [(match_operand 2 "const_0_to_1_operand" "n")]))))]
> + "VECTOR_MEM_VSX_P (<MODE>mode)"
> +{
> + int which_word = INTVAL (operands[2]);
dword, not word.
> + if (!BYTES_BIG_ENDIAN)
> + which_word = 1 - which_word;
> +
> + operands[3] = GEN_INT (which_word ? 3 : 0);
> + return "xxpermdi %x0,%x1,%x1,%3";
Please use gen_vsx_xxspltd_v2di, instead. Which itself should not use
an unspec of course, but that is another patch.
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/powerpc/pr99293.c
> @@ -0,0 +1,36 @@
> +/* { dg-do compile { target powerpc*-*-* } } */
Don't. This is gcc.target/powerpc/ already.
> +/* { dg-final { scan-assembler-times "xxpermdi" 4 } } */
\m \M
Thanks,
Segher
On Mon, Mar 28, 2022 at 12:14:09PM -0500, Segher Boessenkool wrote:
> On Mon, Mar 28, 2022 at 12:26:02PM -0400, Michael Meissner wrote:
> > However on power9 and power10 it generates:
> >
> > ;; vec_splats (vec_extract (src, 0))
> > mfvsld 3,34
> > mtvsrdd 34,9,9
> >
> > ;; vec_splats (vec_extract (src, 1))
> > mfvsrd 9,34
> > mtvsrdd 34,9,9
> >
> > This is due to the power9 having the mfvsrld instruction which can extract
> > either 64-bit element into a GPR. While there are alternatives for both
> > vector registers and GPR registers, the register allocator prefers to put
> > DImode into GPR registers.
>
> As I said in comment 2 in the PR, it is because we do not have this
> pattern yet, we only have vec_concat. The instruction combiner tries
> to use this pattern, but it doesn't exist :-)
Ok. I will create that function and see if it works.
> > +;; Optimize SPLAT of an extract from a V2DF/V2DI vector with a constant element
> > +;; PR target/99293
> > +(define_insn "*vsx_splat_const_extract_<mode>"
> > + [(set (match_operand:VSX_D 0 "vsx_register_operand" "=wa")
> > + (vec_duplicate:VSX_D
> > + (vec_select:<VS_scalar>
> > + (match_operand:VSX_D 1 "vsx_register_operand" "wa")
> > + (parallel [(match_operand 2 "const_0_to_1_operand" "n")]))))]
> > + "VECTOR_MEM_VSX_P (<MODE>mode)"
> > +{
> > + int which_word = INTVAL (operands[2]);
>
> dword, not word.
Ok.
> > + if (!BYTES_BIG_ENDIAN)
> > + which_word = 1 - which_word;
> > +
> > + operands[3] = GEN_INT (which_word ? 3 : 0);
> > + return "xxpermdi %x0,%x1,%x1,%3";
>
> Please use gen_vsx_xxspltd_v2di, instead. Which itself should not use
> an unspec of course, but that is another patch.
There is also vsx_concat_<mode>_3 which does not use an UNSPEC. Maybe
eventually rewrite vsx_xxspltd_v2di to be a define_insn_and_split.
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/powerpc/pr99293.c
> > @@ -0,0 +1,36 @@
> > +/* { dg-do compile { target powerpc*-*-* } } */
>
> Don't. This is gcc.target/powerpc/ already.
Ok.
> > +/* { dg-final { scan-assembler-times "xxpermdi" 4 } } */
>
> \m \M
Ok.
On Mon, Mar 28, 2022 at 06:30:41PM -0400, Michael Meissner wrote:
> On Mon, Mar 28, 2022 at 12:14:09PM -0500, Segher Boessenkool wrote:
> > On Mon, Mar 28, 2022 at 12:26:02PM -0400, Michael Meissner wrote:
> > > However on power9 and power10 it generates:
> > >
> > > ;; vec_splats (vec_extract (src, 0))
> > > mfvsld 3,34
> > > mtvsrdd 34,9,9
> > >
> > > ;; vec_splats (vec_extract (src, 1))
> > > mfvsrd 9,34
> > > mtvsrdd 34,9,9
> > >
> > > This is due to the power9 having the mfvsrld instruction which can extract
> > > either 64-bit element into a GPR. While there are alternatives for both
> > > vector registers and GPR registers, the register allocator prefers to put
> > > DImode into GPR registers.
> >
> > As I said in comment 2 in the PR, it is because we do not have this
> > pattern yet, we only have vec_concat. The instruction combiner tries
> > to use this pattern, but it doesn't exist :-)
>
> Ok. I will create that function and see if it works.
That is what this patch *does*! *That* needs to be in the commit
message!
> > > + if (!BYTES_BIG_ENDIAN)
> > > + which_word = 1 - which_word;
> > > +
> > > + operands[3] = GEN_INT (which_word ? 3 : 0);
> > > + return "xxpermdi %x0,%x1,%x1,%3";
> >
> > Please use gen_vsx_xxspltd_v2di, instead. Which itself should not use
> > an unspec of course, but that is another patch.
>
> There is also vsx_concat_<mode>_3 which does not use an UNSPEC. Maybe
> eventually rewrite vsx_xxspltd_v2di to be a define_insn_and_split.
It almost always is a bad idea to have a 1->1 split: you are rewriting
the canonical way to write something to something non-canonical. (Where
"canonical" means "actually canonical, and/or what GCC generates
anyway").
It doesn't save anything over an extra define_insn either, anyway?
Segher
@@ -4616,6 +4616,25 @@ (define_insn "vsx_splat_v4si_di"
[(set_attr "type" "vecperm")
(set_attr "isa" "p8v,*")])
+;; Optimize SPLAT of an extract from a V2DF/V2DI vector with a constant element
+;; PR target/99293
+(define_insn "*vsx_splat_const_extract_<mode>"
+ [(set (match_operand:VSX_D 0 "vsx_register_operand" "=wa")
+ (vec_duplicate:VSX_D
+ (vec_select:<VS_scalar>
+ (match_operand:VSX_D 1 "vsx_register_operand" "wa")
+ (parallel [(match_operand 2 "const_0_to_1_operand" "n")]))))]
+ "VECTOR_MEM_VSX_P (<MODE>mode)"
+{
+ int which_word = INTVAL (operands[2]);
+ if (!BYTES_BIG_ENDIAN)
+ which_word = 1 - which_word;
+
+ operands[3] = GEN_INT (which_word ? 3 : 0);
+ return "xxpermdi %x0,%x1,%x1,%3";
+}
+ [(set_attr "type" "vecperm")])
+
;; V4SF splat (ISA 3.0)
(define_insn_and_split "vsx_splat_v4sf"
[(set (match_operand:V4SF 0 "vsx_register_operand" "=wa,wa,wa")
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-do compile { target powerpc*-*-* } } */
+/* { dg-require-effective-target powerpc_vsx_ok } */
+/* { dg-options "-O2 -mvsx" } */
+
+/* Test for PR 99263, which wants to do:
+ __builtin_vec_splats (__builtin_vec_extract (v, n))
+
+ where v is a V2DF or V2DI vector and n is either 0 or 1. Previously the
+ compiler would do a direct move to the GPR registers to select the item and
+ a direct move from the GPR registers to do the splat. */
+
+vector long long
+splat_dup_ll_0 (vector long long v)
+{
+ return __builtin_vec_splats (__builtin_vec_extract (v, 0));
+}
+
+vector long long
+splat_dup_ll_1 (vector long long v)
+{
+ return __builtin_vec_splats (__builtin_vec_extract (v, 1));
+}
+
+vector double
+splat_dup_d_0 (vector double v)
+{
+ return __builtin_vec_splats (__builtin_vec_extract (v, 0));
+}
+
+vector double
+splat_dup_d_1 (vector double v)
+{
+ return __builtin_vec_splats (__builtin_vec_extract (v, 1));
+}
+
+/* { dg-final { scan-assembler-times "xxpermdi" 4 } } */