Projects
openEuler:24.03:SP1:Everything
gcc
_service:tar_scm:0099-Enable-Transposed-SLP.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:0099-Enable-Transposed-SLP.patch of Package gcc
From 0dd3b8532f35486bd5db2c71342c8dfed4c0893a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=99=A8=E5=8D=89?= <zhengchenhui1@huawei.com> Date: Thu, 25 Jul 2024 17:25:23 +0800 Subject: [PATCH] Enable Transposed SLP. --- gcc/common.opt | 4 + gcc/testsuite/gcc.dg/vect/transpose-1.c | 53 + gcc/testsuite/gcc.dg/vect/transpose-2.c | 50 + gcc/testsuite/gcc.dg/vect/transpose-3.c | 54 + gcc/testsuite/gcc.dg/vect/transpose-4.c | 53 + gcc/testsuite/gcc.dg/vect/transpose-5.c | 74 ++ gcc/testsuite/gcc.dg/vect/transpose-6.c | 67 + gcc/testsuite/gcc.dg/vect/transpose-7.c | 53 + gcc/testsuite/gcc.dg/vect/transpose-8.c | 53 + gcc/testsuite/gcc.dg/vect/vect.exp | 7 + gcc/tree-loop-distribution.cc | 1464 ++++++++++++++++++++- gcc/tree-vect-data-refs.cc | 237 ++++ gcc/tree-vect-loop.cc | 42 +- gcc/tree-vect-patterns.cc | 4 +- gcc/tree-vect-slp.cc | 1553 ++++++++++++++++++++--- gcc/tree-vect-stmts.cc | 973 +++++++++++++- gcc/tree-vectorizer.h | 96 +- 17 files changed, 4648 insertions(+), 189 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-1.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-2.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-3.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-4.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-5.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-6.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-7.c create mode 100644 gcc/testsuite/gcc.dg/vect/transpose-8.c diff --git a/gcc/common.opt b/gcc/common.opt index b18f0b944..5958c4e0b 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -3221,6 +3221,10 @@ ftree-slp-vectorize Common Var(flag_tree_slp_vectorize) Optimization EnabledBy(ftree-vectorize) Enable basic block vectorization (SLP) on trees. +ftree-slp-transpose-vectorize +Common Var(flag_tree_slp_transpose_vectorize) Optimization Init(0) +Enable basic block vectorization (SLP) for transposed stores and loads on trees. + fvect-cost-model= Common Joined RejectNegative Enum(vect_cost_model) Var(flag_vect_cost_model) Init(VECT_COST_MODEL_DEFAULT) Optimization -fvect-cost-model=[unlimited|dynamic|cheap|very-cheap] Specifies the cost model for vectorization. diff --git a/gcc/testsuite/gcc.dg/vect/transpose-1.c b/gcc/testsuite/gcc.dg/vect/transpose-1.c new file mode 100644 index 000000000..8237a8b9e --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-1.c @@ -0,0 +1,53 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 4 +#define M 256 + +int foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned c0[N], c1[N], c2[N], c3[N], c4[N], c5[N], c6[N], c7[N]; + for (i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + c2[i] = pix1[2] - pix2[2]; + c3[i] = pix1[3] - pix2[3]; + c4[i] = pix1[4] - pix2[4]; + c5[i] = pix1[5] - pix2[5]; + c6[i] = pix1[6] - pix2[6]; + c7[i] = pix1[7] - pix2[7]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i] + c2[i] + c3[i] + c4[i] + c5[i] + c6[i] + c7[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 16; + int i2 = 8; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 2; + input2[i] = i; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 1264) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-2.c b/gcc/testsuite/gcc.dg/vect/transpose-2.c new file mode 100644 index 000000000..fdf4dbd96 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-2.c @@ -0,0 +1,50 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-additional-options "-fno-tree-loop-vectorize -fno-tree-dse" } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 8 +#define M 256 + +int foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned short c0[N], c1[N], c2[N], c3[N], c4[N], c5[N], c6[N], c7[N]; + for (i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + c2[i] = pix1[2] - pix2[2]; + c3[i] = pix1[3] - pix2[3]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i] + c2[i] + c3[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 5; + int i2 = 4; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 4; + input2[i] = i * 2; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 1440) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-3.c b/gcc/testsuite/gcc.dg/vect/transpose-3.c new file mode 100644 index 000000000..e492e3717 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-3.c @@ -0,0 +1,54 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-additional-options "-fno-tree-loop-vectorize -fno-tree-dse -fno-tree-fre" } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 4 +#define M 256 + +int foo (unsigned short *pix1, int i_pix1, unsigned short *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned c0[N], c1[N], c2[N], c3[N], c4[N], c5[N], c6[N], c7[N]; + for (i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + c2[i] = pix1[2] - pix2[2]; + c3[i] = pix1[3] - pix2[3]; + c4[i] = pix1[4] - pix2[4]; + c5[i] = pix1[5] - pix2[5]; + c6[i] = pix1[6] - pix2[6]; + c7[i] = pix1[7] - pix2[7]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i] + c2[i] + c3[i] + c4[i] + c5[i] + c6[i] + c7[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned short input1[M]; + unsigned short input2[M]; + int i1 = 8; + int i2 = 4; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 4; + input2[i] = i; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 1680) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-4.c b/gcc/testsuite/gcc.dg/vect/transpose-4.c new file mode 100644 index 000000000..0b4adea9b --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-4.c @@ -0,0 +1,53 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 4 +#define M 256 + +int foo (unsigned *pix1, int i_pix1, unsigned *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned c0[N], c1[N], c2[N], c3[N], c4[N], c5[N], c6[N], c7[N]; + for (i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + c2[i] = pix1[2] - pix2[2]; + c3[i] = pix1[3] - pix2[3]; + c4[i] = pix1[4] - pix2[4]; + c5[i] = pix1[5] - pix2[5]; + c6[i] = pix1[6] - pix2[6]; + c7[i] = pix1[7] - pix2[7]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i] + c2[i] + c3[i] + c4[i] + c5[i] + c6[i] + c7[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned input1[M]; + unsigned input2[M]; + int i1 = 12; + int i2 = 6; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 7; + input2[i] = i * 3; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 3616) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-5.c b/gcc/testsuite/gcc.dg/vect/transpose-5.c new file mode 100644 index 000000000..040dedf1b --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-5.c @@ -0,0 +1,74 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-additional-options "-fno-tree-dse -fno-tree-fre" } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "tree-vect.h" + +#define N 4 +#define M 256 +#define eps 1e-8 + +double foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + unsigned a0[N]; + unsigned a1[N]; + unsigned a2[N]; + unsigned a3[N]; + + int b0[N]; + int b1[N]; + int b2[N]; + int b3[N]; + + for (int i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + a0[i] = (pix1[0] - pix2[0]) + ((pix1[4] + pix2[4]) << 16); + a1[i] = (pix1[1] - pix2[1]) + ((pix1[5] + pix2[5]) << 16); + a2[i] = (pix1[2] - pix2[2]) + ((pix1[6] + pix2[6]) << 16); + a3[i] = (pix1[3] - pix2[3]) + ((pix1[7] + pix2[7]) << 16); + } + + for (int i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + b0[i] = (pix1[0] - pix2[0]) + (pix1[4] + pix2[4]); + b1[i] = (pix1[1] - pix2[1]) + (pix1[5] + pix2[5]); + b2[i] = (pix1[2] - pix2[2]) + (pix1[6] + pix2[6]); + b3[i] = (pix1[3] - pix2[3]) + (pix1[7] + pix2[7]); + } + + double sum = 0; + for (int i = 0; i < N; i++) + { + sum += a0[i] + a1[i] + a2[i] + a3[i] + b0[i] + b1[i] + b2[i] + b3[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 8; + int i2 = 3; + unsigned char m = 2; + unsigned short n = 12; + float t = 3.0; + double k = 4.2; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 6; + input2[i] = i * 3; + } + double sum = foo (input1, i1, input2, i2); + if (fabs (sum - 78648144) > eps) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ +/* { dg-final { scan-tree-dump-times "vectorizable_store for slp transpose" 2 "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-6.c b/gcc/testsuite/gcc.dg/vect/transpose-6.c new file mode 100644 index 000000000..3e134ac02 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-6.c @@ -0,0 +1,67 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-require-effective-target vect_int } */ +/* { dg-require-effective-target vect_float } */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "tree-vect.h" + +#define N 4 +#define M 256 +#define eps 1e-8 + +float foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + unsigned a0[N]; + unsigned a1[N]; + unsigned a2[N]; + unsigned a3[N]; + + float c0[N]; + float c1[N]; + float c2[N]; + float c3[N]; + + for (int i = 0; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + a0[i] = (pix1[0] - pix2[0]) + ((pix1[4] - pix2[4]) << 16); + a1[i] = (pix1[1] - pix2[1]) + ((pix1[5] - pix2[5]) << 16); + a2[i] = (pix1[2] - pix2[2]) + ((pix1[6] - pix2[6]) << 16); + a3[i] = (pix1[3] - pix2[3]) + ((pix1[7] - pix2[7]) << 16); + + c0[i] = (pix1[0] * pix2[0]) + (pix1[4] * pix2[4]); + c1[i] = (pix1[1] * pix2[1]) + (pix1[5] * pix2[5]); + c2[i] = (pix1[2] * pix2[2]) + (pix1[6] * pix2[6]); + c3[i] = (pix1[3] * pix2[3]) + (pix1[7] * pix2[7]); + } + + float sum = 0; + for (int i = 0; i < N; i++) + { + sum += a0[i] + a1[i] + a2[i] + a3[i] + c0[i] + c1[i] + c2[i] + c3[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 18; + int i2 = 6; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 4; + input2[i] = i * 2; + } + float sum = foo (input1, i1, input2, i2); + if (fabs (sum - 106041168) > eps) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ +/* { dg-final { scan-tree-dump-times "vectorizable_store for slp transpose" 2 "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-7.c b/gcc/testsuite/gcc.dg/vect/transpose-7.c new file mode 100644 index 000000000..8ba1b1b6d --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-7.c @@ -0,0 +1,53 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-additional-options "-fno-tree-loop-vectorize -fno-tree-dse" } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 16 +#define M 256 + +int foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned char c0[N], c1[N]; + for (int i = 0; i < N/2; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + } + for (int i = N/2; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 6; + int i2 = 4; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 5; + input2[i] = i * 2; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 3280) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/transpose-8.c b/gcc/testsuite/gcc.dg/vect/transpose-8.c new file mode 100644 index 000000000..a154f012a --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/transpose-8.c @@ -0,0 +1,53 @@ +/* { dg-do compile { target { aarch64*-*-linux* } } } */ +/* { dg-additional-options "-fno-tree-loop-vectorize" } */ +/* { dg-require-effective-target vect_int } */ +#include <stdio.h> +#include <stdlib.h> +#include "tree-vect.h" + +#define N 32 +#define M 256 + +int foo (unsigned char *pix1, int i_pix1, unsigned char *pix2, int i_pix2) +{ + int i = 0; + int sum = 0; + unsigned char c0[N], c1[N]; + for (int i = 0; i < N/2; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + } + for (int i = N/2; i < N; i++, pix1 += i_pix1, pix2 += i_pix2) + { + c0[i] = pix1[0] - pix2[0]; + c1[i] = pix1[1] - pix2[1]; + } + for (int i = 0; i < N; i++) + { + sum += c0[i] + c1[i]; + } + return sum; +} + +int main (int argc, const char* argv[]) +{ + unsigned char input1[M]; + unsigned char input2[M]; + int i1 = 6; + int i2 = 4; + check_vect (); + for (int i = 0; i < M; i++) + { + input1[i] = i * 5; + input2[i] = i * 2; + } + int sum = foo (input1, i1, input2, i2); + if (sum != 7584) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-tree-dump "vectorized using transposed version" "slp1" } } */ diff --git a/gcc/testsuite/gcc.dg/vect/vect.exp b/gcc/testsuite/gcc.dg/vect/vect.exp index dcaef1e0a..ae5212411 100644 --- a/gcc/testsuite/gcc.dg/vect/vect.exp +++ b/gcc/testsuite/gcc.dg/vect/vect.exp @@ -117,6 +117,13 @@ et-dg-runtest dg-runtest [lsort \ [glob -nocomplain $srcdir/$subdir/no-vfa-*.\[cS\]]] \ "" $DEFAULT_VECTCFLAGS +# -ftree-slp-transpose-vectorize SLP tests +set VECT_SLP_CFLAGS $SAVED_VECT_SLP_CFLAGS +lappend VECT_SLP_CFLAGS "-ftree-slp-transpose-vectorize" +et-dg-runtest dg-runtest [lsort \ + [glob -nocomplain $srcdir/$subdir/transpose-*.\[cS\]]] \ + "" "-ftree-slp-transpose-vectorize -fdump-tree-slp-details -O3" + # -ffast-math tests set DEFAULT_VECTCFLAGS $SAVED_DEFAULT_VECTCFLAGS lappend DEFAULT_VECTCFLAGS "-ffast-math" diff --git a/gcc/tree-loop-distribution.cc b/gcc/tree-loop-distribution.cc index 606eb05e6..8d118e987 100644 --- a/gcc/tree-loop-distribution.cc +++ b/gcc/tree-loop-distribution.cc @@ -36,6 +36,47 @@ along with GCC; see the file COPYING3. If not see | D(I) = A(I-1)*E |ENDDO + If an unvectorizable loop has grouped loads, and calculations from grouped + loads are isomorphic, build temp arrays using stmts where isomorphic + calculations end. Afer distribution, the partition built from temp + arrays can be vectorized in pass SLP after loop unrolling. For example, + + |DO I = 1, N + | A = FOO (ARG_1); + | B = FOO (ARG_2); + | C = BAR_0 (A); + | D = BAR_1 (B); + |ENDDO + + is transformed to + + |DO I = 1, N + | J = FOO (ARG_1); + | K = FOO (ARG_2); + | X[I] = J; + | Y[I] = K; + | A = X[I]; + | B = Y[I]; + | C = BAR_0 (A); + | D = BAR_1 (B); + |ENDDO + + and is then distributed to + + |DO I = 1, N + | J = FOO (ARG_1); + | K = FOO (ARG_2); + | X[I] = J; + | Y[I] = K; + |ENDDO + + |DO I = 1, N + | A = X[I]; + | B = Y[I]; + | C = BAR_0 (A); + | D = BAR_1 (B); + |ENDDO + Loop distribution is the dual of loop fusion. It separates statements of a loop (or loop nest) into multiple loops (or loop nests) with the same loop header. The major goal is to separate statements which may @@ -44,7 +85,9 @@ along with GCC; see the file COPYING3. If not see 1) Seed partitions with specific type statements. For now we support two types seed statements: statement defining variable used outside - of loop; statement storing to memory. + of loop; statement storing to memory. Moreover, for unvectorizable + loops, we try to find isomorphic stmts from grouped load and build + temp arrays as new seed statements. 2) Build reduced dependence graph (RDG) for loop to be distributed. The vertices (RDG:V) model all statements in the loop and the edges (RDG:E) model flow and control dependencies between statements. @@ -90,6 +133,8 @@ along with GCC; see the file COPYING3. If not see data reuse. */ #include "config.h" +#define INCLUDE_MAP +#define INCLUDE_ALGORITHM #include "system.h" #include "coretypes.h" #include "backend.h" @@ -115,6 +160,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-vectorizer.h" #include "tree-eh.h" #include "gimple-fold.h" +#include "optabs-tree.h" #include "tree-affine.h" #include "intl.h" #include "rtl.h" @@ -188,6 +234,52 @@ struct rdg_vertex #define RDG_MEM_WRITE_STMT(RDG, I) RDGV_HAS_MEM_WRITE (&(RDG->vertices[I])) #define RDG_MEM_READS_STMT(RDG, I) RDGV_HAS_MEM_READS (&(RDG->vertices[I])) +/* Results of isomorphic group analysis. */ +#define UNINITIALIZED (0) +#define ISOMORPHIC (1) +#define HETEROGENEOUS (1 << 1) +#define UNCERTAIN (1 << 2) + +/* Information of a stmt while analyzing isomorphic use in group. */ + +typedef struct _group_info +{ + gimple *stmt; + + /* True if stmt can be a cut point. */ + bool cut_point; + + /* For use_stmt with two rhses, one of which is the lhs of stmt. + If the other is unknown to be isomorphic, mark it uncertain. */ + bool uncertain; + + /* Searching of isomorphic stmt reaches heterogeneous groups or reaches + MEM stmts. */ + bool done; + + _group_info () + { + stmt = NULL; + cut_point = false; + uncertain = false; + done = false; + } +} *group_info; + +/* PAIR of cut points and corresponding profit. */ +typedef std::pair<vec<gimple *> *, int> stmts_profit; + +/* MAP of vector factor VF and corresponding stmts_profit PAIR. */ +typedef std::map<unsigned, stmts_profit> vf_stmts_profit_map; + +/* PAIR of group_num and iteration_num. We consider rhses from the same + group and interation are isomorphic. */ +typedef std::pair<unsigned, unsigned> group_iteration; + +/* An isomorphic stmt is detetmined by lhs of use_stmt, group_num and + the iteration_num when we insert this stmt to this map. */ +typedef std::map<tree, group_iteration> isomer_stmt_lhs; + /* Data dependence type. */ enum rdg_dep_type @@ -600,13 +692,14 @@ class loop_distribution /* Returns true when PARTITION1 and PARTITION2 access the same memory object in RDG. */ bool share_memory_accesses (struct graph *rdg, - partition *partition1, partition *partition2); + partition *partition1, partition *partition2, + hash_set<tree> *excluded_arrays); /* For each seed statement in STARTING_STMTS, this function builds partition for it by adding depended statements according to RDG. All partitions are recorded in PARTITIONS. */ void rdg_build_partitions (struct graph *rdg, - vec<gimple *> starting_stmts, + vec<gimple *> *starting_stmts, vec<partition *> *partitions); /* Compute partition dependence created by the data references in DRS1 @@ -643,15 +736,50 @@ class loop_distribution /* Fuse PARTITIONS of LOOP if necessary before finalizing distribution. ALIAS_DDRS contains ddrs which need runtime alias check. */ - void finalize_partitions (class loop *loop, vec<struct partition *> - *partitions, vec<ddr_p> *alias_ddrs); + void finalize_partitions (class loop *loop, + vec<struct partition *> *partitions, + vec<ddr_p> *alias_ddrs, bitmap producers); + + /* Analyze loop form and if it's vectorizable to decide if we need to + insert temp arrays to distribute it. */ + bool may_insert_temp_arrays (loop_p loop, struct graph *&rdg, + control_dependences *cd); + + /* Reset gimple_uid of GIMPLE_DEBUG and GIMPLE_LABEL to -1. */ + void reset_gimple_uid (loop_p loop); + + bool check_loop_vectorizable (loop_p loop); + + inline void rebuild_rdg (loop_p loop, struct graph *&rdg, + control_dependences *cd); + + /* If loop is not distributed, remove inserted temp arrays. */ + void remove_insertion (loop_p loop, struct graph *flow_only_rdg, + bitmap producers, struct partition *partition); + + /* Insert temp arrays if isomorphic computation exists. Temp arrays will be + regarded as SEED_STMTS for building partitions in succeeding processes. */ + bool insert_temp_arrays (loop_p loop, vec<gimple *> seed_stmts, + hash_set<tree> *tmp_array_vars, bitmap producers); + + void build_producers (loop_p loop, bitmap producers, + vec<gimple *> &transformed); + + void do_insertion (loop_p loop, struct graph *flow_only_rdg, tree iv, + bitmap cut_points, hash_set <tree> *tmp_array_vars, + bitmap producers); + + /* Fuse PARTITIONS built from inserted temp arrays into one partition, + fuse the rest into another. */ + void merge_remaining_partitions (vec<struct partition *> *partitions, + bitmap producers); /* Distributes the code from LOOP in such a way that producer statements are placed before consumer statements. Tries to separate only the statements from STMTS into separate loops. Returns the number of distributed loops. Set NB_CALLS to number of generated builtin calls. Set *DESTROY_P to whether LOOP needs to be destroyed. */ - int distribute_loop (class loop *loop, const vec<gimple *> &stmts, + int distribute_loop (class loop *loop, vec<gimple *> &stmts, control_dependences *cd, int *nb_calls, bool *destroy_p, bool only_patterns_p); @@ -1893,7 +2021,8 @@ loop_distribution::classify_partition (loop_p loop, bool loop_distribution::share_memory_accesses (struct graph *rdg, - partition *partition1, partition *partition2) + partition *partition1, partition *partition2, + hash_set <tree> *excluded_arrays) { unsigned i, j; bitmap_iterator bi, bj; @@ -1927,7 +2056,10 @@ loop_distribution::share_memory_accesses (struct graph *rdg, if (operand_equal_p (DR_BASE_ADDRESS (dr1), DR_BASE_ADDRESS (dr2), 0) && operand_equal_p (DR_OFFSET (dr1), DR_OFFSET (dr2), 0) && operand_equal_p (DR_INIT (dr1), DR_INIT (dr2), 0) - && operand_equal_p (DR_STEP (dr1), DR_STEP (dr2), 0)) + && operand_equal_p (DR_STEP (dr1), DR_STEP (dr2), 0) + /* An exception, if PARTITION1 and PARTITION2 contain the + temp array we inserted, do not merge them. */ + && !excluded_arrays->contains (DR_REF (dr1))) return true; } } @@ -1941,14 +2073,14 @@ loop_distribution::share_memory_accesses (struct graph *rdg, void loop_distribution::rdg_build_partitions (struct graph *rdg, - vec<gimple *> starting_stmts, + vec<gimple *> *starting_stmts, vec<partition *> *partitions) { auto_bitmap processed; int i; gimple *stmt; - FOR_EACH_VEC_ELT (starting_stmts, i, stmt) + FOR_EACH_VEC_ELT (*starting_stmts, i, stmt) { int v = rdg_vertex_for_stmt (rdg, stmt); @@ -2912,13 +3044,47 @@ fuse_memset_builtins (vec<struct partition *> *partitions) } } +void +loop_distribution::merge_remaining_partitions + (vec<struct partition *> *partitions, + bitmap producers) +{ + struct partition *partition = NULL; + struct partition *p1 = NULL, *p2 = NULL; + for (unsigned i = 0; partitions->iterate (i, &partition); i++) + { + if (bitmap_intersect_p (producers, partition->stmts)) + { + if (p1 == NULL) + { + p1 = partition; + continue; + } + partition_merge_into (NULL, p1, partition, FUSE_FINALIZE); + } + else + { + if (p2 == NULL) + { + p2 = partition; + continue; + } + partition_merge_into (NULL, p2, partition, FUSE_FINALIZE); + } + partitions->unordered_remove (i); + partition_free (partition); + i--; + } +} + void loop_distribution::finalize_partitions (class loop *loop, vec<struct partition *> *partitions, - vec<ddr_p> *alias_ddrs) + vec<ddr_p> *alias_ddrs, + bitmap producers) { unsigned i; - struct partition *partition, *a; + struct partition *partition; if (partitions->length () == 1 || alias_ddrs->length () > 0) @@ -2950,13 +3116,7 @@ loop_distribution::finalize_partitions (class loop *loop, || (loop->inner == NULL && i >= NUM_PARTITION_THRESHOLD && num_normal > num_builtin)) { - a = (*partitions)[0]; - for (i = 1; partitions->iterate (i, &partition); ++i) - { - partition_merge_into (NULL, a, partition, FUSE_FINALIZE); - partition_free (partition); - } - partitions->truncate (1); + merge_remaining_partitions (partitions, producers); } /* Fuse memset builtins if possible. */ @@ -2964,6 +3124,1216 @@ loop_distribution::finalize_partitions (class loop *loop, fuse_memset_builtins (partitions); } +/* Gimple uids of GIMPLE_DEBUG and GIMPLE_LABEL were changed during function + vect_analyze_loop, reset them to -1. */ + +void +loop_distribution::reset_gimple_uid (loop_p loop) +{ + basic_block *bbs = get_loop_body_in_custom_order (loop, this, + bb_top_order_cmp_r); + for (int i = 0; i < int (loop->num_nodes); i++) + { + basic_block bb = bbs[i]; + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (is_gimple_debug (stmt) || gimple_code (stmt) == GIMPLE_LABEL) + gimple_set_uid (stmt, -1); + } + } + free (bbs); +} + +bool +loop_distribution::check_loop_vectorizable (loop_p loop) +{ + vec_info_shared shared; + vect_analyze_loop (loop, &shared, true); + loop_vec_info vinfo = loop_vec_info_for_loop (loop); + reset_gimple_uid (loop); + if (vinfo == NULL) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Loop %d no temp array insertion: bad data access pattern," + " unable to generate loop_vinfo.\n", loop->num); + return false; + } + if (vinfo->vectorizable) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d no temp array insertion: original loop" + " can be vectorized without distribution.\n", + loop->num); + delete vinfo; + loop->aux = NULL; + return false; + } + if (vinfo->grouped_loads.length () == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d no temp array insertion: original loop" + " has no grouped loads.\n" , loop->num); + delete vinfo; + loop->aux = NULL; + return false; + } + return true; +} + +inline void +loop_distribution::rebuild_rdg (loop_p loop, struct graph *&rdg, + control_dependences *cd) +{ + free_rdg (rdg); + rdg = build_rdg (loop, cd); + gcc_checking_assert (rdg != NULL); +} + +bool +loop_distribution::may_insert_temp_arrays (loop_p loop, struct graph *&rdg, + control_dependences *cd) +{ + if (!(flag_tree_slp_transpose_vectorize && flag_tree_loop_vectorize)) + return false; + + /* Only loops with two basic blocks HEADER and LATCH are supported. HEADER + is the main body of a LOOP and LATCH is the basic block that controls the + LOOP execution. Size of temp array is determined by loop execution time, + so it must be a const. */ + tree loop_extent = number_of_latch_executions (loop); + if (loop->inner != NULL || loop->num_nodes > 2 + || TREE_CODE (loop_extent) != INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d: no temp array insertion: bad loop" + " form.\n", loop->num); + return false; + } + + if (loop->dont_vectorize) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d: no temp array insertion: this loop" + " should never be vectorized.\n", + loop->num); + return false; + } + + /* Do not distribute a LOOP that is able to be vectorized without + distribution. */ + if (!check_loop_vectorizable (loop)) + { + rebuild_rdg (loop, rdg, cd); + return false; + } + + rebuild_rdg (loop, rdg, cd); + return true; +} + +/* Return max grouped loads' length if all groupes length satisfy len = 2 ^ n. + Otherwise, return 0. */ + +static unsigned +get_max_vf (loop_vec_info vinfo) +{ + unsigned size = 0; + unsigned max = 0; + stmt_vec_info stmt_info; + unsigned i = 0; + FOR_EACH_VEC_ELT (vinfo->grouped_loads, i, stmt_info) + { + size = stmt_info->size; + if (!pow2p_hwi (size)) + return 0; + max = size > max ? size : max; + } + return max; +} + +/* Convert grouped_loads from linked list to vector with length vf. Init + group_info of each stmt in the same group and put then into a vector. And + these vectors consist WORKLISTS. We will re-analyze a group if it is + uncertain, so we regard WORKLISTS as a circular queue. */ + +static unsigned +build_queue (loop_vec_info vinfo, unsigned vf, + vec<vec<group_info> *> &worklists) +{ + stmt_vec_info stmt_info; + unsigned i = 0; + group_info ginfo = NULL; + vec<group_info> *worklist = NULL; + FOR_EACH_VEC_ELT (vinfo->grouped_loads, i, stmt_info) + { + unsigned group_size = stmt_info->size; + stmt_vec_info c_stmt_info = stmt_info; + bool succ = true; + while (group_size >= vf) + { + vec_alloc (worklist, vf); + for (unsigned j = 0; j < vf; ++j) + { + if (c_stmt_info == NULL) + { + succ = false; + break; + } + ginfo = new _group_info (); + ginfo->stmt = c_stmt_info->stmt; + worklist->safe_push (ginfo); + c_stmt_info = c_stmt_info->next_element; + } + if (!succ) + { + unsigned k = 0; + ginfo = NULL; + FOR_EACH_VEC_ELT (*worklist, k, ginfo) + delete ginfo; + vec_free (worklist); + break; + } + worklists.safe_push (worklist); + group_size -= vf; + } + } + return worklists.length (); +} + +static bool +check_same_oprand_type (tree op1, tree op2) +{ + tree type1 = TREE_TYPE (op1); + tree type2 = TREE_TYPE (op2); + if (TREE_CODE (type1) != INTEGER_TYPE && TREE_CODE (type1) != REAL_TYPE) + return false; + + return (TREE_CODE (type1) == TREE_CODE (type2) + && TYPE_UNSIGNED (type1) == TYPE_UNSIGNED (type2) + && TYPE_PRECISION (type1) == TYPE_PRECISION (type2)); +} + +static bool +bit_field_p (gimple *stmt) +{ + unsigned i = 0; + auto_vec<data_reference_p, 2> datarefs_vec; + data_reference_p dr; + if (!find_data_references_in_stmt (NULL, stmt, &datarefs_vec)) + return true; + + FOR_EACH_VEC_ELT (datarefs_vec, i, dr) + { + if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1))) + return true; + } + return false; +} + +static inline bool +shift_operation (enum tree_code op) +{ + return op == LSHIFT_EXPR || op == RSHIFT_EXPR || op == LROTATE_EXPR + || op == RROTATE_EXPR; +} + +/* Return relationship between USE_STMT and the first use_stmt of the group. + RHS1 is the lhs of stmt recorded in group_info. If another rhs of use_stmt + is not a constant, return UNCERTAIN and re-check it later. */ + +static unsigned +check_isomorphic (gimple *use_stmt, gimple *first, + tree rhs1, vec<tree> &hetero_lhs) +{ + /* Check same operation. */ + enum tree_code rhs_code_first = gimple_assign_rhs_code (first); + enum tree_code rhs_code_current = gimple_assign_rhs_code (use_stmt); + if (rhs_code_first != rhs_code_current) + return HETEROGENEOUS; + + /* For shift operations, oprands should be equal. */ + if (shift_operation (rhs_code_current)) + { + tree shift_op_first = gimple_assign_rhs2 (first); + tree shift_op_current = gimple_assign_rhs2 (use_stmt); + if (!operand_equal_p (shift_op_first, shift_op_current, 0) + || !TREE_CONSTANT (shift_op_first)) + return HETEROGENEOUS; + + return ISOMORPHIC; + } + /* Type convertion expr or assignment. */ + if (gimple_num_ops (first) == 2) + return (rhs_code_first == NOP_EXPR || rhs_code_first == CONVERT_EXPR + || rhs_code_first == SSA_NAME) ? ISOMORPHIC : HETEROGENEOUS; + + /* We find USE_STMT from lhs of a stmt, denote it as rhs1 of USE_STMT and + the other one as rhs2. Check if define-stmt of current rhs2 is isomorphic + with define-stmt of rhs2 in the first USE_STMT at this group. */ + tree rhs2_first = gimple_assign_rhs1 (use_stmt) == rhs1 + ? gimple_assign_rhs2 (first) : gimple_assign_rhs1 (first); + tree rhs2_curr = gimple_assign_rhs1 (use_stmt) == rhs1 + ? gimple_assign_rhs2 (use_stmt) : gimple_assign_rhs1 (use_stmt); + + if (check_same_oprand_type (rhs2_first, rhs2_curr)) + { + if (TREE_CONSTANT (rhs2_curr)) + return ISOMORPHIC; + else if (hetero_lhs.contains (rhs2_curr)) + return HETEROGENEOUS; + + /* Provisionally set the stmt as uncertain and analyze the whole group + in function CHECK_UNCERTAIN later if all use_stmts are uncertain. */ + return UNCERTAIN; + } + return HETEROGENEOUS; +} + +static bool +unsupported_operations (gimple *stmt) +{ + enum tree_code code = gimple_assign_rhs_code (stmt); + return code == COND_EXPR; +} + +/* Check if the single use_stmt of STMT is isomorphic with the first one's + use_stmt in current group. */ + +static unsigned +check_use_stmt (group_info elmt, gimple *&first, + vec<gimple *> &tmp_stmts, vec<tree> &hetero_lhs) +{ + if (gimple_code (elmt->stmt) != GIMPLE_ASSIGN) + return HETEROGENEOUS; + use_operand_p dummy; + tree lhs = gimple_assign_lhs (elmt->stmt); + gimple *use_stmt = NULL; + single_imm_use (lhs, &dummy, &use_stmt); + /* STMTs with three rhs are not supported, e.g., GIMPLE_COND. */ + if (use_stmt == NULL || gimple_code (use_stmt) != GIMPLE_ASSIGN + || unsupported_operations (use_stmt) || bit_field_p (use_stmt)) + return HETEROGENEOUS; + tmp_stmts.safe_push (use_stmt); + if (first == NULL) + { + first = use_stmt; + return UNINITIALIZED; + } + /* Check if current use_stmt and the first menber's use_stmt in the group + are of the same type. */ + tree first_lhs = gimple_assign_lhs (first); + tree curr_lhs = gimple_assign_lhs (use_stmt); + if (!check_same_oprand_type (first_lhs, curr_lhs)) + return HETEROGENEOUS; + return check_isomorphic (use_stmt, first, lhs, hetero_lhs); +} + +/* Replace stmt field in group with stmts in TMP_STMTS, and insert their + lhs_info to ISOMER_LHS. */ + +static void +update_isomer_lhs (vec<group_info> *group, unsigned group_num, + unsigned iteration, isomer_stmt_lhs &isomer_lhs, + vec<gimple *> &tmp_stmts, int &profit, + vec<unsigned> &merged_groups) +{ + group_info elmt = NULL; + /* Do not insert temp array if isomorphic stmts from grouped load have + only casting operations. Once isomorphic calculation has 3 oprands, + such as plus operation, this group can be regarded as cut point. */ + bool operated = (gimple_num_ops (tmp_stmts[0]) == 3); + /* Do not insert temp arrays if search of iosomophic stmts reaches + MEM stmts. */ + bool has_vdef = gimple_vdef (tmp_stmts[0]) != NULL; + bool merge = false; + for (unsigned i = 0; i < group->length (); i++) + { + elmt = (*group)[i]; + elmt->stmt = has_vdef ? NULL : tmp_stmts[i]; + elmt->cut_point = has_vdef ? false : (elmt->cut_point || operated); + elmt->uncertain = false; + elmt->done = has_vdef; + tree lhs = gimple_assign_lhs (tmp_stmts[i]); + if (isomer_lhs.find (lhs) != isomer_lhs.end ()) + { + merge = true; + continue; + } + isomer_lhs[lhs] = std::make_pair (group_num, iteration); + } + if (merge) + { + merged_groups.safe_push (group_num); + profit = 0; + return; + } + enum vect_cost_for_stmt kind = scalar_stmt; + int scalar_cost = builtin_vectorization_cost (kind, NULL_TREE, 0); + profit = (tmp_stmts.length () - 1) * scalar_cost; +} + +/* Try to find rhs2 in ISOMER_LHS, if all rhs2 were found and their group_num + and iteration are same, GROUP is isomorphic. */ + +static unsigned +check_isomorphic_rhs (vec<group_info> *group, vec<gimple *> &tmp_stmts, + isomer_stmt_lhs &isomer_lhs) +{ + group_info elmt = NULL; + gimple *stmt = NULL; + unsigned j = 0; + unsigned group_num = -1u; + unsigned iteration = -1u; + tree rhs1 = NULL; + tree rhs2 = NULL; + unsigned status = UNINITIALIZED; + FOR_EACH_VEC_ELT (*group, j, elmt) + { + rhs1 = gimple_assign_lhs (elmt->stmt); + stmt = tmp_stmts[j]; + rhs2 = (rhs1 == gimple_assign_rhs1 (stmt)) + ? gimple_assign_rhs2 (stmt) : gimple_assign_rhs1 (stmt); + isomer_stmt_lhs::iterator iter = isomer_lhs.find (rhs2); + if (iter != isomer_lhs.end ()) + { + if (group_num == -1u) + { + group_num = iter->second.first; + iteration = iter->second.second; + status |= ISOMORPHIC; + continue; + } + if (iter->second.first == group_num + && iter->second.second == iteration) + { + status |= ISOMORPHIC; + continue; + } + return HETEROGENEOUS; + } + else + status |= UNCERTAIN; + } + return status; +} + +/* Update group_info for uncertain groups. */ + +static void +update_uncertain_stmts (vec<group_info> *group, unsigned group_num, + unsigned iteration, vec<gimple *> &tmp_stmts) +{ + unsigned j = 0; + group_info elmt = NULL; + FOR_EACH_VEC_ELT (*group, j, elmt) + { + elmt->uncertain = true; + elmt->done = false; + } +} + +/* Push stmts in TMP_STMTS into HETERO_LHS. */ + +static void +set_hetero (vec<group_info> *group, vec<tree> &hetero_lhs, + vec<gimple *> &tmp_stmts) +{ + group_info elmt = NULL; + unsigned i = 0; + for (i = 0; i < group->length (); i++) + { + elmt = (*group)[i]; + elmt->uncertain = false; + elmt->done = true; + } + gimple *stmt = NULL; + FOR_EACH_VEC_ELT (tmp_stmts, i, stmt) + if (stmt != NULL) + hetero_lhs.safe_push (gimple_assign_lhs (stmt)); +} + +/* Given an uncertain group, TMP_STMTS are use_stmts of stmts in GROUP. + Rhs1 is the lhs of stmt in GROUP, rhs2 is the other rhs of USE_STMT. + + Try to find rhs2 in ISOMER_LHS, if all found rhs2 have same group_num + and iteration, this uncertain group is isomorphic. + + If no rhs matched, this GROUP remains uncertain and update group_info. + + Otherwise, this GROUP is heterogeneous and return true to end analysis + for this group. */ + +static bool +check_uncertain (vec<group_info> *group, unsigned group_num, + unsigned iteration, int &profit, + vec<gimple *> &tmp_stmts, isomer_stmt_lhs &isomer_lhs, + vec<tree> &hetero_lhs, vec<unsigned> &merged_groups) +{ + unsigned status = check_isomorphic_rhs (group, tmp_stmts, isomer_lhs); + bool done = false; + switch (status) + { + case UNCERTAIN: + update_uncertain_stmts (group, group_num, iteration, tmp_stmts); + break; + case ISOMORPHIC: + update_isomer_lhs (group, group_num, iteration, isomer_lhs, + tmp_stmts, profit, merged_groups); + break; + default: + set_hetero (group, hetero_lhs, tmp_stmts); + done = true; + } + return done; +} + +/* Return false if analysis of this group is not finished, e.g., isomorphic or + uncertain. Calculate the profit if vectorized. */ + +static bool +check_group (vec<group_info> *group, unsigned group_num, unsigned iteration, + int &profit, vec<unsigned> &merged_groups, + isomer_stmt_lhs &isomer_lhs, vec<tree> &hetero_lhs) +{ + unsigned j = 0; + group_info elmt = NULL; + gimple *first = NULL; + unsigned res = 0; + /* Record single use stmts in TMP_STMTS and decide whether replace stmts in + ginfo in succeeding processes. */ + auto_vec<gimple *, 12> tmp_stmts; + FOR_EACH_VEC_ELT (*group, j, elmt) + { + if (merged_groups.contains (group_num)) + return true; + res |= check_use_stmt (elmt, first, tmp_stmts, hetero_lhs); + } + + /* Update each group member according to RES. */ + switch (res) + { + case ISOMORPHIC: + update_isomer_lhs (group, group_num, iteration, isomer_lhs, + tmp_stmts, profit, merged_groups); + return false; + case UNCERTAIN: + return check_uncertain (group, group_num, iteration, profit, + tmp_stmts, isomer_lhs, hetero_lhs, + merged_groups); + default: + set_hetero (group, hetero_lhs, tmp_stmts); + return true; + } +} + +/* Return true if all analysises are done except uncertain groups. */ + +static bool +end_of_search (vec<vec<group_info> *> &circular_queue, + vec<unsigned> &merged_groups) +{ + unsigned i = 0; + vec<group_info> *group = NULL; + group_info elmt = NULL; + FOR_EACH_VEC_ELT (circular_queue, i, group) + { + if (merged_groups.contains (i)) + continue; + elmt = (*group)[0]; + /* If there is any isomorphic use_stmts, continue analysis of isomorphic + use_stmts. */ + if (!elmt->done && !elmt->uncertain) + return false; + } + return true; +} + +/* Push valid stmts to STMTS as cutpoints. */ + +static bool +check_any_cutpoints (vec<vec<group_info> *> &circular_queue, + vec<gimple *> *&stmts, vec<unsigned> &merged_groups) +{ + unsigned front = 0; + vec<group_info> *group = NULL; + group_info elmt = NULL; + unsigned max = circular_queue.length () * circular_queue[0]->length (); + vec_alloc (stmts, max); + while (front < circular_queue.length ()) + { + unsigned i = 0; + if (merged_groups.contains (front)) + { + front++; + continue; + } + group = circular_queue[front++]; + FOR_EACH_VEC_ELT (*group, i, elmt) + if (elmt->stmt != NULL && elmt->done && elmt->cut_point) + stmts->safe_push (elmt->stmt); + } + return stmts->length () != 0; +} + +/* Grouped loads are isomorphic. Make pair for group number and iteration, + map load stmt to this pair. We set iteration 0 here. */ + +static void +init_isomer_lhs (vec<vec<group_info> *> &groups, isomer_stmt_lhs &isomer_lhs) +{ + vec<group_info> *group = NULL; + group_info elmt = NULL; + unsigned i = 0; + FOR_EACH_VEC_ELT (groups, i, group) + { + unsigned j = 0; + FOR_EACH_VEC_ELT (*group, j, elmt) + isomer_lhs[gimple_assign_lhs (elmt->stmt)] = std::make_pair (i, 0); + } +} + +/* It's not a strict analysis of load/store profit. Assume scalar and vector + load/store are of the same cost. The result PROFIT equals profit form + vectorizing of scalar loads/stores minus cost of a vectorized load/store. */ + +static int +load_store_profit (unsigned scalar_mem_ops, unsigned vf, unsigned new_mem_ops) +{ + int profit = 0; + enum vect_cost_for_stmt kind = scalar_load; + int scalar_cost = builtin_vectorization_cost (kind, NULL_TREE, 0); + profit += (scalar_mem_ops - (scalar_mem_ops / vf)) * scalar_cost; + profit -= new_mem_ops / vf * scalar_cost; + kind = scalar_store; + scalar_cost = builtin_vectorization_cost (kind, NULL_TREE, 0); + profit -= new_mem_ops / vf * scalar_cost; + return profit; +} + +/* Breadth first search the graph consisting of define-use chain starting from + the circular queue initialized by function BUILD_QUEUE. Find single use of + each stmt in group and check if they are isomorphic. Isomorphic is defined + as same rhs type, same operator, and isomorphic calculation of each rhs + starting from load. If another rhs is uncertain to be isomorphic, put it + at the end of circular queue and re-analyze it during the next iteration. + If a group shares the same use_stmt with another group, skip one of them in + succeedor prcoesses as merged. Iterate the circular queue until all + remianing groups heterogeneous or reaches MEN stmts. If all other groups + have finishes the analysis, and the remaining groups are uncertain, + return false to avoid endless loop. */ + +bool +bfs_find_isomer_stmts (vec<vec<group_info> *> &circular_queue, + stmts_profit &profit_pair, unsigned vf, + bool &reach_vdef) +{ + isomer_stmt_lhs isomer_lhs; + auto_vec<tree> hetero_lhs; + auto_vec<unsigned> merged_groups; + vec<group_info> *group = NULL; + /* True if analysis finishes. */ + bool done = false; + int profit_sum = 0; + vec<gimple *> *stmts = NULL; + init_isomer_lhs (circular_queue, isomer_lhs); + for (unsigned i = 1; !done; ++i) + { + unsigned front = 0; + /* Re-initialize DONE to TRUE while a new iteration begins. */ + done = true; + while (front < circular_queue.length ()) + { + int profit = 0; + group = circular_queue[front]; + done &= check_group (group, front, i, profit, merged_groups, + isomer_lhs, hetero_lhs); + profit_sum += profit; + if (profit != 0 && (*group)[0]->stmt == NULL) + { + reach_vdef = true; + return false; + } + ++front; + } + /* Uncertain result, return. */ + if (!done && end_of_search (circular_queue, merged_groups)) + return false; + } + if (check_any_cutpoints (circular_queue, stmts, merged_groups)) + { + profit_pair.first = stmts; + unsigned loads = circular_queue.length () * circular_queue[0]->length (); + profit_pair.second = profit_sum + load_store_profit (loads, vf, + stmts->length ()); + if (profit_pair.second > 0) + return true; + } + return false; +} + +/* Free memory allocated by ginfo. */ + +static void +free_ginfos (vec<vec<group_info> *> &worklists) +{ + vec<group_info> *worklist; + unsigned i = 0; + while (i < worklists.length ()) + { + worklist = worklists[i++]; + group_info ginfo; + unsigned j = 0; + FOR_EACH_VEC_ELT (*worklist, j, ginfo) + delete ginfo; + vec_free (worklist); + } +} + +static void +release_tmp_stmts (vf_stmts_profit_map &candi_stmts) +{ + vf_stmts_profit_map::iterator iter; + for (iter = candi_stmts.begin (); iter != candi_stmts.end (); ++iter) + iter->second.first->release (); +} + +/* Choose the group of stmt with maximun profit. */ + +static bool +decide_stmts_by_profit (vf_stmts_profit_map &candi_stmts, vec<gimple *> &stmts) +{ + vf_stmts_profit_map::iterator iter; + int profit = 0; + int max = 0; + vec<gimple *> *tmp = NULL; + for (iter = candi_stmts.begin (); iter != candi_stmts.end (); ++iter) + { + profit = iter->second.second; + if (profit > max) + { + tmp = iter->second.first; + max = profit; + } + } + if (max == 0) + { + release_tmp_stmts (candi_stmts); + return false; + } + unsigned i = 0; + gimple *stmt = NULL; + FOR_EACH_VEC_ELT (*tmp, i, stmt) + stmts.safe_push (stmt); + release_tmp_stmts (candi_stmts); + return stmts.length () != 0; +} + +/* Find isomorphic stmts from grouped loads with vector factor VF. + + Given source code as follows and ignore casting. + + a0 = (a[0] + b[0]) + ((a[4] - b[4]) << 16); + a1 = (a[1] + b[1]) + ((a[5] - b[5]) << 16); + a2 = (a[2] + b[2]) + ((a[6] - b[6]) << 16); + a3 = (a[3] + b[3]) + ((a[7] - b[7]) << 16); + + We get grouped loads in VINFO as + + GROUP_1 GROUP_2 + _1 = *a _11 = *b + _2 = *(a + 1) _12 = *(b + 1) + _3 = *(a + 2) _13 = *(b + 2) + _4 = *(a + 3) _14 = *(b + 3) + _5 = *(a + 4) _15 = *(b + 4) + _6 = *(a + 5) _16 = *(b + 5) + _7 = *(a + 6) _17 = *(b + 6) + _8 = *(a + 7) _18 = *(b + 7) + + First we try VF = 8, we get two worklists + + WORKLIST_1 WORKLIST_2 + _1 = *a _11 = *b + _2 = *(a + 1) _12 = *(b + 1) + _3 = *(a + 2) _13 = *(b + 2) + _4 = *(a + 3) _14 = *(b + 3) + _5 = *(a + 4) _15 = *(b + 4) + _6 = *(a + 5) _16 = *(b + 5) + _7 = *(a + 6) _17 = *(b + 6) + _8 = *(a + 7) _18 = *(b + 7) + + We find _111 = _1 + _11 and _115 = _5 - _15 are not isomorphic, + so we try VF = VF / 2. + + GROUP_1 GROUP_2 + _1 = *a _5 = *(a + 4) + _2 = *(a + 1) _6 = *(a + 5) + _3 = *(a + 2) _7 = *(a + 6) + _4 = *(a + 3) _8 = *(a + 7) + + GROUP_3 GROUP_4 + _11 = *b _15 = *(b + 4) + _12 = *(b + 1) _16 = *(b + 5) + _13 = *(b + 2) _17 = *(b + 6) + _14 = *(b + 3) _18 = *(b + 7) + + We first analyze group_1, and find all operations are isomorphic, then + replace stmts in group_1 with their use_stmts. Group_2 as well. + + GROUP_1 GROUP_2 + _111 = _1 + _11 _115 = _5 - _15 + _112 = _2 + _12 _116 = _6 - _16 + _113 = _3 + _13 _117 = _7 - _17 + _114 = _4 + _14 _118 = _8 - _18 + + When analyzing group_3 and group_4, we find their use_stmts are the same + as group_1 and group_2. So group_3 is regarded as being merged to group_1 + and group_4 being merged to group_2. In future procedures, we will skip + group_3 and group_4. + + We repeat such processing until opreations are not isomorphic or searching + reaches MEM stmts. In our given case, searching end up at a0, a1, a2 and + a3. */ + +static bool +find_isomorphic_stmts (loop_vec_info vinfo, vec<gimple *> &stmts) +{ + unsigned vf = get_max_vf (vinfo); + if (vf == 0) + return false; + auto_vec<vec<group_info> *> circular_queue; + /* Map of vector factor and corresponding vectorizing profit. */ + stmts_profit profit_map; + /* Map of cut_points and vector factor. */ + vf_stmts_profit_map candi_stmts; + bool reach_vdef = false; + while (vf > 2) + { + if (build_queue (vinfo, vf, circular_queue) == 0) + return false; + if (!bfs_find_isomer_stmts (circular_queue, profit_map, vf, reach_vdef)) + { + if (reach_vdef) + { + release_tmp_stmts (candi_stmts); + free_ginfos (circular_queue); + circular_queue.release (); + return false; + } + vf /= 2; + free_ginfos (circular_queue); + circular_queue.release (); + continue; + } + candi_stmts[vf] = profit_map; + free_ginfos (circular_queue); + vf /= 2; + circular_queue.release (); + } + return decide_stmts_by_profit (candi_stmts, stmts); +} + +/* Get iv from SEED_STMTS and make sure each seed_stmt has only one iv as index + and all indices are the same. */ + +static tree +find_index (vec<gimple *> seed_stmts) +{ + if (seed_stmts.length () == 0) + return NULL; + bool found_index = false; + tree index = NULL; + unsigned ui = 0; + for (ui = 0; ui < seed_stmts.length (); ui++) + { + if (!gimple_vdef (seed_stmts[ui])) + return NULL; + tree lhs = gimple_assign_lhs (seed_stmts[ui]); + unsigned num_index = 0; + while (TREE_CODE (lhs) == ARRAY_REF) + { + if (TREE_CODE (TREE_OPERAND (lhs, 1)) == SSA_NAME) + { + num_index++; + if (num_index > 1) + return NULL; + if (index == NULL) + { + index = TREE_OPERAND (lhs, 1); + found_index = true; + } + else if (index != TREE_OPERAND (lhs, 1)) + return NULL; + } + lhs = TREE_OPERAND (lhs, 0); + } + if (!found_index) + return NULL; + } + return index; +} + +/* Check if expression of phi is an increament of a const. */ + +static void +check_phi_inc (struct vertex *v_phi, struct graph *rdg, bool &found_inc) +{ + struct graph_edge *e_phi; + for (e_phi = v_phi->succ; e_phi; e_phi = e_phi->succ_next) + { + struct vertex *v_inc = &(rdg->vertices[e_phi->dest]); + if (!is_gimple_assign (RDGV_STMT (v_inc)) + || gimple_expr_code (RDGV_STMT (v_inc)) != PLUS_EXPR) + continue; + tree rhs1 = gimple_assign_rhs1 (RDGV_STMT (v_inc)); + tree rhs2 = gimple_assign_rhs2 (RDGV_STMT (v_inc)); + if (!(integer_onep (rhs1) || integer_onep (rhs2))) + continue; + struct graph_edge *e_inc; + /* find cycle with only two vertices inc and phi: inc <--> phi. */ + bool found_cycle = false; + for (e_inc = v_inc->succ; e_inc; e_inc = e_inc->succ_next) + { + if (e_inc->dest == e_phi->src) + { + found_cycle = true; + break; + } + } + if (!found_cycle) + continue; + found_inc = true; + } +} + +/* Check if phi satisfies form like PHI <0, i>. */ + +static inline bool +iv_check_phi_stmt (gimple *phi_stmt) +{ + return gimple_phi_num_args (phi_stmt) == 2 + && (integer_zerop (gimple_phi_arg_def (phi_stmt, 0)) + || integer_zerop (gimple_phi_arg_def (phi_stmt, 1))); +} + +/* Make sure the iteration varible is a phi. */ + +static tree +get_iv_from_seed (struct graph *flow_only_rdg, vec<gimple *> seed_stmts) +{ + tree index = find_index (seed_stmts); + if (index == NULL) + return NULL; + for (int i = 0; i < flow_only_rdg->n_vertices; i++) + { + struct vertex *v = &(flow_only_rdg->vertices[i]); + if (RDGV_STMT (v) != seed_stmts[0]) + continue; + struct graph_edge *e; + bool found_phi = false; + for (e = v->pred; e; e = e->pred_next) + { + struct vertex *v_phi = &(flow_only_rdg->vertices[e->src]); + gimple *phi_stmt = RDGV_STMT (v_phi); + if (gimple_code (phi_stmt) != GIMPLE_PHI + || gimple_phi_result (phi_stmt) != index) + continue; + if (!iv_check_phi_stmt (phi_stmt)) + return NULL; + /* find inc expr in succ of phi. */ + bool found_inc = false; + check_phi_inc (v_phi, flow_only_rdg, found_inc); + if (!found_inc) + return NULL; + found_phi = true; + break; + } + if (!found_phi) + return NULL; + break; + } + return index; +} + +/* Do not distribute loop if vertexes in ROOT_MAP have antidependence with in + FLOW_ONLY_RDG. */ + +static bool +check_no_dependency (struct graph *flow_only_rdg, bitmap root_map) +{ + bitmap_iterator bi; + unsigned ui; + auto_vec<unsigned, 16> visited_nodes; + auto_bitmap visited_map; + EXECUTE_IF_SET_IN_BITMAP (root_map, 0, ui, bi) + visited_nodes.safe_push (ui); + for (ui = 0; ui < visited_nodes.length (); ui++) + { + struct vertex *v = &(flow_only_rdg->vertices[visited_nodes[ui]]); + struct graph_edge *e; + for (e = v->succ; e; e = e->succ_next) + { + if (bitmap_bit_p (root_map, e->dest)) + return false; + if (bitmap_bit_p (visited_map, e->dest)) + continue; + visited_nodes.safe_push (e->dest); + bitmap_set_bit (visited_map, e->dest); + } + } + return true; +} + +/* Find isomorphic stmts from GROUPED_LOADS in VINFO and make sure + there is no dependency among those STMT we found. */ + +static unsigned +get_cut_points (struct graph *flow_only_rdg, bitmap cut_points, + loop_vec_info vinfo) +{ + unsigned n_stmts = 0; + + /* STMTS that may be CUT_POINTS. */ + auto_vec<gimple *> stmts; + if (!find_isomorphic_stmts (vinfo, stmts)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "No temp array insertion: no isomorphic stmts" + " were found.\n"); + return 0; + } + + for (int i = 0; i < flow_only_rdg->n_vertices; i++) + { + if (stmts.contains (RDG_STMT (flow_only_rdg, i))) + bitmap_set_bit (cut_points, i); + } + n_stmts = bitmap_count_bits (cut_points); + + bool succ = check_no_dependency (flow_only_rdg, cut_points); + if (!succ) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "No temp array inserted: data dependency" + " among isomorphic stmts.\n"); + return 0; + } + return n_stmts; +} + +static void +build_temp_array (struct vertex *v, gimple_stmt_iterator &gsi, + poly_uint64 array_extent, tree iv, + hash_set<tree> *tmp_array_vars, vec<gimple *> *transformed) +{ + gimple *stmt = RDGV_STMT (v); + tree lhs = gimple_assign_lhs (stmt); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "original stmt:\t"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + } + tree var_ssa = duplicate_ssa_name (lhs, stmt); + gimple_assign_set_lhs (stmt, var_ssa); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "changed to:\t"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS | TDF_MEMSYMS); + } + gimple_set_uid (gsi_stmt (gsi), -1); + tree vect_elt_type = TREE_TYPE (lhs); + tree array_type = build_array_type_nelts (vect_elt_type, array_extent); + tree array = create_tmp_var (array_type); + tree array_ssa = build4 (ARRAY_REF, vect_elt_type, array, iv, NULL, NULL); + tmp_array_vars->add (array_ssa); + gimple *store = gimple_build_assign (array_ssa, var_ssa); + tree new_vdef = make_ssa_name (gimple_vop (cfun), store); + gsi_insert_after (&gsi, store, GSI_NEW_STMT); + gimple_set_vdef (store, new_vdef); + transformed->safe_push (store); + gimple_set_uid (gsi_stmt (gsi), -1); + tree array_ssa2 = build4 (ARRAY_REF, vect_elt_type, array, iv, NULL, NULL); + tmp_array_vars->add (array_ssa2); + gimple *load = gimple_build_assign (lhs, array_ssa2); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "insert stmt:\t"); + print_gimple_stmt (dump_file, store, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " and stmt:\t"); + print_gimple_stmt (dump_file, load, 0, TDF_VOPS|TDF_MEMSYMS); + } + gimple_set_vuse (load, new_vdef); + gsi_insert_after (&gsi, load, GSI_NEW_STMT); + gimple_set_uid (gsi_stmt (gsi), -1); +} + +/* Set bitmap PRODUCERS based on vec TRANSFORMED. */ + +void +loop_distribution::build_producers (loop_p loop, bitmap producers, + vec<gimple *> &transformed) +{ + auto_vec<gimple *, 10> stmts; + stmts_from_loop (loop, &stmts); + int i = 0; + gimple *stmt = NULL; + + FOR_EACH_VEC_ELT (stmts, i, stmt) + gimple_set_uid (stmt, i); + i = 0; + FOR_EACH_VEC_ELT (transformed, i, stmt) + bitmap_set_bit (producers, stmt->uid); +} + +/* Transform stmt + + A = FOO (ARG_1); + + to + + STMT_1: A1 = FOO (ARG_1); + STMT_2: X[I] = A1; + STMT_3: A = X[I]; + + Producer is STMT_2 who defines the temp array and consumer is + STMT_3 who uses the temp array. */ + +void +loop_distribution::do_insertion (loop_p loop, struct graph *flow_only_rdg, + tree iv, bitmap cut_points, + hash_set<tree> *tmp_array_vars, + bitmap producers) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "=== do insertion ===\n"); + + auto_vec<gimple *> transformed; + + /* Execution times of loop. */ + poly_uint64 array_extent + = tree_to_poly_uint64 (number_of_latch_executions (loop)) + 1; + + basic_block *bbs = get_loop_body_in_custom_order (loop, this, + bb_top_order_cmp_r); + + for (int i = 0; i < int (loop->num_nodes); i++) + { + basic_block bb = bbs[i]; + + /* Find all cut points in bb and transform them. */ + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + unsigned j = gimple_uid (gsi_stmt (gsi)); + if (bitmap_bit_p (cut_points, j)) + { + struct vertex *v = &(flow_only_rdg->vertices[j]); + build_temp_array (v, gsi, array_extent, iv, tmp_array_vars, + &transformed); + } + } + } + build_producers (loop, producers, transformed); + update_ssa (TODO_update_ssa); + free (bbs); +} + +/* After temp array insertion, given stmts + STMT_1: M = FOO (ARG_1); + STMT_2: X[I] = M; + STMT_3: A = X[I]; + STMT_2 is the producer, STMT_1 is its prev and STMT_3 is its next. + Replace M with A, and remove STMT_2 and STMT_3. */ + +static void +reset_gimple_assign (struct graph *flow_only_rdg, struct partition *partition, + gimple_stmt_iterator &gsi, int j) +{ + struct vertex *v = &(flow_only_rdg->vertices[j]); + gimple *stmt = RDGV_STMT (v); + gimple *prev = stmt->prev; + gimple *next = stmt->next; + tree n_lhs = gimple_assign_lhs (next); + gimple_assign_set_lhs (prev, n_lhs); + unlink_stmt_vdef (stmt); + if (partition) + bitmap_clear_bit (partition->stmts, gimple_uid (gsi_stmt (gsi))); + gsi_remove (&gsi, true); + release_defs (stmt); + if (partition) + bitmap_clear_bit (partition->stmts, gimple_uid (gsi_stmt (gsi))); + gsi_remove (&gsi, true); +} + +void +loop_distribution::remove_insertion (loop_p loop, struct graph *flow_only_rdg, + bitmap producers, struct partition *partition) +{ + basic_block *bbs = get_loop_body_in_custom_order (loop, this, + bb_top_order_cmp_r); + for (int i = 0; i < int (loop->num_nodes); i++) + { + basic_block bb = bbs[i]; + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + unsigned j = gimple_uid (gsi_stmt (gsi)); + if (bitmap_bit_p (producers, j)) + reset_gimple_assign (flow_only_rdg, partition, gsi, j); + } + } + update_ssa (TODO_update_ssa); + free (bbs); +} + +/* Insert temp arrays if isomorphic computation exists. Temp arrays will be + regarded as SEED_STMTS for building partitions in succeeding processes. */ + +bool +loop_distribution::insert_temp_arrays (loop_p loop, vec<gimple *> seed_stmts, + hash_set<tree> *tmp_array_vars, bitmap producers) +{ + struct graph *flow_only_rdg = build_rdg (loop, NULL); + gcc_checking_assert (flow_only_rdg != NULL); + tree iv = get_iv_from_seed (flow_only_rdg, seed_stmts); + if (iv == NULL) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d no temp array insertion: failed to get" + " iteration variable.\n", loop->num); + free_rdg (flow_only_rdg); + return false; + } + auto_bitmap cut_points; + loop_vec_info vinfo = loop_vec_info_for_loop (loop); + unsigned n_cut_points = get_cut_points (flow_only_rdg, cut_points, vinfo); + delete vinfo; + loop->aux = NULL; + if (n_cut_points == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop %d no temp array insertion: no cut points" + " found.\n", loop->num); + free_rdg (flow_only_rdg); + return false; + } + do_insertion (loop, flow_only_rdg, iv, cut_points, tmp_array_vars, producers); + if (dump_enabled_p ()) + { + dump_user_location_t loc = find_loop_location (loop); + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "Insertion done:" + " %d temp arrays inserted in Loop %d.\n", + n_cut_points, loop->num); + } + free_rdg (flow_only_rdg); + return true; +} + +static bool find_seed_stmts_for_distribution (class loop *, vec<gimple *> *); + /* Distributes the code from LOOP in such a way that producer statements are placed before consumer statements. Tries to separate only the statements from STMTS into separate loops. Returns the number of @@ -2972,7 +4342,7 @@ loop_distribution::finalize_partitions (class loop *loop, int loop_distribution::distribute_loop (class loop *loop, - const vec<gimple *> &stmts, + vec<gimple *> &stmts, control_dependences *cd, int *nb_calls, bool *destroy_p, bool only_patterns_p) { @@ -3021,6 +4391,33 @@ loop_distribution::distribute_loop (class loop *loop, return 0; } + /* Try to distribute LOOP if LOOP is simple enough and unable to vectorize. + If LOOP has grouped loads, recursively find isomorphic stmts and insert + temp arrays, rebuild RDG and call find_seed_stmts_for_distribution + to replace STMTS. */ + + hash_set<tree> tmp_array_vars; + + /* STMTs that define those inserted TMP_ARRAYs. */ + auto_bitmap producers; + + /* New SEED_STMTS after insertion. */ + auto_vec<gimple *> work_list; + bool insert_success = false; + if (may_insert_temp_arrays (loop, rdg, cd)) + { + if (insert_temp_arrays (loop, stmts, &tmp_array_vars, producers)) + { + if (find_seed_stmts_for_distribution (loop, &work_list)) + { + insert_success = true; + } + else + remove_insertion (loop, rdg, producers, NULL); + rebuild_rdg (loop, rdg, cd); + } + } + data_reference_p dref; for (i = 0; datarefs_vec.iterate (i, &dref); ++i) dref->aux = (void *) (uintptr_t) i; @@ -3029,7 +4426,10 @@ loop_distribution::distribute_loop (class loop *loop, dump_rdg (dump_file, rdg); auto_vec<struct partition *, 3> partitions; - rdg_build_partitions (rdg, stmts, &partitions); + if (work_list.length() > stmts.length()) + rdg_build_partitions (rdg, &work_list, &partitions); + else + rdg_build_partitions (rdg, &stmts, &partitions); auto_vec<ddr_p> alias_ddrs; @@ -3101,7 +4501,7 @@ loop_distribution::distribute_loop (class loop *loop, for (int j = i + 1; partitions.iterate (j, &partition); ++j) { - if (share_memory_accesses (rdg, into, partition)) + if (share_memory_accesses (rdg, into, partition, &tmp_array_vars)) { partition_merge_into (rdg, into, partition, FUSE_SHARE_REF); partitions.unordered_remove (j); @@ -3151,7 +4551,7 @@ loop_distribution::distribute_loop (class loop *loop, } } - finalize_partitions (loop, &partitions, &alias_ddrs); + finalize_partitions (loop, &partitions, &alias_ddrs, producers); /* If there is a reduction in all partitions make sure the last one is not classified for builtin code generation. */ @@ -3169,6 +4569,24 @@ loop_distribution::distribute_loop (class loop *loop, } nbp = partitions.length (); + + /* If we have inserted TMP_ARRAYs but there is only one partition left in + the succeeding processes, remove those inserted TMP_ARRAYs back to the + original version. */ + + if (nbp == 1 && insert_success) + { + struct partition *partition = NULL; + partitions.iterate (0, &partition); + remove_insertion (loop, rdg, producers, partition); + if (dump_enabled_p ()) + { + dump_user_location_t loc = find_loop_location (loop); + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "Insertion removed:" + " unable to distribute loop %d.\n", loop->num); + } + } + if (nbp == 0 || (nbp == 1 && !partition_builtin_p (partitions[0])) || (nbp > 1 && partition_contains_all_rw (rdg, partitions))) diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc index 04e68f621..aae7f62f3 100644 --- a/gcc/tree-vect-data-refs.cc +++ b/gcc/tree-vect-data-refs.cc @@ -2791,6 +2791,9 @@ vect_analyze_group_access_1 (vec_info *vinfo, dr_vec_info *dr_info) DR_GROUP_GAP (stmt_info) = groupsize - last_accessed_element; DR_GROUP_SIZE (stmt_info) = groupsize; + + DR_GROUP_SLP_TRANSPOSE (stmt_info) = false; + if (dump_enabled_p ()) { dump_printf_loc (MSG_NOTE, vect_location, @@ -2820,6 +2823,20 @@ vect_analyze_group_access_1 (vec_info *vinfo, dr_vec_info *dr_info) DR_GROUP_GAP (stmt_info)); } + /* SLP: create an SLP data structure for every interleaving group of + loads for further analysis in vect_analyse_slp. */ + if (DR_IS_READ (dr) && !slp_impossible) + { + if (loop_vinfo) + { + LOOP_VINFO_GROUPED_LOADS (loop_vinfo).safe_push (stmt_info); + } + if (bb_vinfo) + { + BB_VINFO_GROUPED_LOADS (bb_vinfo).safe_push (stmt_info); + } + } + /* SLP: create an SLP data structure for every interleaving group of stores for further analysis in vect_analyse_slp. */ if (DR_IS_WRITE (dr) && !slp_impossible) @@ -5636,6 +5653,226 @@ vect_permute_store_chain (vec_info *vinfo, vec<tree> &dr_chain, } } +/* Encoding the PERM_MASK_FIRST. */ + +static void +vect_indices_encoding_first (tree vectype, unsigned int array_num, + tree &perm_mask_high_first, + tree &perm_mask_low_first) +{ + unsigned int nelt = TYPE_VECTOR_SUBPARTS (vectype).to_constant (); + vec_perm_builder sel (nelt, nelt, 1); + sel.quick_grow (nelt); + unsigned int group_num = nelt / array_num; + unsigned int index = 0; + unsigned int array = 0; + unsigned int group = 0; + + /* The encoding has 1 pattern in the fisrt stage. */ + for (array = 0; array < array_num / 2; array++) + { + for (group = 0; group < group_num * 2; group++) + { + sel[index++] = array + array_num * group; + } + } + vec_perm_indices indices (sel, 2, nelt); + perm_mask_high_first = vect_gen_perm_mask_checked (vectype, indices); + + index = 0; + for (array = array_num / 2; array < array_num; array++) + { + for (group = 0; group < group_num * 2; group++) + { + sel[index++] = array + array_num * group; + } + } + indices.new_vector (sel, 2, nelt); + perm_mask_low_first = vect_gen_perm_mask_checked (vectype, indices); +} + +/* Encoding the PERM_MASK. */ + +static void +vect_indices_encoding (tree vectype, unsigned int array_num, + tree &perm_mask_high, tree &perm_mask_low) +{ + unsigned int nelt = TYPE_VECTOR_SUBPARTS (vectype).to_constant (); + vec_perm_builder sel (nelt, nelt, 1); + sel.quick_grow (nelt); + unsigned int group_num = nelt / array_num; + unsigned int index = 0; + unsigned int array = 0; + unsigned int group = 0; + + /* The encoding has 2 patterns in the folllowing stages. */ + for (array = 0; array < array_num / 2; array++) + { + for (group = 0; group < group_num; group++) + { + sel[index++] = group + group_num * array; + } + for (group = 0; group < group_num; group++) + { + sel[index++] = nelt + group + group_num * array; + } + } + vec_perm_indices indices (sel, 2, nelt); + perm_mask_high = vect_gen_perm_mask_checked (vectype, indices); + + index = 0; + for (array = array_num / 2; array < array_num; array++) + { + for (group = 0; group < group_num; group++) + { + sel[index++] = group + group_num * array; + } + for (group = 0; group < group_num; group++) + { + sel[index++] = nelt + group + group_num * array; + } + } + indices.new_vector (sel, 2, nelt); + perm_mask_low = vect_gen_perm_mask_checked (vectype, indices); +} + +/* Function vect_transpose_store_chain. + + Given a chain of interleaved stores in DR_CHAIN of LENGTH and ARRAY_NUM that + must be a power of 2. Generate interleave_high/low stmts to reorder + the data correctly for the stores. Return the final references for stores + in RESULT_CHAIN. This function is similar to vect_permute_store_chain (), + we interleave the contents of the vectors in their order. + + E.g., LENGTH is 4, the scalar type is short (i.e., VF is 8) and ARRAY_NUM + is 4. That is, the input is 4 vectors each containing 8 elements. + And 2 (VF / ARRAY_NUM) of 8 elements come from the same array. we interleave + the contents of the four vectors in their order. We assign a number to each + element, the input sequence is: + + 1st vec: 0 1 2 3 4 5 6 7 + 2nd vec: 8 9 10 11 12 13 14 15 + 3rd vec: 16 17 18 19 20 21 22 23 + 4th vec: 24 25 26 27 28 29 30 31 + + The output sequence should be: + + 1st vec: 0 4 8 12 16 20 24 28 + 2nd vec: 1 5 9 13 17 21 25 29 + 3rd vec: 2 6 10 14 18 22 26 30 + 4th vec: 3 7 11 15 19 23 27 31 + + In our example, + We get 2 (VF / ARRAY_NUM) elements together in every vector. + + I1: 0 4 1 5 2 6 3 7 + I2: 8 12 9 13 10 14 11 15 + I3: 16 20 17 21 18 22 19 23 + I4: 24 28 25 29 26 30 27 31 + + Then, we use interleave_high/low instructions to create such output. + Every 2 (VF / ARRAY_NUM) elements are regarded as a whole. The permutation + is done in log LENGTH stages. + + I1: interleave_high (1st vec, 3rd vec) + I2: interleave_low (1st vec, 3rd vec) + I3: interleave_high (2nd vec, 4th vec) + I4: interleave_low (2nd vec, 4th vec) + + The first stage of the sequence should be: + + I1: 0 4 16 20 1 5 17 21 + I2: 2 6 18 22 3 7 19 23 + I3: 8 12 24 28 9 13 25 29 + I4: 10 14 26 30 11 15 27 31 + + The following stage sequence should be, i.e. the final result is: + + I1: 0 4 8 12 16 20 24 28 + I2: 1 5 9 13 17 21 25 29 + I3: 2 6 10 14 18 22 26 30 + I4: 3 7 11 15 19 23 27 31. */ + +void +vect_transpose_store_chain (vec_info *vinfo, vec<tree> dr_chain, + unsigned int length, unsigned int array_num, + stmt_vec_info stmt_info, gimple_stmt_iterator *gsi, + vec<tree> *result_chain) +{ + gimple *perm_stmt = NULL; + tree vectype = STMT_VINFO_VECTYPE (stmt_info); + tree perm_mask_low_first = NULL; + tree perm_mask_high_first = NULL; + tree perm_mask_low = NULL; + tree perm_mask_high = NULL; + unsigned int log_length = exact_log2 (length); + + /* Only power of 2 is supported. */ + gcc_assert (pow2p_hwi (length)); + + /* The encoding has 2 types, one for the grouped pattern in the fisrt stage, + another for the interleaved patterns in the following stages. */ + gcc_assert (array_num != 0); + + /* Create grouped stmt (in the first stage): + group = nelt / array_num; + high_first = VEC_PERM_EXPR <vect1, vect2, + {0, array_num, 2*array_num, ..., (2*group-1)*array_num, + 1, 1+array_num, 1+2*array_num, ..., 1+(2*group-1)*array_num, + ..., + array_num/2-1, (array_num/2-1)+array_num, ..., + (array_num/2-1)+(2*group-1)*array_num}> + low_first = VEC_PERM_EXPR <vect1, vect2, + {array_num/2, array_num/2+array_num, array_num/2+2*array_num, + ..., array_num/2+(2*group-1)*array_num, + array_num/2+1, array_num/2+1+array_num, + ..., array_num/2+1+(2*group-1)*array_num, + ..., + array_num-1, array_num-1+array_num, + ..., array_num-1+(2*group-1)*array_num}> */ + vect_indices_encoding_first (vectype, array_num, perm_mask_high_first, + perm_mask_low_first); + + /* Create interleaving stmt (in the following stages): + high = VEC_PERM_EXPR <vect1, vect2, {0, 1, ..., group-1, + nelt, nelt+1, ..., nelt+group-1, + group, group+1, ..., 2*group-1, + nelt+group, nelt+group+1, ..., nelt+2*group-1, + ...}> + low = VEC_PERM_EXPR <vect1, vect2, + {nelt/2, nelt/2+1, ..., nelt/2+group-1, + nelt*3/2, nelt*3/2+1, ..., nelt*3/2+group-1, + nelt/2+group, nelt/2+group+1, ..., nelt/2+2*group-1, + nelt*3/2+group, nelt*3/2+group+1, ..., nelt*3/2+2*group-1, + ...}> */ + vect_indices_encoding (vectype, array_num, perm_mask_high, perm_mask_low); + + for (unsigned int perm_time = 0; perm_time < log_length; perm_time++) + { + for (unsigned int index = 0; index < length / 2; index++) + { + tree vect1 = dr_chain[index]; + tree vect2 = dr_chain[index + length / 2]; + + tree high = make_temp_ssa_name (vectype, NULL, "vect_inter_high"); + perm_stmt = gimple_build_assign (high, VEC_PERM_EXPR, vect1, vect2, + perm_time == 0 ? perm_mask_high_first + : perm_mask_high); + vect_finish_stmt_generation (vinfo, stmt_info, perm_stmt, gsi); + (*result_chain)[2 * index] = high; + + tree low = make_temp_ssa_name (vectype, NULL, "vect_inter_low"); + perm_stmt = gimple_build_assign (low, VEC_PERM_EXPR, vect1, vect2, + perm_time == 0 ? perm_mask_low_first + : perm_mask_low); + vect_finish_stmt_generation (vinfo, stmt_info, perm_stmt, gsi); + (*result_chain)[2 * index+1] = low; + } + memcpy (dr_chain.address (), result_chain->address (), + length * sizeof (tree)); + } +} + /* Function vect_setup_realignment This function is called when vectorizing an unaligned load using diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc index 3435f9378..f296e9415 100644 --- a/gcc/tree-vect-loop.cc +++ b/gcc/tree-vect-loop.cc @@ -2856,7 +2856,7 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared, loop_vec_info main_loop_vinfo, const vector_modes &vector_modes, unsigned &mode_i, machine_mode &autodetected_vector_mode, - bool &fatal) + bool &fatal, bool result_only_p) { loop_vec_info loop_vinfo = vect_create_loop_vinfo (loop, shared, loop_form_info, main_loop_vinfo); @@ -2865,6 +2865,8 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared, loop_vinfo->vector_mode = vector_mode; unsigned int suggested_unroll_factor = 1; + /* Loop_vinfo for loop-distribution pass. */ + opt_loop_vec_info fail_loop_vinfo = opt_loop_vec_info::success (NULL); /* Run the main analysis. */ opt_result res = vect_analyze_loop_2 (loop_vinfo, fatal, &suggested_unroll_factor); @@ -2933,7 +2935,21 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared, if (!res) { - delete loop_vinfo; + + /* If current analysis shows LOOP is unable to vectorize, loop_vinfo + will be deleted. If LOOP is under ldist analysis, backup it before + it is deleted and return it if all modes are analyzed and still + fail to vectorize. */ + if (result_only_p && (mode_i == vector_modes.length () + || autodetected_vector_mode == VOIDmode)) + { + fail_loop_vinfo = opt_loop_vec_info::success (loop_vinfo); + loop->aux = (loop_vec_info) fail_loop_vinfo; + } + else + { + delete loop_vinfo; + } if (fatal) gcc_checking_assert (main_loop_vinfo == NULL); return opt_loop_vec_info::propagate_failure (res); @@ -2946,9 +2962,11 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared, Apply a set of analyses on LOOP, and create a loop_vec_info struct for it. The different analyses will record information in the - loop_vec_info struct. */ + loop_vec_info struct. When RESULT_ONLY_P is true, quit analysis + if loop is vectorizable, otherwise, do not delete vinfo. */ opt_loop_vec_info -vect_analyze_loop (class loop *loop, vec_info_shared *shared) +vect_analyze_loop (class loop *loop, vec_info_shared *shared, + bool result_only_p) { DUMP_VECT_SCOPE ("analyze_loop_nest"); @@ -2996,6 +3014,12 @@ vect_analyze_loop (class loop *loop, vec_info_shared *shared) && !unlimited_cost_model (loop)); machine_mode autodetected_vector_mode = VOIDmode; opt_loop_vec_info first_loop_vinfo = opt_loop_vec_info::success (NULL); + /* Loop_vinfo for loop-distribution pass. */ + opt_loop_vec_info fail_loop_vinfo = opt_loop_vec_info::success (NULL); + if (result_only_p) + { + vect_slp_init (); + } unsigned int mode_i = 0; unsigned HOST_WIDE_INT simdlen = loop->simdlen; @@ -3019,10 +3043,16 @@ vect_analyze_loop (class loop *loop, vec_info_shared *shared) opt_loop_vec_info loop_vinfo = vect_analyze_loop_1 (loop, shared, &loop_form_info, NULL, vector_modes, mode_i, - autodetected_vector_mode, fatal); + autodetected_vector_mode, fatal, result_only_p); if (fatal) break; + if (result_only_p && (mode_i == vector_modes.length () + || autodetected_vector_mode == VOIDmode)) + { + return loop_vinfo; + } + if (loop_vinfo) { /* Analyzis has been successful so update the VF value. The @@ -3132,7 +3162,7 @@ vect_analyze_loop (class loop *loop, vec_info_shared *shared) = vect_analyze_loop_1 (loop, shared, &loop_form_info, first_loop_vinfo, vector_modes, mode_i, - autodetected_vector_mode, fatal); + autodetected_vector_mode, fatal, result_only_p); if (fatal) break; diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc index e1bcab0f7..c0c15773d 100644 --- a/gcc/tree-vect-patterns.cc +++ b/gcc/tree-vect-patterns.cc @@ -5632,8 +5632,8 @@ static vect_recog_func vect_vect_recog_func_ptrs[] = { internal functions. */ { vect_recog_gather_scatter_pattern, "gather_scatter" }, { vect_recog_mask_conversion_pattern, "mask_conversion" }, - { vect_recog_widen_plus_pattern, "widen_plus" }, - { vect_recog_widen_minus_pattern, "widen_minus" }, + // { vect_recog_widen_plus_pattern, "widen_plus" }, + // { vect_recog_widen_minus_pattern, "widen_minus" }, }; const unsigned int NUM_PATTERNS = ARRAY_SIZE (vect_vect_recog_func_ptrs); diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc index af477c31a..6cbf8085f 100644 --- a/gcc/tree-vect-slp.cc +++ b/gcc/tree-vect-slp.cc @@ -49,6 +49,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-eh.h" #include "tree-cfg.h" #include "alloc-pool.h" +#include "print-tree.h" +#include "gimple-pretty-print.h" static bool vectorizable_slp_permutation (vec_info *, gimple_stmt_iterator *, slp_tree, stmt_vector_for_cost *); @@ -994,6 +996,21 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap, } gcc_assert (vectype); + if (!STMT_VINFO_VECTYPE (stmt_info)) + STMT_VINFO_VECTYPE (stmt_info) = vectype; + if (dump_file) + { + fprintf (dump_file, "vect_build_slp_tree_1: %p\n", stmt_info); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "vect_build_slp_tree_1: vectype="); + if (vectype) + print_generic_expr (dump_file, vectype); + fprintf (dump_file, "\n"); + fprintf (dump_file, "internal vectype="); + if (STMT_VINFO_VECTYPE (stmt_info)) + print_generic_expr (dump_file, STMT_VINFO_VECTYPE (stmt_info)); + fprintf (dump_file, "\n"); + } gcall *call_stmt = dyn_cast <gcall *> (stmt); if (call_stmt) @@ -1575,10 +1592,10 @@ vect_build_slp_tree (vec_info *vinfo, dump_printf_loc (MSG_NOTE, vect_location, "SLP discovery for node %p succeeded\n", res); gcc_assert (res_ == res); - res->max_nunits = this_max_nunits; + res_->max_nunits = this_max_nunits; vect_update_max_nunits (max_nunits, this_max_nunits); /* Keep a reference for the bst_map use. */ - SLP_TREE_REF_COUNT (res)++; + SLP_TREE_REF_COUNT (res_)++; } return res_; } @@ -3190,8 +3207,10 @@ vect_build_slp_instance (vec_info *vinfo, /* For basic block SLP, try to break the group up into multiples of a vector size. */ + bb_vec_info bb_vinfo = dyn_cast <bb_vec_info> (vinfo); if (is_a <bb_vec_info> (vinfo) - && (i > 1 && i < group_size)) + && (i > 1 && i < group_size) + && !bb_vinfo->transposed) { tree scalar_type = TREE_TYPE (DR_REF (STMT_VINFO_DATA_REF (stmt_info))); @@ -3301,84 +3320,1034 @@ vect_analyze_slp_instance (vec_info *vinfo, scalar_stmts.create (DR_GROUP_SIZE (stmt_info)); while (next_info) { - scalar_stmts.quick_push (vect_stmt_to_vectorize (next_info)); - next_info = DR_GROUP_NEXT_ELEMENT (next_info); + scalar_stmts.quick_push (vect_stmt_to_vectorize (next_info)); + next_info = DR_GROUP_NEXT_ELEMENT (next_info); + } + } + else if (kind == slp_inst_kind_reduc_chain) + { + /* Collect the reduction stmts and store them in scalar_stmts. */ + scalar_stmts.create (REDUC_GROUP_SIZE (stmt_info)); + while (next_info) + { + scalar_stmts.quick_push (vect_stmt_to_vectorize (next_info)); + next_info = REDUC_GROUP_NEXT_ELEMENT (next_info); + } + /* Mark the first element of the reduction chain as reduction to properly + transform the node. In the reduction analysis phase only the last + element of the chain is marked as reduction. */ + STMT_VINFO_DEF_TYPE (stmt_info) + = STMT_VINFO_DEF_TYPE (scalar_stmts.last ()); + STMT_VINFO_REDUC_DEF (vect_orig_stmt (stmt_info)) + = STMT_VINFO_REDUC_DEF (vect_orig_stmt (scalar_stmts.last ())); + } + else if (kind == slp_inst_kind_ctor) + { + tree rhs = gimple_assign_rhs1 (stmt_info->stmt); + tree val; + scalar_stmts.create (CONSTRUCTOR_NELTS (rhs)); + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val) + { + stmt_vec_info def_info = vinfo->lookup_def (val); + def_info = vect_stmt_to_vectorize (def_info); + scalar_stmts.quick_push (def_info); + } + if (dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "Analyzing vectorizable constructor: %G\n", + stmt_info->stmt); + } + else if (kind == slp_inst_kind_reduc_group) + { + /* Collect reduction statements. */ + const vec<stmt_vec_info> &reductions + = as_a <loop_vec_info> (vinfo)->reductions; + scalar_stmts.create (reductions.length ()); + for (i = 0; reductions.iterate (i, &next_info); i++) + if ((STMT_VINFO_RELEVANT_P (next_info) + || STMT_VINFO_LIVE_P (next_info)) + /* ??? Make sure we didn't skip a conversion around a reduction + path. In that case we'd have to reverse engineer that conversion + stmt following the chain using reduc_idx and from the PHI + using reduc_def. */ + && STMT_VINFO_DEF_TYPE (next_info) == vect_reduction_def) + scalar_stmts.quick_push (next_info); + /* If less than two were relevant/live there's nothing to SLP. */ + if (scalar_stmts.length () < 2) + return false; + } + else + gcc_unreachable (); + + vec<stmt_vec_info> roots = vNULL; + if (kind == slp_inst_kind_ctor) + { + roots.create (1); + roots.quick_push (stmt_info); + } + /* Build the tree for the SLP instance. */ + bool res = vect_build_slp_instance (vinfo, kind, scalar_stmts, + roots, + max_tree_size, limit, bst_map, + kind == slp_inst_kind_store + ? stmt_info : NULL); + if (!res) + roots.release (); + + /* ??? If this is slp_inst_kind_store and the above succeeded here's + where we should do store group splitting. */ + + return res; +} + +static inline bool +is_const_assign (stmt_vec_info store_elem) +{ + if (store_elem == NULL) + { + gcc_unreachable (); + } + gimple *stmt = store_elem->stmt; + gimple_rhs_class rhs_class = gimple_assign_rhs_class (stmt); + return rhs_class == GIMPLE_SINGLE_RHS + && TREE_CONSTANT (gimple_assign_rhs1 (store_elem->stmt)); +} + +/* Push inits to INNERMOST_INITS and check const assign. */ + +static bool +record_innermost (vec<tree> &innermost_inits, + vec<tree> &innermost_offsets, + stmt_vec_info stmt_vinfo) +{ + if (!stmt_vinfo) + { + return false; + } + stmt_vec_info next_info = stmt_vinfo; + while (next_info) + { + /* No need to vectorize constant assign in a transposed version. */ + if (is_const_assign (next_info)) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "no need to vectorize, store is const assign: %G", + next_info->stmt); + } + return false; + } + innermost_inits.safe_push (STMT_VINFO_DR_INIT (next_info)); + innermost_offsets.safe_push (STMT_VINFO_DR_OFFSET (next_info)); + next_info = DR_GROUP_NEXT_ELEMENT (next_info); + } + return true; +} + +/* Compare inits to INNERMOST_INITS, return FALSE if inits do not match + the first grouped_store. And check const assign meanwhile. */ + +static bool +compare_innermost (const vec<tree> &innermost_inits, + const vec<tree> &innermost_offsets, + stmt_vec_info stmt_vinfo) +{ + if (!stmt_vinfo || innermost_inits.length () != stmt_vinfo->size) + { + return false; + } + stmt_vec_info next_info = stmt_vinfo; + unsigned int i = 0; + while (next_info) + { + if (is_const_assign (next_info)) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "no need to vectorize, store is const " + "assign: %G", next_info->stmt); + } + return false; + } + if (innermost_inits[i] != STMT_VINFO_DR_INIT (next_info) + || innermost_offsets[i] != STMT_VINFO_DR_OFFSET (next_info)) + { + return false; + } + next_info = DR_GROUP_NEXT_ELEMENT (next_info); + i++; + } + return true; +} + +static bool +check_same_bb (stmt_vec_info grp1, stmt_vec_info grp2) +{ + if (grp1->stmt->bb->index == grp2->stmt->bb->index) + { + return true; + } + return false; +} + +/* Check if grouped stores are of same type. + input: t1/t2 = TREE_TYPE (gimple_assign_lhs (first_element->stmt)) + output: 0 if same, 1 or -1 else. */ + +static int +tree_type_cmp (const tree t1, const tree t2) +{ + gcc_checking_assert (t1 != NULL && t2 != NULL); + if (t1 != t2) + { + if (TREE_CODE (t1) != TREE_CODE (t2)) + { + return TREE_CODE (t1) > TREE_CODE (t2) ? 1 : -1; + } + if (TYPE_UNSIGNED (t1) != TYPE_UNSIGNED (t2)) + { + return TYPE_UNSIGNED (t1) > TYPE_UNSIGNED (t2) ? 1 : -1; + } + if (TYPE_PRECISION (t1) != TYPE_PRECISION (t2)) + { + return TYPE_PRECISION (t1) > TYPE_PRECISION (t2) ? 1 : -1; + } + } + return 0; +} + +/* Check it if 2 grouped stores are of same type that + we can analyze them in a transpose group. */ +static int +check_same_store_type (stmt_vec_info grp1, stmt_vec_info grp2) +{ + if (grp1 == grp2) + { + return 0; + } + if (grp1->size != grp2->size) + { + return grp1->size > grp2->size ? 1 : -1; + } + tree lhs1 = gimple_assign_lhs (grp1->stmt); + tree lhs2 = gimple_assign_lhs (grp2->stmt); + if (TREE_CODE (lhs1) != TREE_CODE (lhs2)) + { + return TREE_CODE (lhs1) > TREE_CODE (lhs2) ? 1 : -1; + } + tree grp_type1 = TREE_TYPE (gimple_assign_lhs (grp1->stmt)); + tree grp_type2 = TREE_TYPE (gimple_assign_lhs (grp2->stmt)); + int cmp = tree_type_cmp (grp_type1, grp_type2); + return cmp; +} + +/* Sort grouped stores according to group_size and store_type. + output: 0 if same, 1 if grp1 > grp2, -1 otherwise. */ + +static int +grouped_store_cmp (const void *grp1_, const void *grp2_) +{ + stmt_vec_info grp1 = *(stmt_vec_info *)const_cast<void *>(grp1_); + stmt_vec_info grp2 = *(stmt_vec_info *)const_cast<void *>(grp2_); + return check_same_store_type (grp1, grp2); +} + +/* Transposing is based on permutation in registers. Permutation requires + vector length being power of 2 and satisfying the vector mode. */ + +static inline bool +check_filling_reg (stmt_vec_info current_element) +{ + if (current_element->size == 0) + { + return false; + } + /* If the gimple STMT was already vectorized in vect pass, it's unable to + conduct transpose analysis, skip it. */ + bool lhs_vectorized + = TREE_CODE (TREE_TYPE (gimple_get_lhs (current_element->stmt))) + == VECTOR_TYPE; + bool rhs_vectorized + = TREE_CODE (TREE_TYPE (gimple_assign_rhs1 (current_element->stmt))) + == VECTOR_TYPE; + if (lhs_vectorized || rhs_vectorized) + { + return false; + } + unsigned int store_precision + = TYPE_PRECISION (TREE_TYPE (gimple_get_lhs (current_element->stmt))); + auto_vector_modes vector_modes; + targetm.vectorize.autovectorize_vector_modes (&vector_modes, false); + unsigned min_mode_size = -1u; + for (unsigned i = 0; i < vector_modes.length (); i++) + { + unsigned mode_bit_size = (GET_MODE_BITSIZE (vector_modes[i])).coeffs[0]; + min_mode_size = mode_bit_size < min_mode_size + ? mode_bit_size : min_mode_size; + } + return store_precision != 0 + && pow2p_hwi (current_element->size) + && (current_element->size * store_precision % min_mode_size == 0); +} + +/* Check if previous groups are suitable to transpose, if not, set their + group number to -1, reduce grp_num and clear current_groups. + Otherwise, just clear current_groups. */ + +static void +check_and_clear_groups (vec<stmt_vec_info> ¤t_groups, + unsigned int &grp_num) +{ + stmt_vec_info first_element; + if (current_groups.length () == 1 + || (current_groups.length () != 0 + && !pow2p_hwi (current_groups.length ()))) + { + while (current_groups.length () != 0) + { + first_element = current_groups.pop (); + first_element->group_number = -1; + } + grp_num--; + } + else + { + while (current_groups.length ()) + { + current_groups.pop (); + } + } +} + + +/* Make sure that transpose slp vectorization is conducted only if grouped + stores are one dimension array ref. */ + +static bool +is_store_one_dim_array (gimple *stmt) +{ + tree op = gimple_get_lhs (stmt); + if (TREE_CODE (op) != ARRAY_REF) + return false; + return TREE_OPERAND_LENGTH (op) > 0 + && TREE_OPERAND_LENGTH (TREE_OPERAND (op, 0)) == 0; +} + +/* Set grouped_stores with similar MEM_REF to the same group and mark their + grp_num. Groups with same grp_num consist the minimum unit to analyze + transpose. Return num of such units. */ + +static unsigned +vect_prepare_transpose (bb_vec_info bb_vinfo) +{ + stmt_vec_info current_element = NULL; + stmt_vec_info first_element = NULL; + unsigned int i = 0; + unsigned int grp_num = 0; + /* Use arrays to record MEM_REF data in different GROUPED_STORES. */ + auto_vec<tree> innermost_inits; + auto_vec<tree> innermost_offsets; + + /* A set of stmt_vec_info with same store type. Analyze them if their size + is suitable to transpose. */ + auto_vec<stmt_vec_info> current_groups; + + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, i, current_element) + { + /* Compare current grouped_store to the first one if first_element exists, + push current_element to current_groups if they are similar on innermost + behavior of MEM_REF. */ + if (first_element != NULL + && !check_same_store_type (first_element, current_element) + && compare_innermost (innermost_inits, innermost_offsets, + current_element) + && check_same_bb (first_element, current_element)) + { + current_groups.safe_push (current_element); + current_element->group_number = grp_num; + /* If current_element is the last element in grouped_stores, continue + will exit the loop and leave the last group unanalyzed. */ + if (i == bb_vinfo->grouped_stores.length () - 1) + { + check_and_clear_groups (current_groups, grp_num); + } + continue; + } + check_and_clear_groups (current_groups, grp_num); + innermost_inits.release (); + innermost_offsets.release (); + /* Beginning of a new group to analyze whether they are able to consist + a unit to conduct transpose analysis. */ + first_element = NULL; + if (is_store_one_dim_array (current_element->stmt) + && check_filling_reg (current_element) + && record_innermost (innermost_inits, innermost_offsets, + current_element)) + { + first_element = current_element; + current_groups.safe_push (current_element); + current_element->group_number = ++grp_num; + if (i == bb_vinfo->grouped_stores.length () - 1) + { + check_and_clear_groups (current_groups, grp_num); + } + continue; + } + current_element->group_number = -1; + } + return grp_num; +} + +/* Return a flag to transpose grouped stores before building slp tree. + Add bool may_transpose in class vec_info. */ + +static bool +vect_may_transpose (bb_vec_info bb_vinfo) +{ + if (targetm.vectorize.vec_perm_const == NULL) + { + return false; + } + + if (bb_vinfo->grouped_stores.length () < 2) + { + return false; + } + + DUMP_VECT_SCOPE ("analyze if grouped stores may transpose to slp"); + /* Sort grouped_stores according to size and type for function + vect_prepare_transpose (). */ + bb_vinfo->grouped_stores.qsort (grouped_store_cmp); + + int groups = vect_prepare_transpose (bb_vinfo); + BB_VINFO_TRANS_GROUPS (bb_vinfo) = groups; + if (dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "%d groups to analyze transposed slp.\n", groups); + return groups != 0; +} + +/* Get the base address of STMT_INFO. */ + +static tree +get_op_base_address (stmt_vec_info stmt_info) +{ + struct data_reference *dr = STMT_VINFO_DATA_REF (stmt_info); + tree op = DR_BASE_ADDRESS (dr); + while (TREE_OPERAND_LENGTH (op) > 0) + { + op = TREE_OPERAND (op, 0); + } + return op; +} + +/* Compare the UID of the two stmt_info STMTINFO_A and STMTINFO_B. + Sorting them in ascending order. */ + +static int +dr_group_cmp (const void *stmtinfo_a_, const void *stmtinfo_b_) +{ + stmt_vec_info stmtinfo_a + = *(stmt_vec_info *) const_cast<void *> (stmtinfo_a_); + stmt_vec_info stmtinfo_b + = *(stmt_vec_info *) const_cast<void *> (stmtinfo_b_); + + /* Stabilize sort. */ + if (stmtinfo_a == stmtinfo_b) + { + return 0; + } + return gimple_uid (stmtinfo_a->stmt) < gimple_uid (stmtinfo_b->stmt) ? -1 : 1; +} + +/* Find the first elements of the grouped loads which are required to merge. */ + +static void +vect_slp_grouped_load_find (bb_vec_info bb_vinfo, vec<bool> &visited, + vec<stmt_vec_info> &res) +{ + unsigned int i = 0; + stmt_vec_info merge_first_element = NULL; + stmt_vec_info first_element = NULL; + tree opa = NULL; + unsigned int grp_size_a = 0; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_loads, i, first_element) + { + if (visited[i]) + { + continue; + } + if (!STMT_VINFO_GROUPED_ACCESS (first_element) + || !pow2p_hwi (DR_GROUP_SIZE (first_element))) + { + /* Non-conforming grouped load should be grouped separately. */ + if (merge_first_element == NULL) + { + visited[i] = true; + res.safe_push (first_element); + return; + } + } + if (merge_first_element == NULL) + { + merge_first_element = first_element; + opa = get_op_base_address (first_element); + grp_size_a = DR_GROUP_SIZE (first_element); + res.safe_push (first_element); + visited[i] = true; + continue; + } + + /* If the two first elements are of the same base address and group size, + these two grouped loads need to be merged. */ + tree opb = get_op_base_address (first_element); + unsigned int grp_size_b = DR_GROUP_SIZE (first_element); + if (opa == opb && grp_size_a == grp_size_b) + { + res.safe_push (first_element); + visited[i] = true; + } + } +} + +/* Merge the grouped loads that are found from + vect_slp_grouped_load_find (). */ + +static stmt_vec_info +vect_slp_grouped_load_merge (vec<stmt_vec_info> &res) +{ + stmt_vec_info stmt_info = res[0]; + if (res.length () == 1) + { + return stmt_info; + } + unsigned int i = 0; + unsigned int size = DR_GROUP_SIZE (res[0]); + unsigned int new_group_size = size * res.length (); + stmt_vec_info first_element = NULL; + stmt_vec_info merge_first_element = NULL; + stmt_vec_info last_element = NULL; + FOR_EACH_VEC_ELT (res, i, first_element) + { + if (merge_first_element == NULL) + { + merge_first_element = first_element; + last_element = merge_first_element; + size = DR_GROUP_SIZE (merge_first_element); + } + + if (last_element != first_element + && !DR_GROUP_NEXT_ELEMENT (last_element)) + { + DR_GROUP_NEXT_ELEMENT (last_element) = first_element; + /* Store the gap from the previous member of the group. If there is + no gap in the access, DR_GROUP_GAP is always 1. */ + DR_GROUP_GAP_TRANS (first_element) = DR_GROUP_GAP (first_element); + DR_GROUP_GAP (first_element) = 1; + } + for (stmt_info = first_element; stmt_info; + stmt_info = DR_GROUP_NEXT_ELEMENT (stmt_info)) + { + DR_GROUP_FIRST_ELEMENT (stmt_info) = merge_first_element; + DR_GROUP_SIZE_TRANS (stmt_info) = DR_GROUP_SIZE (stmt_info); + DR_GROUP_SIZE (stmt_info) = new_group_size; + last_element = stmt_info; + } + } + DR_GROUP_SIZE (merge_first_element) = new_group_size; + DR_GROUP_SLP_TRANSPOSE (merge_first_element) = true; + DR_GROUP_NEXT_ELEMENT (last_element) = NULL; + return merge_first_element; +} + +/* Merge the grouped loads which have the same base address and group size. + For example, for grouped loads (opa_1, opa_2, opb_1, opb_2): + opa_1: a0->a1->a2->a3 + opa_2: a8->a9->a10->a11 + opb_1: b0->b1 + opb_2: b16->b17 + we can probably get two merged grouped loads: + opa: a0->a1->a2->a3->a8->a9->a10->a11 + opb: b0->b1->b16->b17. */ + +static bool +vect_merge_slp_grouped_loads (bb_vec_info bb_vinfo) +{ + if (bb_vinfo->grouped_loads.length () <= 0) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "The number of grouped loads is 0.\n"); + } + return false; + } + bb_vinfo->grouped_loads.qsort (dr_group_cmp); + auto_vec<bool> visited (bb_vinfo->grouped_loads.length ()); + auto_vec<stmt_vec_info> grouped_loads_merge; + for (unsigned int i = 0; i < bb_vinfo->grouped_loads.length (); i++) + { + visited.safe_push (false); + } + while (1) + { + /* Find grouped loads which are required to merge. */ + auto_vec<stmt_vec_info> res; + vect_slp_grouped_load_find (bb_vinfo, visited, res); + if (res.is_empty ()) + { + break; + } + /* Merge the required grouped loads into one group. */ + grouped_loads_merge.safe_push (vect_slp_grouped_load_merge (res)); + } + if (grouped_loads_merge.length () == bb_vinfo->grouped_loads.length ()) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "No grouped loads need to be merged.\n"); + } + return false; + } + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Merging grouped loads successfully.\n"); + } + BB_VINFO_GROUPED_LOADS (bb_vinfo).release (); + for (unsigned int i = 0; i < grouped_loads_merge.length (); i++) + { + BB_VINFO_GROUPED_LOADS (bb_vinfo).safe_push (grouped_loads_merge[i]); + } + return true; +} + +/* Find the first elements of the grouped stores + which are required to transpose and merge. */ + +static void +vect_slp_grouped_store_find (bb_vec_info bb_vinfo, vec<bool> &visited, + vec<stmt_vec_info> &res) +{ + stmt_vec_info first_element = NULL; + stmt_vec_info merge_first_element = NULL; + unsigned int k = 0; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, k, first_element) + { + if (visited[k]) + { + continue; + } + /* Non-conforming grouped store should be grouped separately. */ + if (!STMT_VINFO_GROUPED_ACCESS (first_element) + || first_element->group_number == -1) + { + if (merge_first_element == NULL) + { + visited[k] = true; + res.safe_push (first_element); + return; + } + } + if (first_element->group_number != -1 + && merge_first_element == NULL) + { + merge_first_element = first_element; + } + if (merge_first_element->group_number == first_element->group_number) + { + visited[k] = true; + res.safe_push (first_element); + } + } +} + +/* Transpose and merge the grouped stores that are found from + vect_slp_grouped_store_find (). */ + +static stmt_vec_info +vect_slp_grouped_store_transform (vec<stmt_vec_info> &res) +{ + stmt_vec_info stmt_info = res[0]; + if (res.length () == 1) + { + return stmt_info; + } + stmt_vec_info rearrange_first_element = stmt_info; + stmt_vec_info last_element = rearrange_first_element; + + unsigned int size = DR_GROUP_SIZE (rearrange_first_element); + unsigned int new_group_size = size * res.length (); + for (unsigned int i = 1; i < res.length (); i++) + { + /* Store the gap from the previous member of the group. If there is no + gap in the access, DR_GROUP_GAP is always 1. */ + DR_GROUP_GAP_TRANS (res[i]) = DR_GROUP_GAP (res[i]); + DR_GROUP_GAP (res[i]) = 1; + } + while (!res.is_empty ()) + { + stmt_info = res[0]; + res.ordered_remove (0); + if (DR_GROUP_NEXT_ELEMENT (stmt_info)) + { + res.safe_push (DR_GROUP_NEXT_ELEMENT (stmt_info)); + } + DR_GROUP_FIRST_ELEMENT (stmt_info) = rearrange_first_element; + DR_GROUP_NEXT_ELEMENT (last_element) = stmt_info; + DR_GROUP_SIZE_TRANS (stmt_info) = DR_GROUP_SIZE (stmt_info); + DR_GROUP_SIZE (stmt_info) = new_group_size; + last_element = stmt_info; + } + + DR_GROUP_SIZE (rearrange_first_element) = new_group_size; + DR_GROUP_SLP_TRANSPOSE (rearrange_first_element) = true; + DR_GROUP_NEXT_ELEMENT (last_element) = NULL; + return rearrange_first_element; +} + +/* Save the STMT_INFO in the grouped stores to BB_VINFO_SCALAR_STORES for + transposing back grouped stores. */ + +static void +get_scalar_stores (bb_vec_info bb_vinfo) +{ + unsigned int k = 0; + stmt_vec_info first_element = NULL; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, k, first_element) + { + /* Filter the grouped store which is unnecessary for transposing. */ + if (!STMT_VINFO_GROUPED_ACCESS (first_element) + || first_element->group_number == -1) + { + continue; + } + vec<stmt_vec_info> tmp_scalar_store; + tmp_scalar_store.create (DR_GROUP_SIZE (first_element)); + for (stmt_vec_info stmt_info = first_element; stmt_info; + stmt_info = DR_GROUP_NEXT_ELEMENT (stmt_info)) + { + tmp_scalar_store.safe_push (stmt_info); + } + BB_VINFO_SCALAR_STORES (bb_vinfo).safe_push (tmp_scalar_store); + } +} + +/* Transpose and merge the grouped stores which have the same group number. + For example, for grouped stores (opa_0, opa_1, opa_2, opa_3): + opa_0: a00->a01->a02->a03 + opa_1: a10->a11->a12->a13 + opa_2: a20->a21->a22->a23 + opa_2: a30->a31->a32->a33 + we can probably get the merged grouped store: + opa: a00->a10->a20->a30 + ->a01->a11->a21->a31 + ->a02->a12->a22->a32 + ->a03->a13->a23->a33. */ + +static bool +vect_transform_slp_grouped_stores (bb_vec_info bb_vinfo) +{ + if (bb_vinfo->grouped_stores.length () <= 0) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "The number of grouped stores is 0.\n"); + } + return false; + } + + bb_vinfo->grouped_stores.qsort (dr_group_cmp); + auto_vec<stmt_vec_info> grouped_stores_merge; + auto_vec<bool> visited (bb_vinfo->grouped_stores.length ()); + unsigned int i = 0; + for (i = 0; i < bb_vinfo->grouped_stores.length (); i++) + { + visited.safe_push (false); + } + + /* Get scalar stores for the following transposition recovery. */ + get_scalar_stores (bb_vinfo); + + while (1) + { + /* Find grouped stores which are required to transpose and merge. */ + auto_vec<stmt_vec_info> res; + vect_slp_grouped_store_find (bb_vinfo, visited, res); + if (res.is_empty ()) + { + break; + } + /* Transpose and merge the required grouped stores into one group. */ + grouped_stores_merge.safe_push (vect_slp_grouped_store_transform (res)); + } + + BB_VINFO_GROUPED_STORES (bb_vinfo).release (); + for (i = 0; i < grouped_stores_merge.length (); i++) + { + BB_VINFO_GROUPED_STORES (bb_vinfo).safe_push (grouped_stores_merge[i]); + } + + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Transposing grouped stores successfully.\n"); + } + return true; +} + +/* A helpful function of vect_transform_back_slp_grouped_stores (). */ + +static auto_vec<stmt_vec_info> +vect_transform_back_slp_grouped_store (bb_vec_info bb_vinfo, + stmt_vec_info first_stmt_info) +{ + auto_vec<stmt_vec_info> grouped_stores_split; + for (unsigned int i = 0; i < bb_vinfo->scalar_stores.length (); i++) + { + vec<stmt_vec_info> scalar_tmp = bb_vinfo->scalar_stores[i]; + if (scalar_tmp.length () > 1 + && scalar_tmp[0]->group_number != first_stmt_info->group_number) + { + continue; + } + stmt_vec_info cur_stmt_info = NULL; + stmt_vec_info cur_first_stmt_info = NULL; + stmt_vec_info last_stmt_info = NULL; + unsigned int k = 0; + FOR_EACH_VEC_ELT (scalar_tmp, k, cur_stmt_info) + { + if (k == 0) + { + cur_first_stmt_info = cur_stmt_info; + last_stmt_info = cur_stmt_info; + } + DR_GROUP_FIRST_ELEMENT (cur_stmt_info) = cur_first_stmt_info; + DR_GROUP_NEXT_ELEMENT (last_stmt_info) = cur_stmt_info; + last_stmt_info = cur_stmt_info; + } + DR_GROUP_SIZE (cur_first_stmt_info) = k; + DR_GROUP_NEXT_ELEMENT (last_stmt_info) = NULL; + if (first_stmt_info != cur_first_stmt_info) + { + DR_GROUP_GAP (cur_first_stmt_info) + = DR_GROUP_GAP_TRANS (cur_first_stmt_info); + DR_GROUP_SLP_TRANSPOSE (cur_first_stmt_info) = false; + DR_GROUP_NUMBER (cur_first_stmt_info) = -1; + } + grouped_stores_split.safe_push (cur_first_stmt_info); + } + return grouped_stores_split; +} + +/* Transform the grouped store back. */ + +void +vect_transform_back_slp_grouped_stores (bb_vec_info bb_vinfo, + stmt_vec_info first_stmt_info) +{ + if (first_stmt_info->group_number == -1) + { + return; + } + /* Transform back. */ + auto_vec<stmt_vec_info> grouped_stores_split + = vect_transform_back_slp_grouped_store (bb_vinfo, first_stmt_info); + + /* Add the remaining grouped stores to grouped_stores_split. */ + stmt_vec_info first_element = NULL; + unsigned int i = 0; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, i, first_element) + { + if (first_element->group_number != first_stmt_info->group_number) + { + grouped_stores_split.safe_push (first_element); + } + } + DR_GROUP_SLP_TRANSPOSE (first_stmt_info) = false; + DR_GROUP_NUMBER (first_stmt_info) = -1; + BB_VINFO_GROUPED_STORES (bb_vinfo).release (); + for (i = 0; i < grouped_stores_split.length (); i++) + { + BB_VINFO_GROUPED_STORES (bb_vinfo).safe_push (grouped_stores_split[i]); + } +} + +/* Function check_for_slp_vectype + + Restriction for grouped stores by checking their vectype. + If the vectype of the grouped store is changed, it need transform back. + If all grouped stores need to be transformed back, return FALSE. */ + +static bool +check_for_slp_vectype (bb_vec_info bb_vinfo) +{ + if (dump_file) + fprintf (dump_file, "check_for_slp_vectype: enter\n"); + stmt_vec_info first_element = NULL; + unsigned int i = 0; + int count = 0; + auto_vec<stmt_vec_info> grouped_stores_check; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, i, first_element) + { + grouped_stores_check.safe_push (first_element); + } + FOR_EACH_VEC_ELT (grouped_stores_check, i, first_element) + { + if (STMT_VINFO_GROUPED_ACCESS (first_element) + && first_element->group_number != -1) + { + unsigned int group_size_b + = DR_GROUP_SIZE_TRANS (first_element); + tree vectype = STMT_VINFO_VECTYPE (first_element); + gimple *stmt = STMT_VINFO_STMT (first_element); + tree lhs = gimple_get_lhs (stmt); + tree type = TREE_TYPE (lhs); +#if 0 + if (!vectype && !type) + { + if (dump_file) + fprintf (dump_file, "check_for_slp_vectype: no vectype/stmt type\n"); + continue; + } + + if (!vectype) + vectype = type; +#endif + if (dump_file) + { + fprintf (dump_file, "check_for_slp_vectype: %p\n", first_element); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "check_for_slp_vectype: vectype="); + if (vectype) + print_generic_expr (dump_file, vectype); + fprintf (dump_file, "\n"); + } +#if 0 + if (!vectype || !VECTOR_TYPE_P (vectype)) + continue; +#endif + poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (vectype); + if (nunits.to_constant () > group_size_b) + { + count++; + /* If the vectype is changed, this grouped store need + to be transformed back. */ + vect_transform_back_slp_grouped_stores (bb_vinfo, first_element); + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "No supported: only supported for" + " group_size geq than nunits.\n"); + } + } + } + } + if (count == BB_VINFO_TRANS_GROUPS (bb_vinfo)) + { + return false; + } + if (dump_file) + fprintf (dump_file, "check_for_slp_vectype: True\n"); + return true; +} + +/* Function check_for_dr_alignment + + Check the alignment of the slp instance loads. + Return FALSE if a load cannot be vectorized. */ + +static bool +check_for_dr_alignment (bb_vec_info bb_vinfo, slp_instance instance) +{ + slp_tree node = NULL; + unsigned int i = 0; + FOR_EACH_VEC_ELT (SLP_INSTANCE_LOADS (instance), i, node) + { + stmt_vec_info first_stmt_info = SLP_TREE_SCALAR_STMTS (node)[0]; + dr_vec_info *first_dr_info = STMT_VINFO_DR_INFO (first_stmt_info); + if (dump_file) + { + fprintf (dump_file, "check_for_dr_alignment: %p\n", first_stmt_info); + + gimple *stmt = STMT_VINFO_STMT (first_stmt_info); + tree lhs = gimple_get_lhs (stmt); + tree type = TREE_TYPE (lhs); + print_gimple_stmt (dump_file, stmt, 0); + } + + tree vectype = STMT_VINFO_VECTYPE (first_stmt_info); + int malign = dr_misalignment (first_dr_info, vectype); + enum dr_alignment_support supportable_dr_alignment + = vect_supportable_dr_alignment (bb_vinfo, first_dr_info, + vectype, malign); + if (supportable_dr_alignment == dr_explicit_realign_optimized + || supportable_dr_alignment == dr_explicit_realign) + { + return false; } } - else if (kind == slp_inst_kind_reduc_chain) + return true; +} + +/* Initialize slp_transpose flag before transposing. */ + +static void +init_stmt_info_slp_transpose (bb_vec_info bb_vinfo) +{ + stmt_vec_info first_element = NULL; + unsigned int k = 0; + FOR_EACH_VEC_ELT (bb_vinfo->grouped_stores, k, first_element) { - /* Collect the reduction stmts and store them in scalar_stmts. */ - scalar_stmts.create (REDUC_GROUP_SIZE (stmt_info)); - while (next_info) + if (STMT_VINFO_GROUPED_ACCESS (first_element)) { - scalar_stmts.quick_push (vect_stmt_to_vectorize (next_info)); - next_info = REDUC_GROUP_NEXT_ELEMENT (next_info); + DR_GROUP_SLP_TRANSPOSE (first_element) = false; } - /* Mark the first element of the reduction chain as reduction to properly - transform the node. In the reduction analysis phase only the last - element of the chain is marked as reduction. */ - STMT_VINFO_DEF_TYPE (stmt_info) - = STMT_VINFO_DEF_TYPE (scalar_stmts.last ()); - STMT_VINFO_REDUC_DEF (vect_orig_stmt (stmt_info)) - = STMT_VINFO_REDUC_DEF (vect_orig_stmt (scalar_stmts.last ())); } - else if (kind == slp_inst_kind_ctor) + FOR_EACH_VEC_ELT (bb_vinfo->grouped_loads, k, first_element) { - tree rhs = gimple_assign_rhs1 (stmt_info->stmt); - tree val; - scalar_stmts.create (CONSTRUCTOR_NELTS (rhs)); - FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val) + if (STMT_VINFO_GROUPED_ACCESS (first_element)) { - stmt_vec_info def_info = vinfo->lookup_def (val); - def_info = vect_stmt_to_vectorize (def_info); - scalar_stmts.quick_push (def_info); + DR_GROUP_SLP_TRANSPOSE (first_element) = false; } - if (dump_enabled_p ()) - dump_printf_loc (MSG_NOTE, vect_location, - "Analyzing vectorizable constructor: %G\n", - stmt_info->stmt); } - else if (kind == slp_inst_kind_reduc_group) +} + +/* Analyze and transpose the stmts before building the SLP tree. */ + +static bool +vect_analyze_transpose (bb_vec_info bb_vinfo) +{ + DUMP_VECT_SCOPE ("vect_analyze_transpose"); + + if (!vect_may_transpose (bb_vinfo)) { - /* Collect reduction statements. */ - const vec<stmt_vec_info> &reductions - = as_a <loop_vec_info> (vinfo)->reductions; - scalar_stmts.create (reductions.length ()); - for (i = 0; reductions.iterate (i, &next_info); i++) - if ((STMT_VINFO_RELEVANT_P (next_info) - || STMT_VINFO_LIVE_P (next_info)) - /* ??? Make sure we didn't skip a conversion around a reduction - path. In that case we'd have to reverse engineer that conversion - stmt following the chain using reduc_idx and from the PHI - using reduc_def. */ - && STMT_VINFO_DEF_TYPE (next_info) == vect_reduction_def) - scalar_stmts.quick_push (next_info); - /* If less than two were relevant/live there's nothing to SLP. */ - if (scalar_stmts.length () < 2) - return false; + return false; } - else - gcc_unreachable (); - vec<stmt_vec_info> roots = vNULL; - if (kind == slp_inst_kind_ctor) + /* For basic block SLP, try to merge the grouped stores and loads + into one group. */ + init_stmt_info_slp_transpose (bb_vinfo); + if (vect_transform_slp_grouped_stores (bb_vinfo) + && vect_merge_slp_grouped_loads (bb_vinfo)) { - roots.create (1); - roots.quick_push (stmt_info); + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Analysis succeeded with SLP transposed.\n"); + } + return true; } - /* Build the tree for the SLP instance. */ - bool res = vect_build_slp_instance (vinfo, kind, scalar_stmts, - roots, - max_tree_size, limit, bst_map, - kind == slp_inst_kind_store - ? stmt_info : NULL); - if (!res) - roots.release (); - - /* ??? If this is slp_inst_kind_store and the above succeeded here's - where we should do store group splitting. */ - - return res; + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Analysis failed with SLP transposed.\n"); + } + return false; } /* Check if there are stmts in the loop can be vectorized using SLP. Build SLP @@ -4963,7 +5932,7 @@ vect_slp_analyze_operations (vec_info *vinfo) /* Check we can vectorize the reduction. */ || (SLP_INSTANCE_KIND (instance) == slp_inst_kind_bb_reduc && !vectorizable_bb_reduc_epilogue (instance, &cost_vec))) - { + { slp_tree node = SLP_INSTANCE_TREE (instance); stmt_vec_info stmt_info; if (!SLP_INSTANCE_ROOT_STMTS (instance).is_empty ()) @@ -4975,7 +5944,7 @@ vect_slp_analyze_operations (vec_info *vinfo) "removing SLP instance operations starting from: %G", stmt_info->stmt); vect_free_slp_instance (instance); - vinfo->slp_instances.ordered_remove (i); + vinfo->slp_instances.ordered_remove (i); cost_vec.release (); while (!visited_vec.is_empty ()) visited.remove (visited_vec.pop ()); @@ -5204,7 +6173,7 @@ vect_bb_slp_scalar_cost (vec_info *vinfo, gimple *orig_stmt = orig_stmt_info->stmt; /* If there is a non-vectorized use of the defs then the scalar - stmt is kept live in which case we do not account it or any + stmt is kept live in which case we do not account it or any required defs in the SLP children in the scalar cost. This way we make the vectorization more costly when compared to the scalar cost. */ @@ -5481,7 +6450,11 @@ vect_bb_vectorization_profitable_p (bb_vec_info bb_vinfo, vec_outside_cost = vec_prologue_cost + vec_epilogue_cost; - if (dump_enabled_p ()) + BB_VINFO_VEC_INSIDE_COST (bb_vinfo) = vec_inside_cost; + BB_VINFO_VEC_OUTSIDE_COST (bb_vinfo) = vec_outside_cost; + BB_VINFO_SCALAR_COST (bb_vinfo) = scalar_cost; + + if (!unlimited_cost_model (NULL) && dump_enabled_p ()) { dump_printf_loc (MSG_NOTE, vect_location, "Cost model analysis for part in loop %d:\n", sl); @@ -5819,7 +6792,7 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int n_stmts, bool &fatal, if (!vect_analyze_data_refs (bb_vinfo, &min_vf, NULL)) { if (dump_enabled_p ()) - dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, "not vectorized: unhandled data-ref in basic " "block.\n"); return false; @@ -5854,6 +6827,22 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int n_stmts, bool &fatal, vect_pattern_recog (bb_vinfo); + /* Transpose grouped stores and loads for better vectorizable version. */ + if (bb_vinfo->transposed) + { + if (!vect_analyze_transpose (bb_vinfo)) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: unhandled slp transposed in " + "basic block.\n"); + } + return false; + } + } + bb_vinfo->before_slp = true; + /* Update store groups from pattern processing. */ vect_fixup_store_groups_with_patterns (bb_vinfo); @@ -5872,6 +6861,20 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int n_stmts, bool &fatal, return false; } + /* Check if the vectype is suitable for SLP transposed. */ + if (bb_vinfo->transposed && !check_for_slp_vectype (bb_vinfo)) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "Failed to SLP transposed in the basic block.\n"); + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: vectype is not suitable for " + "SLP transposed in basic block.\n"); + } + return false; + } + /* Optimize permutations. */ vect_optimize_slp (bb_vinfo); @@ -5914,6 +6917,27 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int n_stmts, bool &fatal, if (! BB_VINFO_SLP_INSTANCES (bb_vinfo).length ()) return false; + /* Check if the alignment is suitable for SLP transposed. */ + if (bb_vinfo->transposed) + { + for (i = 0; BB_VINFO_SLP_INSTANCES (bb_vinfo).iterate (i, &instance); i++) + { + if (!check_for_dr_alignment (bb_vinfo, instance)) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "Failed to SLP transposed in the basic " + "block.\n"); + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: alignment is not suitable " + "for SLP transposed in basic block.\n"); + } + return false; + } + } + } + if (!vect_slp_analyze_operations (bb_vinfo)) { if (dump_enabled_p ()) @@ -5923,7 +6947,88 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int n_stmts, bool &fatal, } vect_bb_partition_graph (bb_vinfo); + return true; +} + +static bool +may_new_transpose_bbvinfo (bb_vec_info bb_vinfo_ori, bool res_ori, + loop_p orig_loop) +{ + /* If the flag is false or the slp analysis is broken before + vect_analyze_slp, we don't try to analyze the transposed SLP version. */ + if (!flag_tree_slp_transpose_vectorize + || !BB_VINFO_BEFORE_SLP (bb_vinfo_ori)) + { + return false; + } + + /* If the original bb_vinfo can't be vectorized, try to new a bb_vinfo + of the transposed version. */ + if (!res_ori) + { + return true; + } + + /* Caculate the cost of the original bb_vinfo. */ + if (unlimited_cost_model (NULL)) + { + vec<slp_instance> &instances = BB_VINFO_SLP_INSTANCES (bb_vinfo_ori); + vect_bb_vectorization_profitable_p (bb_vinfo_ori, instances, orig_loop); + } + /* If the vec cost and scalar cost are not much difference (here we set the + threshold to 4), we try to new a bb_vinfo of the transposed version. */ + if (BB_VINFO_SCALAR_COST (bb_vinfo_ori) + < 4 * (BB_VINFO_VEC_INSIDE_COST (bb_vinfo_ori) + + BB_VINFO_VEC_OUTSIDE_COST (bb_vinfo_ori))) + { + return true; + } + return false; +} +static bool +may_choose_transpose_bbvinfo (bb_vec_info bb_vinfo_trans, bool res_trans, + bb_vec_info bb_vinfo_ori, bool res_ori, + loop_p orig_loop) +{ + /* The original bb_vinfo is chosen if the transposed bb_vinfo + can't be vectorized. */ + if (!res_trans) + { + return false; + } + /* Caculate the cost of the transposed bb_vinfo. */ + if (unlimited_cost_model (NULL)) + { + vec<slp_instance> &instances = BB_VINFO_SLP_INSTANCES (bb_vinfo_trans); + vect_bb_vectorization_profitable_p (bb_vinfo_trans, instances, + orig_loop); + } + int diff_bb_cost = -1; + int diff_bb_cost_trans = -1; + if (res_ori) + { + diff_bb_cost = BB_VINFO_SCALAR_COST (bb_vinfo_ori) + - BB_VINFO_VEC_INSIDE_COST (bb_vinfo_ori) + - BB_VINFO_VEC_OUTSIDE_COST (bb_vinfo_ori); + } + if (res_trans) + { + diff_bb_cost_trans = BB_VINFO_SCALAR_COST (bb_vinfo_trans) + - BB_VINFO_VEC_INSIDE_COST (bb_vinfo_trans) + - BB_VINFO_VEC_OUTSIDE_COST (bb_vinfo_trans); + } + /* The original bb_vinfo is chosen when one of the following conditions + is satisfied as follows: + 1) The cost of original version is better transposed version. + 2) The vec cost is similar to scalar cost in the transposed version. */ + if ((res_ori && res_trans && diff_bb_cost >= diff_bb_cost_trans) + || (res_trans && BB_VINFO_SCALAR_COST (bb_vinfo_trans) + <= (BB_VINFO_VEC_INSIDE_COST (bb_vinfo_trans) + + BB_VINFO_VEC_OUTSIDE_COST (bb_vinfo_trans)))) + { + return false; + } return true; } @@ -5937,6 +7042,7 @@ vect_slp_region (vec<basic_block> bbs, vec<data_reference_p> datarefs, loop_p orig_loop) { bb_vec_info bb_vinfo; + bb_vec_info bb_vinfo_trans = NULL; auto_vector_modes vector_modes; /* Autodetect first vector size we try. */ @@ -5951,6 +7057,10 @@ vect_slp_region (vec<basic_block> bbs, vec<data_reference_p> datarefs, { bool vectorized = false; bool fatal = false; + bool res_bb_vinfo_ori = false; + bool res_bb_vinfo_trans = false; + + /* New a bb_vinfo of the original version. */ bb_vinfo = new _bb_vec_info (bbs, &shared); bool first_time_p = shared.datarefs.is_empty (); @@ -5960,8 +7070,113 @@ vect_slp_region (vec<basic_block> bbs, vec<data_reference_p> datarefs, else bb_vinfo->shared->check_datarefs (); bb_vinfo->vector_mode = next_vector_mode; + bb_vinfo->transposed = false; + bb_vinfo->before_slp = false; + + res_bb_vinfo_ori = vect_slp_analyze_bb_1 (bb_vinfo, n_stmts, fatal, + dataref_groups); + auto_vec<slp_instance> profitable_subgraphs; + auto_vec<slp_instance> profitable_subgraphs_trans; + for (slp_instance instance : BB_VINFO_SLP_INSTANCES (bb_vinfo)) + { + if (instance->subgraph_entries.is_empty ()) + continue; + + vect_location = instance->location (); + if (!unlimited_cost_model (NULL) + && !vect_bb_vectorization_profitable_p + (bb_vinfo, instance->subgraph_entries, orig_loop)) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: vectorization is not " + "profitable.\n"); + continue; + } + if (res_bb_vinfo_ori) + { + if (!dbg_cnt (vect_slp)) + continue; + profitable_subgraphs.safe_push (instance); + } + } + + /* Analyze and new a transposed bb_vinfo. */ + if (may_new_transpose_bbvinfo (bb_vinfo, res_bb_vinfo_ori, orig_loop)) + { + bool fatal_trans = false; + bb_vinfo_trans + = new _bb_vec_info (bbs, &shared); + bool first_time_p = shared.datarefs.is_empty (); + BB_VINFO_DATAREFS (bb_vinfo_trans) = datarefs; + if (first_time_p) + { + bb_vinfo_trans->shared->save_datarefs (); + } + else + { + bb_vinfo_trans->shared->check_datarefs (); + } + bb_vinfo_trans->vector_mode = next_vector_mode; + bb_vinfo_trans->transposed = true; + bb_vinfo_trans->before_slp = false; + + res_bb_vinfo_trans + = vect_slp_analyze_bb_1 (bb_vinfo_trans, n_stmts, fatal_trans, + dataref_groups); + if (res_bb_vinfo_trans) + { + for (slp_instance instance : BB_VINFO_SLP_INSTANCES (bb_vinfo_trans)) + { + if (instance->subgraph_entries.is_empty ()) + continue; + + vect_location = instance->location (); + if (!unlimited_cost_model (NULL) + && !vect_bb_vectorization_profitable_p + (bb_vinfo_trans, instance->subgraph_entries, orig_loop)) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "not vectorized: transpose vectorization is not " + "profitable.\n"); + res_bb_vinfo_trans = false; + continue; + } + if (res_bb_vinfo_trans) + { + if (!dbg_cnt (vect_slp)) + continue; + profitable_subgraphs_trans.safe_push (instance); + } + } + } + if (may_choose_transpose_bbvinfo (bb_vinfo_trans, + res_bb_vinfo_trans, + bb_vinfo, res_bb_vinfo_ori, + orig_loop)) + { + bb_vinfo = bb_vinfo_trans; + fatal = fatal_trans; + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Basic block part vectorized " + "using transposed version.\n"); + } + } + else + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "Basic block part vectorized " + "\n"); + } + } + } - if (vect_slp_analyze_bb_1 (bb_vinfo, n_stmts, fatal, dataref_groups)) + if (res_bb_vinfo_ori || res_bb_vinfo_trans) { if (dump_enabled_p ()) { @@ -5972,90 +7187,129 @@ vect_slp_region (vec<basic_block> bbs, vec<data_reference_p> datarefs, } bb_vinfo->shared->check_datarefs (); - - auto_vec<slp_instance> profitable_subgraphs; - for (slp_instance instance : BB_VINFO_SLP_INSTANCES (bb_vinfo)) + if (!res_bb_vinfo_trans) { - if (instance->subgraph_entries.is_empty ()) - continue; - - vect_location = instance->location (); - if (!unlimited_cost_model (NULL) - && !vect_bb_vectorization_profitable_p - (bb_vinfo, instance->subgraph_entries, orig_loop)) + /* When we're vectorizing an if-converted loop body make sure + we vectorized all if-converted code. */ + if (!profitable_subgraphs.is_empty () + && orig_loop) { - if (dump_enabled_p ()) - dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, - "not vectorized: vectorization is not " - "profitable.\n"); - continue; + gcc_assert (bb_vinfo->bbs.length () == 1); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb_vinfo->bbs[0]); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + /* The costing above left us with DCEable vectorized scalar + stmts having the visited flag set on profitable + subgraphs. Do the delayed clearing of the flag here. */ + if (gimple_visited_p (gsi_stmt (gsi))) + { + gimple_set_visited (gsi_stmt (gsi), false); + continue; + } + if (flag_vect_cost_model == VECT_COST_MODEL_UNLIMITED) + continue; + + if (gassign *ass = dyn_cast <gassign *> (gsi_stmt (gsi))) + if (gimple_assign_rhs_code (ass) == COND_EXPR) + { + if (!profitable_subgraphs.is_empty () + && dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "not profitable because of " + "unprofitable if-converted scalar " + "code\n"); + profitable_subgraphs.truncate (0); + } + } } - if (!dbg_cnt (vect_slp)) - continue; + /* Finally schedule the profitable subgraphs. */ + for (slp_instance instance : profitable_subgraphs) + { + if (!vectorized && dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "Basic block will be vectorized " + "using SLP\n"); + vectorized = true; - profitable_subgraphs.safe_push (instance); - } + vect_schedule_slp (bb_vinfo, instance->subgraph_entries); - /* When we're vectorizing an if-converted loop body make sure - we vectorized all if-converted code. */ - if (!profitable_subgraphs.is_empty () - && orig_loop) + unsigned HOST_WIDE_INT bytes; + if (dump_enabled_p ()) + { + if (GET_MODE_SIZE + (bb_vinfo->vector_mode).is_constant (&bytes)) + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, + "basic block part vectorized using %wu " + "byte vectors\n", bytes); + else + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, + "basic block part vectorized using " + "variable length vectors\n"); + } + } + } + else { - gcc_assert (bb_vinfo->bbs.length () == 1); - for (gimple_stmt_iterator gsi = gsi_start_bb (bb_vinfo->bbs[0]); - !gsi_end_p (gsi); gsi_next (&gsi)) + if (!profitable_subgraphs_trans.is_empty () + && orig_loop) { - /* The costing above left us with DCEable vectorized scalar - stmts having the visited flag set on profitable - subgraphs. Do the delayed clearing of the flag here. */ - if (gimple_visited_p (gsi_stmt (gsi))) + gcc_assert (bb_vinfo->bbs.length () == 1); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb_vinfo->bbs[0]); + !gsi_end_p (gsi); gsi_next (&gsi)) { - gimple_set_visited (gsi_stmt (gsi), false); - continue; + /* The costing above left us with DCEable vectorized scalar + stmts having the visited flag set on profitable + subgraphs. Do the delayed clearing of the flag here. */ + if (gimple_visited_p (gsi_stmt (gsi))) + { + gimple_set_visited (gsi_stmt (gsi), false); + continue; + } + if (flag_vect_cost_model == VECT_COST_MODEL_UNLIMITED) + continue; + + if (gassign *ass = dyn_cast <gassign *> (gsi_stmt (gsi))) + if (gimple_assign_rhs_code (ass) == COND_EXPR) + { + if (!profitable_subgraphs_trans.is_empty () + && dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "not profitable because of " + "unprofitable if-converted scalar " + "code\n"); + profitable_subgraphs_trans.truncate (0); + } } - if (flag_vect_cost_model == VECT_COST_MODEL_UNLIMITED) - continue; - - if (gassign *ass = dyn_cast <gassign *> (gsi_stmt (gsi))) - if (gimple_assign_rhs_code (ass) == COND_EXPR) - { - if (!profitable_subgraphs.is_empty () - && dump_enabled_p ()) - dump_printf_loc (MSG_NOTE, vect_location, - "not profitable because of " - "unprofitable if-converted scalar " - "code\n"); - profitable_subgraphs.truncate (0); - } } - } - /* Finally schedule the profitable subgraphs. */ - for (slp_instance instance : profitable_subgraphs) - { - if (!vectorized && dump_enabled_p ()) - dump_printf_loc (MSG_NOTE, vect_location, - "Basic block will be vectorized " - "using SLP\n"); - vectorized = true; + /* Finally schedule the profitable subgraphs. */ + for (slp_instance instance : profitable_subgraphs_trans) + { + if (!vectorized && dump_enabled_p ()) + dump_printf_loc (MSG_NOTE, vect_location, + "Basic block will be vectorized " + "using SLP\n"); + vectorized = true; - vect_schedule_slp (bb_vinfo, instance->subgraph_entries); + vect_schedule_slp (bb_vinfo, instance->subgraph_entries); - unsigned HOST_WIDE_INT bytes; - if (dump_enabled_p ()) - { - if (GET_MODE_SIZE - (bb_vinfo->vector_mode).is_constant (&bytes)) - dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, - "basic block part vectorized using %wu " - "byte vectors\n", bytes); - else - dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, - "basic block part vectorized using " - "variable length vectors\n"); + unsigned HOST_WIDE_INT bytes; + if (dump_enabled_p ()) + { + if (GET_MODE_SIZE + (bb_vinfo->vector_mode).is_constant (&bytes)) + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, + "basic block part vectorized using %wu " + "byte vectors\n", bytes); + else + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location, + "basic block part vectorized using " + "variable length vectors\n"); + } } } + } else { @@ -6081,6 +7335,10 @@ vect_slp_region (vec<basic_block> bbs, vec<data_reference_p> datarefs, } delete bb_vinfo; + if (bb_vinfo_trans) + { + bb_vinfo_trans = NULL; + } if (mode_i < vector_modes.length () && VECTOR_MODE_P (autodetected_vector_mode) @@ -7244,10 +8502,17 @@ vect_schedule_slp_node (vec_info *vinfo, ready early, vectorized stores go before the last scalar stmt which is where all uses are ready. */ stmt_vec_info last_stmt_info = NULL; - if (DR_IS_READ (STMT_VINFO_DATA_REF (stmt_info))) - last_stmt_info = vect_find_first_scalar_stmt_in_slp (node); - else /* DR_IS_WRITE */ - last_stmt_info = vect_find_last_scalar_stmt_in_slp (node); + + if (DR_GROUP_FIRST_ELEMENT (stmt_info) + && DR_GROUP_SLP_TRANSPOSE (DR_GROUP_FIRST_ELEMENT (stmt_info))) + last_stmt_info = vect_find_last_scalar_stmt_in_slp (node); + else + { + if (DR_IS_READ (STMT_VINFO_DATA_REF (stmt_info))) + last_stmt_info = vect_find_first_scalar_stmt_in_slp (node); + else /* DR_IS_WRITE */ + last_stmt_info = vect_find_last_scalar_stmt_in_slp (node); + } si = gsi_for_stmt (last_stmt_info->stmt); } else if ((STMT_VINFO_TYPE (stmt_info) == cycle_phi_info_type diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc index 349200411..3099f6743 100644 --- a/gcc/tree-vect-stmts.cc +++ b/gcc/tree-vect-stmts.cc @@ -1369,10 +1369,10 @@ vect_get_load_cost (vec_info *, stmt_vec_info stmt_info, int ncopies, static void vect_init_vector_1 (vec_info *vinfo, stmt_vec_info stmt_vinfo, gimple *new_stmt, - gimple_stmt_iterator *gsi) + gimple_stmt_iterator *gsi, bool transpose=false) { if (gsi) - vect_finish_stmt_generation (vinfo, stmt_vinfo, new_stmt, gsi); + vect_finish_stmt_generation (vinfo, stmt_vinfo, new_stmt, gsi, transpose); else vinfo->insert_on_entry (stmt_vinfo, new_stmt); @@ -1393,7 +1393,7 @@ vect_init_vector_1 (vec_info *vinfo, stmt_vec_info stmt_vinfo, gimple *new_stmt, tree vect_init_vector (vec_info *vinfo, stmt_vec_info stmt_info, tree val, tree type, - gimple_stmt_iterator *gsi) + gimple_stmt_iterator *gsi, bool transpose) { gimple *init_stmt; tree new_temp; @@ -1418,7 +1418,7 @@ vect_init_vector (vec_info *vinfo, stmt_vec_info stmt_info, tree val, tree type, new_temp = make_ssa_name (TREE_TYPE (type)); init_stmt = gimple_build_assign (new_temp, COND_EXPR, val, true_val, false_val); - vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi); + vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi, transpose); val = new_temp; } } @@ -1437,7 +1437,7 @@ vect_init_vector (vec_info *vinfo, stmt_vec_info stmt_info, tree val, tree type, { init_stmt = gsi_stmt (gsi2); gsi_remove (&gsi2, false); - vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi); + vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi, transpose); } } } @@ -1446,7 +1446,7 @@ vect_init_vector (vec_info *vinfo, stmt_vec_info stmt_info, tree val, tree type, new_temp = vect_get_new_ssa_name (type, vect_simple_var, "cst_"); init_stmt = gimple_build_assign (new_temp, val); - vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi); + vect_init_vector_1 (vinfo, stmt_info, init_stmt, gsi, transpose); return new_temp; } @@ -1572,9 +1572,11 @@ vect_get_vec_defs (vec_info *vinfo, stmt_vec_info stmt_info, slp_tree slp_node, statement and create and return a stmt_vec_info for it. */ static void -vect_finish_stmt_generation_1 (vec_info *, - stmt_vec_info stmt_info, gimple *vec_stmt) +vect_finish_stmt_generation_1 (vec_info *vinfo, + stmt_vec_info stmt_info, gimple *vec_stmt, bool transpose=false) { + if (transpose) + stmt_vec_info vec_stmt_info = vinfo->add_pattern_stmt (vec_stmt, NULL); if (dump_enabled_p ()) dump_printf_loc (MSG_NOTE, vect_location, "add new stmt: %G", vec_stmt); @@ -1616,7 +1618,7 @@ vect_finish_replace_stmt (vec_info *vinfo, void vect_finish_stmt_generation (vec_info *vinfo, stmt_vec_info stmt_info, gimple *vec_stmt, - gimple_stmt_iterator *gsi) + gimple_stmt_iterator *gsi, bool transpose) { gcc_assert (!stmt_info || gimple_code (stmt_info->stmt) != GIMPLE_LABEL); @@ -1648,7 +1650,7 @@ vect_finish_stmt_generation (vec_info *vinfo, } } gsi_insert_before (gsi, vec_stmt, GSI_SAME_STMT); - vect_finish_stmt_generation_1 (vinfo, stmt_info, vec_stmt); + vect_finish_stmt_generation_1 (vinfo, stmt_info, vec_stmt, transpose); } /* We want to vectorize a call to combined function CFN with function @@ -2159,6 +2161,173 @@ vector_vector_composition_type (tree vtype, poly_uint64 nelts, tree *ptype) return NULL_TREE; } +/* Check succeedor BB, BB without load is regarded as empty BB. Ignore empty + BB in DFS. */ + +static unsigned +mem_refs_in_bb (basic_block bb, vec<gimple *> &stmts) +{ + unsigned num = 0; + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (is_gimple_debug (stmt)) + continue; + if (is_gimple_assign (stmt) && gimple_has_mem_ops (stmt) + && !gimple_has_volatile_ops (stmt)) + { + if (gimple_assign_rhs_code (stmt) == MEM_REF + || gimple_assign_rhs_code (stmt) == ARRAY_REF) + { + stmts.safe_push (stmt); + num++; + } + else if (TREE_CODE (gimple_get_lhs (stmt)) == MEM_REF + || TREE_CODE (gimple_get_lhs (stmt)) == ARRAY_REF) + num++; + } + } + return num; +} + +static bool +check_same_base (vec<data_reference_p> *datarefs, data_reference_p dr) +{ + for (unsigned ui = 0; ui < datarefs->length (); ui++) + { + tree op1 = TREE_OPERAND (DR_BASE_OBJECT (dr), 0); + tree op2 = TREE_OPERAND (DR_BASE_OBJECT ((*datarefs)[ui]), 0); + if (TREE_CODE (op1) != TREE_CODE (op2)) + continue; + if (TREE_CODE (op1) == ADDR_EXPR) + { + op1 = TREE_OPERAND (op1, 0); + op2 = TREE_OPERAND (op2, 0); + } + enum tree_code code = TREE_CODE (op1); + switch (code) + { + case VAR_DECL: + if (DECL_NAME (op1) == DECL_NAME (op2) + && DR_IS_READ ((*datarefs)[ui])) + return true; + break; + case SSA_NAME: + if (SSA_NAME_VERSION (op1) == SSA_NAME_VERSION (op2) + && DR_IS_READ ((*datarefs)[ui])) + return true; + break; + default: + break; + } + } + return false; +} + +/* Iterate all load STMTS, if staisfying same base vectorized stmt, then return, + Otherwise, set false to SUCCESS. */ + +static void +check_vec_use (loop_vec_info loop_vinfo, vec<gimple *> &stmts, + stmt_vec_info stmt_info, bool &success) +{ + if (stmt_info == NULL) + { + success = false; + return; + } + if (DR_IS_READ (stmt_info->dr_aux.dr)) + { + success = false; + return; + } + unsigned ui = 0; + gimple *candidate = NULL; + FOR_EACH_VEC_ELT (stmts, ui, candidate) + { + if (TREE_CODE (TREE_TYPE (gimple_get_lhs (candidate))) != VECTOR_TYPE) + continue; + + if (candidate->bb != candidate->bb->loop_father->header) + { + success = false; + return; + } + auto_vec<data_reference_p> datarefs; + tree res = find_data_references_in_bb (candidate->bb->loop_father, + candidate->bb, &datarefs); + if (res == chrec_dont_know) + { + success = false; + return; + } + if (check_same_base (&datarefs, stmt_info->dr_aux.dr)) + return; + } + success = false; +} + +/* Deep first search from present BB. If succeedor has load STMTS, + stop further searching. */ + +static void +dfs_check_bb (loop_vec_info loop_vinfo, basic_block bb, stmt_vec_info stmt_info, + bool &success, vec<basic_block> &visited_bbs) +{ + if (bb == cfun->cfg->x_exit_block_ptr) + { + success = false; + return; + } + if (!success || visited_bbs.contains (bb) || bb == loop_vinfo->loop->latch) + return; + + visited_bbs.safe_push (bb); + auto_vec<gimple *> stmts; + unsigned num = mem_refs_in_bb (bb, stmts); + /* Empty BB. */ + if (num == 0) + { + edge e; + edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->succs) + { + dfs_check_bb (loop_vinfo, e->dest, stmt_info, success, visited_bbs); + if (!success) + return; + } + return; + } + /* Non-empty BB. */ + check_vec_use (loop_vinfo, stmts, stmt_info, success); +} + +/* For grouped store, if all succeedors of present BB have vectorized load + from same base of store. If so, set memory_access_type using + VMAT_CONTIGUOUS_PERMUTE instead of VMAT_LOAD_STORE_LANES. */ + +static bool +conti_perm (stmt_vec_info stmt_vinfo, loop_vec_info loop_vinfo) +{ + gimple *stmt = stmt_vinfo->stmt; + if (gimple_code (stmt) != GIMPLE_ASSIGN) + return false; + + if (DR_IS_READ (stmt_vinfo->dr_aux.dr)) + return false; + + basic_block bb = stmt->bb; + bool success = true; + auto_vec<basic_block> visited_bbs; + visited_bbs.safe_push (bb); + edge e; + edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->succs) + dfs_check_bb (loop_vinfo, e->dest, stmt_vinfo, success, visited_bbs); + return success; +} + /* A subroutine of get_load_store_type, with a subset of the same arguments. Handle the case where STMT_INFO is part of a grouped load or store. @@ -2373,6 +2542,20 @@ get_group_load_store_type (vec_info *vinfo, stmt_vec_info stmt_info, *memory_access_type = VMAT_CONTIGUOUS_PERMUTE; overrun_p = would_overrun_p; } + + if (*memory_access_type == VMAT_LOAD_STORE_LANES + && TREE_CODE (loop_vinfo->num_iters) == INTEGER_CST + && maybe_eq (tree_to_shwi (loop_vinfo->num_iters), + loop_vinfo->vectorization_factor) + && conti_perm (stmt_info, loop_vinfo) + && (vls_type == VLS_LOAD + ? vect_grouped_load_supported (vectype, single_element_p, + group_size) + : vect_grouped_store_supported (vectype, group_size))) + { + *memory_access_type = VMAT_CONTIGUOUS_PERMUTE; + overrun_p = would_overrun_p; + } } /* As a last resort, trying using a gather load or scatter store. @@ -7456,6 +7639,154 @@ vectorizable_scan_store (vec_info *vinfo, return true; } +/* Function vect_permute_store_chains + + Call function vect_permute_store_chain (). + Given a chain of interleaved stores in DR_CHAIN, generate + interleave_high/low stmts to reorder the data correctly. + Return the final references for stores in RESULT_CHAIN. */ + +static void +vect_permute_store_chains (vec_info *vinfo, vec<tree> dr_chain, + unsigned int num_each, stmt_vec_info stmt_info, + gimple_stmt_iterator *gsi, vec<tree> *result_chain, + unsigned int group) +{ + unsigned int k = 0; + unsigned int t = 0; + + /* Divide vectors into GROUP parts. And permute every NUM_EACH vectors + together. */ + for (k = 0; k < group; k++) + { + auto_vec<tree> dr_chain_transposed (num_each); + auto_vec<tree> result_chain_transposed (num_each); + for (t = k; t < dr_chain.length (); t = t + group) + { + dr_chain_transposed.quick_push (dr_chain[t]); + } + vect_permute_store_chain (vinfo, dr_chain_transposed, num_each, + stmt_info, gsi, &result_chain_transposed); + for (t = 0; t < num_each; t++) + { + result_chain->quick_push (result_chain_transposed[t]); + } + } +} + +/* Function transpose_oprnd_store + + Calculate the transposed results from VEC_OPRNDS (VEC_STMT) + for vectorizable_store. */ + +static void +transpose_oprnd_store (vec_info *vinfo, vec<tree>vec_oprnds, + vec<tree> *result_chain, unsigned int vec_num, + unsigned int const_nunits, unsigned int array_num, + stmt_vec_info first_stmt_info, + gimple_stmt_iterator *gsi) +{ + unsigned int group_for_transform = 0; + unsigned int num_each = 0; + + /* Transpose back for vec_oprnds. */ + /* vec = {vec1, vec2, ...} */ + if (array_num < const_nunits + && const_nunits % array_num == 0) + { + vect_transpose_store_chain (vinfo, vec_oprnds, + vec_num, array_num, + first_stmt_info, + gsi, result_chain); + } + /* vec1 = {vec_part1}, vec2 = {vec_part2}, ... */ + else if (array_num >= const_nunits + && array_num % const_nunits == 0) + { + group_for_transform = array_num / const_nunits; + num_each = vec_oprnds.length () / group_for_transform; + vect_permute_store_chains (vinfo, vec_oprnds, + num_each, first_stmt_info, + gsi, result_chain, + group_for_transform); + } + else + { + gcc_unreachable (); + } +} + +static dr_vec_info * +get_dr_info (stmt_vec_info stmt_info) +{ + dr_vec_info *dr_info = STMT_VINFO_DR_INFO (stmt_info); + if (dr_info->misalignment == DR_MISALIGNMENT_UNINITIALIZED) + { + SET_DR_MISALIGNMENT (dr_info, DR_MISALIGNMENT_UNKNOWN); + } + return dr_info; +} + +static unsigned +dr_align_vect_store (vec_info *vinfo, dr_vec_info *cur_first_dr_info, + tree vectype, unsigned HOST_WIDE_INT &align) +{ + unsigned misalign = 0; + align = known_alignment (DR_TARGET_ALIGNMENT (cur_first_dr_info)); + if (aligned_access_p (cur_first_dr_info, vectype)) + { + return misalign; + } + else if (cur_first_dr_info->misalignment == -1) + { + align = dr_alignment (vect_dr_behavior (vinfo, cur_first_dr_info)); + } + else + { + misalign = cur_first_dr_info->misalignment; + } + return misalign; +} + +static void +add_new_stmt_vect_store (vec_info *vinfo, tree vectype, tree dataref_ptr, + tree dataref_offset, tree ref_type, + dr_vec_info *cur_first_dr_info, tree vec_oprnd, + gimple_stmt_iterator *gsi, stmt_vec_info stmt_info) +{ + /* Data align. */ + unsigned HOST_WIDE_INT align; + unsigned misalign = dr_align_vect_store (vinfo, cur_first_dr_info, + vectype, align); + + if (dataref_offset == NULL_TREE && TREE_CODE (dataref_ptr) == SSA_NAME) + { + set_ptr_info_alignment (get_ptr_info (dataref_ptr), align, misalign); + } + + /* Get data_ref. */ + tree offset = dataref_offset ? dataref_offset : build_int_cst (ref_type, 0); + tree data_ref = fold_build2 (MEM_REF, vectype, dataref_ptr, offset); + if (aligned_access_p (cur_first_dr_info, vectype)) + { + ; + } + else if (cur_first_dr_info->misalignment == -1) + { + TREE_TYPE (data_ref) = build_aligned_type (TREE_TYPE (data_ref), + align * BITS_PER_UNIT); + } + else + { + tree elem_type = TREE_TYPE (vectype); + TREE_TYPE (data_ref) = build_aligned_type (TREE_TYPE (data_ref), + TYPE_ALIGN (elem_type)); + } + /* Add new stmt. */ + vect_copy_ref_info (data_ref, DR_REF (cur_first_dr_info->dr)); + gassign *new_stmt = gimple_build_assign (data_ref, vec_oprnd); + vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi, true); +} /* Function vectorizable_store. @@ -8333,6 +8664,16 @@ vectorizable_store (vec_info *vinfo, &vec_offsets); vec_offset = vec_offsets[0]; } + /* If the stmt_info need to be transposed recovery, dataref_ptr + will be caculated later. */ + else if (memory_access_type == VMAT_CONTIGUOUS + && is_a <bb_vec_info> (vinfo) + && STMT_VINFO_GROUPED_ACCESS (stmt_info) + && DR_GROUP_SLP_TRANSPOSE ( + DR_GROUP_FIRST_ELEMENT (stmt_info))) + { + dataref_ptr = NULL_TREE; + } else dataref_ptr = vect_create_data_ref_ptr (vinfo, first_stmt_info, aggr_type, @@ -8423,6 +8764,75 @@ vectorizable_store (vec_info *vinfo, } else { + /* group_size: the size of group after transposing and merging. + group_size_b: the size of group before transposing and merging, + and only group_size_b >= const_nunits is supported. + array_num: the number of arrays. + const_nunits: TYPE_VECTOR_SUBPARTS (vectype). + ncontinues: group_size_b / const_nunits, it means the number of + times an array is stored in memory. */ + if (slp && is_a <bb_vec_info> (vinfo) + && STMT_VINFO_GROUPED_ACCESS (stmt_info) + && DR_GROUP_SLP_TRANSPOSE (DR_GROUP_FIRST_ELEMENT (stmt_info))) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "vectorizable_store for slp transpose.\n"); + } + /* Transpose back for grouped stores. */ + vect_transform_back_slp_grouped_stores (bb_vinfo, + first_stmt_info); + + result_chain.create (vec_oprnds.length ()); + unsigned int const_nunits = nunits.to_constant (); + unsigned int group_size_b = DR_GROUP_SIZE_TRANS (first_stmt_info); + unsigned int array_num = group_size / group_size_b; + transpose_oprnd_store (vinfo, vec_oprnds, &result_chain, vec_num, + const_nunits, array_num, + first_stmt_info, gsi); + + /* For every store group, not for every vec, because transposing + and merging have changed the data reference access. */ + gcc_assert (group_size_b >= const_nunits); + unsigned int ncontinues = group_size_b / const_nunits; + + unsigned int k = 0; + for (i = 0; i < array_num; i++) + { + stmt_vec_info first_stmt_b; + BB_VINFO_GROUPED_STORES (vinfo).iterate (i, &first_stmt_b); + bool simd_lane_access_p + = STMT_VINFO_SIMD_LANE_ACCESS_P (first_stmt_b) != 0; + tree ref_type = get_group_alias_ptr_type (first_stmt_b); + dataref_ptr = vect_create_data_ref_ptr ( + vinfo, first_stmt_b, aggr_type, + simd_lane_access_p ? loop : NULL, + offset, &dummy, gsi, &ptr_incr, + simd_lane_access_p, bump); + dr_vec_info *cur_first_dr_info = get_dr_info (first_stmt_b); + for (unsigned int t = 0; t < ncontinues; t++) + { + vec_oprnd = result_chain[k]; + k++; + if (t > 0) + { + /* Bump the vector pointer. */ + dataref_ptr = bump_vector_ptr (vinfo, dataref_ptr, + ptr_incr, gsi, + first_stmt_b, bump); + } + add_new_stmt_vect_store (vinfo, vectype, dataref_ptr, + dataref_offset, ref_type, + cur_first_dr_info, vec_oprnd, + gsi, first_stmt_b); + } + } + oprnds.release (); + result_chain.release (); + vec_oprnds.release (); + return true; + } new_stmt = NULL; if (grouped_store) { @@ -8719,6 +9129,451 @@ hoist_defs_of_uses (stmt_vec_info stmt_info, class loop *loop) return true; } +static tree +calculate_new_type (tree vectype, unsigned int const_nunits, + unsigned int group_size_b, unsigned int &nloads, + unsigned int &ncontinues, tree &lvectype) +{ + tree ltype = TREE_TYPE (vectype); + /* nloads is the number of ARRAYs in a vector. + vectemp = {a[], b[], ...} */ + if (group_size_b < const_nunits) + { + tree ptype; + tree vtype + = vector_vector_composition_type (vectype, + const_nunits / group_size_b, + &ptype); + if (vtype != NULL_TREE) + { + nloads = const_nunits / group_size_b; + lvectype = vtype; + ltype = ptype; + ncontinues = 1; + } + } + /* ncontinues is the number of vectors from an ARRAY. + vectemp1 = {a[0], a[1], ...} + ... + vectempm = {a[k], a[k+1], ...} */ + else + { + nloads = 1; + ltype = vectype; + ncontinues = group_size_b / const_nunits; + } + ltype = build_aligned_type (ltype, TYPE_ALIGN (TREE_TYPE (vectype))); + return ltype; +} + +static void +generate_old_load_permutations (slp_tree slp_node, unsigned int group_size, + vec<unsigned> &old_load_permutation) +{ + /* Generate the old load permutations from the slp_node. */ + unsigned i = 0; + unsigned k = 0; + + /* If SLP_NODE has load_permutation, we copy it to old_load_permutation. + Otherwise, we generate a permutation sequentially. */ + if (SLP_TREE_LOAD_PERMUTATION (slp_node).exists ()) + { + FOR_EACH_VEC_ELT (SLP_TREE_LOAD_PERMUTATION (slp_node), i, k) + { + old_load_permutation.safe_push (k); + } + } + else + { + for (unsigned i = 0; i < group_size; i++) + { + old_load_permutation.safe_push (i); + } + } +} + +static void +generate_new_load_permutation_mapping (unsigned slp_node_length, + vec<unsigned> &group_idx, + const vec<unsigned> &load_permutation, + unsigned int group_size_b, + unsigned &new_group_size, + vec<unsigned> &group_from) +{ + /* group_num_vec: only stores the group_loads IDs which are caculated from + load_permutation. */ + auto_vec<unsigned> group_num_vec; + + /* Caculate which group_loads are the stmts in SLP_NODE from. */ + unsigned i = 0; + unsigned k = 0; + FOR_EACH_VEC_ELT (load_permutation, i, k) + { + unsigned int t0 = k / group_size_b; + if (!group_num_vec.contains (t0)) + { + group_num_vec.safe_push (t0); + } + group_from.safe_push (t0); + } + group_num_vec.qsort (cmp_for_group_num); + /* n_groups: the number of group_loads. */ + unsigned int n_groups = group_num_vec.length (); + new_group_size = n_groups * group_size_b; + for (i = 0; i < n_groups; i++) + { + group_idx.safe_push (group_num_vec[i] * group_size_b); + } + /* A new mapping from group_ind_vec to group_from. + For example: + Origin: group_from = {1,1,3,3,5,5,7,7}; + After mapping: group_from = {0,0,1,1,2,2,2,2}; */ + auto_vec<unsigned> group_ind_vec (n_groups); + for (k = 0; k < n_groups; k++) + { + group_ind_vec.safe_push (k); + } + for (i = 0; i < slp_node_length; i++) + { + for (k = 0; k < n_groups; k++) + { + if (group_from[i] == group_num_vec[k]) + { + group_from[i] = group_ind_vec[k]; + break; + } + } + } +} + +static void +generate_new_load_permutation (vec<unsigned> &new_load_permutation, + const vec<unsigned> &old_load_permutation, + slp_tree slp_node, bool &this_load_permuted, + const vec<unsigned> &group_from, + unsigned int group_size_b) +{ + unsigned slp_node_length = SLP_TREE_SCALAR_STMTS (slp_node).length (); + /* Generate the new load permutation from the new mapping. */ + new_load_permutation.create (slp_node_length); + unsigned i = 0; + unsigned k = 0; + FOR_EACH_VEC_ELT (old_load_permutation, i, k) + { + /* t1 is the new permutation of k in the old permutation. + t1 = base_address + offset: + base_address = group_from[i] * group_size_b; + offset = k % group_size_b. */ + unsigned int t1 + = group_from[i] * group_size_b + k % group_size_b; + new_load_permutation.safe_push (t1); + if (t1 != k) + { + this_load_permuted = true; + } + } +} + +static bool +is_slp_perm (bool slp_perm, bool this_load_permuted, poly_uint64 nunits, + unsigned int group_size, stmt_vec_info first_stmt_info) +{ + /* Calculate the unrolling factor based on the smallest type. */ + poly_uint64 unrolling_factor + = exact_div (common_multiple (nunits, group_size), group_size); + /* The load requires permutation when unrolling exposes + a gap either because the group is larger than the SLP + group-size or because there is a gap between the groups. */ + if (!slp_perm && !this_load_permuted + && (known_eq (unrolling_factor, 1U) + || (group_size == DR_GROUP_SIZE (first_stmt_info) + && DR_GROUP_GAP (first_stmt_info) == 0))) + { + return false; + } + else + { + return true; + } +} + +static void +generate_load_permutation (slp_tree slp_node, unsigned &new_group_size, + unsigned int group_size, unsigned int group_size_b, + bool &this_load_permuted, vec<unsigned> &group_idx, + vec<unsigned> &new_load_permutation) +{ + /* Generate the old load permutations from SLP_NODE. */ + vec<unsigned> old_load_permutation; + old_load_permutation.create (group_size); + generate_old_load_permutations (slp_node, group_size, old_load_permutation); + + /* Caculate which group_loads are the stmts in SLP_NODE from. */ + unsigned slp_node_length = SLP_TREE_SCALAR_STMTS (slp_node).length (); + /* group_from: stores the group_loads ID for every stmt in SLP_NODE. */ + vec<unsigned> group_from; + group_from.create (slp_node_length); + generate_new_load_permutation_mapping (slp_node_length, group_idx, + old_load_permutation, + group_size_b, new_group_size, + group_from); + + /* Generate the new load permutation from the new mapping and caculate + this_load_permuted flag. If this_load_permuted is true, we need execute + slp permutation by using new load permutation. */ + generate_new_load_permutation (new_load_permutation, old_load_permutation, + slp_node, this_load_permuted, group_from, + group_size_b); + old_load_permutation.release (); + group_from.release (); +} + +static unsigned int +dr_align_vect_load (vec_info *vinfo, dr_vec_info *cur_first_dr_info, + tree vectype, unsigned HOST_WIDE_INT &align, + enum dr_alignment_support alignment_support_scheme) +{ + unsigned int misalign = 0; + + align = known_alignment (DR_TARGET_ALIGNMENT (cur_first_dr_info)); + if (alignment_support_scheme == dr_aligned) + { + gcc_assert (aligned_access_p (cur_first_dr_info, vectype)); + } + else if (cur_first_dr_info->misalignment == -1) + { + align = dr_alignment (vect_dr_behavior (vinfo, cur_first_dr_info)); + } + else + { + misalign = cur_first_dr_info->misalignment; + } + return misalign; +} + +static stmt_vec_info +add_new_stmt_vect_load (vec_info *vinfo, tree vectype, tree dataref_ptr, + tree dataref_offset, tree ref_type, tree ltype, + gassign *(&new_stmt), dr_vec_info *cur_first_dr_info, + gimple_stmt_iterator *gsi, stmt_vec_info stmt_info) +{ + /* Data align. */ + int malign = dr_misalignment (cur_first_dr_info, vectype); + enum dr_alignment_support alignment_support_scheme + = vect_supportable_dr_alignment (vinfo, cur_first_dr_info, + vectype, malign); + unsigned HOST_WIDE_INT align; + unsigned int misalign = dr_align_vect_load (vinfo, cur_first_dr_info, + vectype, align, + alignment_support_scheme); + if (dataref_offset == NULL_TREE && TREE_CODE (dataref_ptr) == SSA_NAME) + { + set_ptr_info_alignment (get_ptr_info (dataref_ptr), align, misalign); + } + + /* Get data_ref. */ + tree offset = dataref_offset ? dataref_offset : build_int_cst (ref_type, 0); + tree data_ref = fold_build2 (MEM_REF, ltype, dataref_ptr, offset); + if (alignment_support_scheme == dr_aligned) + { + ; + } + else if (cur_first_dr_info->misalignment == -1) + { + TREE_TYPE (data_ref) + = build_aligned_type (TREE_TYPE (data_ref), align * BITS_PER_UNIT); + } + else + { + tree elem_type = TREE_TYPE (vectype); + TREE_TYPE (data_ref) + = build_aligned_type (TREE_TYPE (data_ref), TYPE_ALIGN (elem_type)); + } + + /* Add new stmt. */ + vect_copy_ref_info (data_ref, DR_REF (cur_first_dr_info->dr)); + new_stmt = gimple_build_assign (make_ssa_name (ltype), data_ref); + vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi, true); + stmt_vec_info vec_stmt_info = vinfo->lookup_stmt (new_stmt); + return vec_stmt_info; +} + +static void +push_new_stmt_to_dr_chain (bool slp_perm, stmt_vec_info new_stmt_info, + vec<tree> dr_chain, slp_tree slp_node) +{ + if (slp_perm) + dr_chain.quick_push (gimple_assign_lhs (new_stmt_info->stmt)); + else + SLP_TREE_VEC_STMTS (slp_node).quick_push (new_stmt_info->stmt); +} + +static stmt_vec_info +get_first_stmt_info_before_transpose (stmt_vec_info first_stmt_info, + unsigned int group_el, + unsigned int group_size) +{ + stmt_vec_info last_stmt_info = first_stmt_info; + unsigned int count = 0; + gcc_assert (group_el < group_size); + while (count < group_el) + { + last_stmt_info = DR_GROUP_NEXT_ELEMENT (last_stmt_info); + count++; + } + return last_stmt_info; +} + +static stmt_vec_info +add_new_stmt_for_nloads_greater_than_one (vec_info *vinfo, tree lvectype, + tree vectype, + vec<constructor_elt, va_gc> *v, + stmt_vec_info stmt_info, + gimple_stmt_iterator *gsi) +{ + tree vec_inv = build_constructor (lvectype, v); + tree new_temp = vect_init_vector (vinfo, stmt_info, vec_inv, lvectype, gsi, true); + stmt_vec_info new_stmt_info = vinfo->lookup_def (new_temp); + if (lvectype != vectype) + { + gassign *new_stmt = gimple_build_assign (make_ssa_name (vectype), + VIEW_CONVERT_EXPR, + build1 (VIEW_CONVERT_EXPR, + vectype, new_temp)); + vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi, true); + new_stmt_info = vinfo->lookup_stmt (new_stmt); + } + return new_stmt_info; +} + +/* Function new_vect_stmt_for_nloads. + + New a VEC_STMT when nloads Arrays are merged into a vector. + + ncopies is the number of vectors that need to be loaded from memmory. + nloads is the number of ARRAYs in a vector. + vectemp = {a[], b[], ...} */ + +static void +new_vect_stmt_for_nloads (vec_info *vinfo, unsigned int ncopies, + unsigned int nloads, const vec<unsigned> &group_idx, + stmt_vec_info stmt_info, offset_info *offset_info, + vectype_info *vectype_info, + vect_memory_access_type memory_access_type, + bool slp_perm, vec<tree> dr_chain, slp_tree slp_node, + gimple_stmt_iterator *gsi) +{ + vec<constructor_elt, va_gc> *v = NULL; + stmt_vec_info first_stmt_info = DR_GROUP_FIRST_ELEMENT (stmt_info); + unsigned int group_size = DR_GROUP_SIZE (first_stmt_info); + stmt_vec_info first_stmt_info_b = NULL; + stmt_vec_info new_stmt_info = NULL; + tree dataref_ptr = NULL_TREE; + tree dummy; + gimple *ptr_incr = NULL; + unsigned int n = 0; + for (unsigned int i = 0; i < ncopies; i++) + { + vec_alloc (v, nloads); + for (unsigned int t = 0; t < nloads; t++) + { + first_stmt_info_b = get_first_stmt_info_before_transpose ( + first_stmt_info, group_idx[n++], group_size); + dr_vec_info* cur_first_dr_info = get_dr_info (first_stmt_info_b); + tree bump = vect_get_data_ptr_increment (vinfo, cur_first_dr_info, + vectype_info->ltype, + memory_access_type); + bool simd_lane_access_p + = STMT_VINFO_SIMD_LANE_ACCESS_P (first_stmt_info_b) != 0; + + /* Create dataref_ptr which is point to init_address. */ + dataref_ptr = vect_create_data_ref_ptr ( + vinfo, first_stmt_info_b, vectype_info->ltype, NULL, + offset_info->offset, &dummy, gsi, &ptr_incr, + simd_lane_access_p, bump); + + gassign *new_stmt = NULL; + new_stmt_info = add_new_stmt_vect_load (vinfo, vectype_info->vectype, dataref_ptr, + offset_info->dataref_offset, + vectype_info->ref_type, vectype_info->ltype, + new_stmt, cur_first_dr_info, gsi, + first_stmt_info_b); + + CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, gimple_assign_lhs (new_stmt)); + } + new_stmt_info = add_new_stmt_for_nloads_greater_than_one ( + vinfo, vectype_info->lvectype, + vectype_info->vectype, v, + first_stmt_info_b, gsi); + push_new_stmt_to_dr_chain (slp_perm, new_stmt_info, + dr_chain, slp_node); + } +} + +/* Function new_vect_stmt_for_ncontinues. + + New a VEC_STMTs when an Array is divided into several vectors. + + n_groups is the number of ARRAYs. + ncontinues is the number of vectors from an ARRAY. + vectemp1 = {a[0], a[1], ...} + ... + vectempm = {a[k], a[k+1], ...} */ + +static void +new_vect_stmt_for_ncontinues (vec_info *vinfo, unsigned int ncontinues, + const vec<unsigned> &group_idx, + stmt_vec_info stmt_info, + offset_info* offset_info, + vectype_info* vectype_info, + vect_memory_access_type memory_access_type, + bool slp_perm, vec<tree> &dr_chain, + slp_tree slp_node, + gimple_stmt_iterator *gsi) +{ + stmt_vec_info first_stmt_info = DR_GROUP_FIRST_ELEMENT (stmt_info); + unsigned int group_size = DR_GROUP_SIZE (first_stmt_info); + stmt_vec_info new_stmt_info = NULL; + tree dataref_ptr = NULL_TREE; + tree dummy; + gimple *ptr_incr = NULL; + unsigned int n_groups = group_idx.length (); + for (unsigned int i = 0; i < n_groups; i++) + { + stmt_vec_info first_stmt_info_b = get_first_stmt_info_before_transpose ( + first_stmt_info, group_idx[i], group_size); + dr_vec_info* cur_first_dr_info = get_dr_info (first_stmt_info_b); + tree bump = vect_get_data_ptr_increment (vinfo, cur_first_dr_info, + vectype_info->ltype, memory_access_type); + bool simd_lane_access_p + = STMT_VINFO_SIMD_LANE_ACCESS_P (first_stmt_info_b) != 0; + for (unsigned int k = 0; k < ncontinues; k++) + { + /* Create dataref_ptr which is point to init_address. */ + if (k == 0) + { + dataref_ptr = vect_create_data_ref_ptr ( + vinfo, first_stmt_info_b, vectype_info->ltype, NULL, + offset_info->offset, &dummy, gsi, &ptr_incr, + simd_lane_access_p, bump); + } + else + { + dataref_ptr = bump_vector_ptr (vinfo, dataref_ptr, ptr_incr, + gsi, first_stmt_info_b, bump); + } + gassign *new_stmt = NULL; + new_stmt_info = add_new_stmt_vect_load (vinfo, vectype_info->vectype, dataref_ptr, + offset_info->dataref_offset, + vectype_info->ref_type, vectype_info->ltype, + new_stmt, cur_first_dr_info, gsi, + first_stmt_info_b); + push_new_stmt_to_dr_chain (slp_perm, new_stmt_info, + dr_chain, slp_node); + } + } +} + /* vectorizable_load. Check if STMT_INFO reads a non scalar data-ref (array/pointer/structure) @@ -9338,6 +10193,8 @@ vectorizable_load (vec_info *vinfo, if (bb_vinfo) first_stmt_info_for_drptr = vect_find_first_scalar_stmt_in_slp (slp_node); + // first_stmt_info_for_drptr = SLP_TREE_SCALAR_STMTS (slp_node)[0]; + /* Check if the chain of loads is already vectorized. */ if (STMT_VINFO_VEC_STMTS (first_stmt_info).exists () @@ -9601,6 +10458,9 @@ vectorizable_load (vec_info *vinfo, } tree vec_mask = NULL_TREE; poly_uint64 group_elt = 0; + unsigned new_group_size = 0; + vec<unsigned> new_load_permutation; + for (j = 0; j < ncopies; j++) { /* 1. Create the vector or array pointer update chain. */ @@ -9621,6 +10481,15 @@ vectorizable_load (vec_info *vinfo, dataref_ptr = unshare_expr (DR_BASE_ADDRESS (first_dr_info->dr)); dataref_offset = build_int_cst (ref_type, 0); } + /* If the stmt_info need to be transposed recovery, dataref_ptr + will be caculated later. */ + else if (slp && is_a <bb_vec_info> (vinfo) + && STMT_VINFO_GROUPED_ACCESS (stmt_info) + && DR_GROUP_SLP_TRANSPOSE ( + DR_GROUP_FIRST_ELEMENT (stmt_info))) + { + dataref_ptr = NULL_TREE; + } else if (diff_first_stmt_info) { dataref_ptr @@ -9731,6 +10600,63 @@ vectorizable_load (vec_info *vinfo, /* Record that VEC_ARRAY is now dead. */ vect_clobber_variable (vinfo, stmt_info, gsi, vec_array); } + else if (slp && is_a <bb_vec_info> (vinfo) + && STMT_VINFO_GROUPED_ACCESS (stmt_info) + && DR_GROUP_SLP_TRANSPOSE (DR_GROUP_FIRST_ELEMENT (stmt_info))) + { + if (dump_enabled_p ()) + { + dump_printf_loc (MSG_NOTE, vect_location, + "vectorizable_load for slp transpose.\n"); + } + /* group_size: the size of group after merging. + group_size_b: the size of group before merging. + const_nunits: TYPE_VECTOR_SUBPARTS (vectype), it is the number of + elements in a vector. + nloads: const_nunits / group_size_b or 1, it means the number + of ARRAYs in a vector. + ncontinues: group_size_b / const_nunits or 1, it means the number + of vectors from an ARRAY. */ + unsigned int group_size_b = DR_GROUP_SIZE_TRANS (first_stmt_info); + unsigned int const_nunits = nunits.to_constant (); + unsigned int nloads = const_nunits; + unsigned int ncontinues = group_size_b; + tree lvectype = vectype; + tree ltype = calculate_new_type (vectype, const_nunits, + group_size_b, nloads, + ncontinues, lvectype); + bool this_load_permuted = false; + auto_vec<unsigned> group_idx; + generate_load_permutation (slp_node, new_group_size, group_size, + group_size_b, this_load_permuted, + group_idx, new_load_permutation); + slp_perm = is_slp_perm (slp_perm, this_load_permuted, nunits, + group_size, first_stmt_info); + + /* ncopies: the number of vectors that need to be loaded from + memmory. */ + unsigned int ncopies = new_group_size / const_nunits; + offset_info offset_info = {offset, NULL_TREE, dataref_offset}; + vectype_info vectype_info = {vectype, ltype, lvectype, ref_type}; + if (slp_perm) + { + dr_chain.create (ncopies); + } + if (nloads > 1 && ncontinues == 1) + { + new_vect_stmt_for_nloads (vinfo, ncopies, nloads, group_idx, + stmt_info, &offset_info, &vectype_info, + memory_access_type, slp_perm, dr_chain, + slp_node, gsi); + } + else + { + new_vect_stmt_for_ncontinues (vinfo, ncontinues, group_idx, + stmt_info, &offset_info, + &vectype_info, memory_access_type, + slp_perm, dr_chain, slp_node, gsi); + } + } else { for (i = 0; i < vec_num; i++) @@ -10177,7 +11103,32 @@ vectorizable_load (vec_info *vinfo, if (slp && !slp_perm) continue; - if (slp_perm) + /* Using the new load permutation to generate vector permute statements + from a list of loads in DR_CHAIN. */ + if (slp && slp_perm && is_a <bb_vec_info> (vinfo) + && STMT_VINFO_GROUPED_ACCESS (stmt_info) + && DR_GROUP_SLP_TRANSPOSE (DR_GROUP_FIRST_ELEMENT (stmt_info))) + { + unsigned n_perms; + stmt_vec_info stmt_info_ = SLP_TREE_SCALAR_STMTS (slp_node)[0]; + unsigned int old_size = DR_GROUP_SIZE (stmt_info); + DR_GROUP_SIZE (stmt_info_) = new_group_size; + vec<unsigned> old_load_permutation + = SLP_TREE_LOAD_PERMUTATION (slp_node); + SLP_TREE_LOAD_PERMUTATION (slp_node) = new_load_permutation; + bool perm_load_success = vect_transform_slp_perm_load ( + vinfo, slp_node, dr_chain, gsi, vf, + false, &n_perms); + DR_GROUP_SIZE (stmt_info_) = old_size; + SLP_TREE_LOAD_PERMUTATION (slp_node) = old_load_permutation; + new_load_permutation.release (); + if (!perm_load_success) + { + dr_chain.release (); + return false; + } + } + else if (slp_perm) { unsigned n_perms; /* For SLP we know we've seen all possible uses of dr_chain so diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h index 642eb0aeb..e13bc6c99 100644 --- a/gcc/tree-vectorizer.h +++ b/gcc/tree-vectorizer.h @@ -412,6 +412,21 @@ public: vec<ddr_p> ddrs; }; +/* Information about offset in vectorizable_load. */ +struct offset_info { + tree offset; + tree byte_offset; + tree dataref_offset; +}; + +/* Information about vectype in vectorizable_load. */ +struct vectype_info { + tree vectype; + tree ltype; + tree lvectype; + tree ref_type; +}; + /* Vectorizer state common between loop and basic-block vectorization. */ class vec_info { public: @@ -455,6 +470,14 @@ public: stmt in the chain. */ auto_vec<stmt_vec_info> grouped_stores; + /* All interleaving chains of loads, represented by the first + stmt in the chain. */ + auto_vec<stmt_vec_info> grouped_loads; + + /* All interleaving chains of stores (before transposed), represented by all + stmt in the chain. */ + auto_vec<vec<stmt_vec_info> > scalar_stores; + /* The set of vector modes used in the vectorized region. */ mode_set used_vector_modes; @@ -899,6 +922,8 @@ public: #define LOOP_VINFO_CHECK_NONZERO(L) (L)->check_nonzero #define LOOP_VINFO_LOWER_BOUNDS(L) (L)->lower_bounds #define LOOP_VINFO_GROUPED_STORES(L) (L)->grouped_stores +#define LOOP_VINFO_GROUPED_LOADS(L) (L)->grouped_loads +#define LOOP_VINFO_SCALAR_STORES(L) (L)->scalar_stores #define LOOP_VINFO_SLP_INSTANCES(L) (L)->slp_instances #define LOOP_VINFO_SLP_UNROLLING_FACTOR(L) (L)->slp_unrolling_factor #define LOOP_VINFO_REDUCTIONS(L) (L)->reductions @@ -982,6 +1007,25 @@ public: vec<basic_block> bbs; vec<slp_root> roots; + + /* True, if bb_vinfo can goto vect_analyze_slp. */ + bool before_slp; + + /* True, if bb_vinfo is a transposed version. */ + bool transposed; + + /* The number of transposed groups. */ + int transposed_group; + + /* The cost of the scalar iterations. */ + int scalar_cost; + + /* The cost of the vector prologue and epilogue, including peeled + iterations and set-up code. */ + int vec_outside_cost; + + /* The cost of the vector loop body. */ + int vec_inside_cost; } *bb_vec_info; #define BB_VINFO_BB(B) (B)->bb @@ -989,6 +1033,14 @@ public: #define BB_VINFO_SLP_INSTANCES(B) (B)->slp_instances #define BB_VINFO_DATAREFS(B) (B)->shared->datarefs #define BB_VINFO_DDRS(B) (B)->shared->ddrs +#define BB_VINFO_GROUPED_LOADS(B) (B)->grouped_loads +#define BB_VINFO_SCALAR_STORES(B) (B)->scalar_stores +#define BB_VINFO_VEC_OUTSIDE_COST(B) (B)->vec_outside_cost +#define BB_VINFO_VEC_INSIDE_COST(B) (B)->vec_inside_cost +#define BB_VINFO_SCALAR_COST(B) (B)->scalar_cost +#define BB_VINFO_SLP_TRANSPOSED(B) (B)->transposed +#define BB_VINFO_BEFORE_SLP(B) (B)->before_slp +#define BB_VINFO_TRANS_GROUPS(B) (B)->transposed_group /*-----------------------------------------------------------------*/ /* Info on vectorized defs. */ @@ -1219,6 +1271,17 @@ public: stmt_vec_info next_element; /* The size of the group. */ unsigned int size; + + /* The size of the group before transposed. */ + unsigned int size_before_transpose; + + /* If true, the stmt_info is slp transposed. */ + bool slp_transpose; + + /* Mark the group store number for rebuild interleaving chain + during transpose phase. Value -1 represents unable to transpose. */ + int group_number; + /* For stores, number of stores from this group seen. We vectorize the last one. */ unsigned int store_count; @@ -1226,6 +1289,9 @@ public: is 1. */ unsigned int gap; + /* The gap before transposed. */ + unsigned int gap_before_transpose; + /* The minimum negative dependence distance this stmt participates in or zero if none. */ unsigned int min_neg_dist; @@ -1427,6 +1493,12 @@ struct gather_scatter_info { #define STMT_VINFO_SLP_VECT_ONLY(S) (S)->slp_vect_only_p #define STMT_VINFO_SLP_VECT_ONLY_PATTERN(S) (S)->slp_vect_pattern_only_p +#define DR_GROUP_SLP_TRANSPOSE(S) \ + (gcc_checking_assert ((S)->dr_aux.dr), (S)->slp_transpose) +#define DR_GROUP_SIZE_TRANS(S) \ + (gcc_checking_assert ((S)->dr_aux.dr), (S)->size_before_transpose) +#define DR_GROUP_NUMBER(S) \ + (gcc_checking_assert ((S)->dr_aux.dr), (S)->group_number) #define DR_GROUP_FIRST_ELEMENT(S) \ (gcc_checking_assert ((S)->dr_aux.dr), (S)->first_element) #define DR_GROUP_NEXT_ELEMENT(S) \ @@ -1437,6 +1509,8 @@ struct gather_scatter_info { (gcc_checking_assert ((S)->dr_aux.dr), (S)->store_count) #define DR_GROUP_GAP(S) \ (gcc_checking_assert ((S)->dr_aux.dr), (S)->gap) +#define DR_GROUP_GAP_TRANS(S) \ + (gcc_checking_assert ((S)->dr_aux.dr), (S)->gap_before_transpose) #define REDUC_GROUP_FIRST_ELEMENT(S) \ (gcc_checking_assert (!(S)->dr_aux.dr), (S)->first_element) @@ -2033,6 +2107,17 @@ vect_get_scalar_dr_size (dr_vec_info *dr_info) return tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr_info->dr)))); } +/* Compare two unsigned int A and B. + Sorting them in ascending order. */ + +static inline int +cmp_for_group_num (const void *a_, const void *b_) +{ + unsigned int a = *(unsigned int *)const_cast<void *>(a_); + unsigned int b = *(unsigned int *)const_cast<void *>(b_); + return a < b ? -1 : 1; +} + /* Return true if LOOP_VINFO requires a runtime check for whether the vector loop is profitable. */ @@ -2152,7 +2237,7 @@ record_stmt_cost (stmt_vector_for_cost *body_cost_vec, int count, extern void vect_finish_replace_stmt (vec_info *, stmt_vec_info, gimple *); extern void vect_finish_stmt_generation (vec_info *, stmt_vec_info, gimple *, - gimple_stmt_iterator *); + gimple_stmt_iterator *,bool transpose=false); extern opt_result vect_mark_stmts_to_be_vectorized (loop_vec_info, bool *); extern tree vect_get_store_rhs (stmt_vec_info); void vect_get_vec_defs_for_operand (vec_info *vinfo, stmt_vec_info, unsigned, @@ -2168,7 +2253,7 @@ void vect_get_vec_defs (vec_info *, stmt_vec_info, slp_tree, unsigned, tree = NULL, vec<tree> * = NULL, tree = NULL, tree = NULL, vec<tree> * = NULL, tree = NULL); extern tree vect_init_vector (vec_info *, stmt_vec_info, tree, tree, - gimple_stmt_iterator *); + gimple_stmt_iterator *, bool transpose=false); extern tree vect_get_slp_vect_def (slp_tree, unsigned); extern bool vect_transform_stmt (vec_info *, stmt_vec_info, gimple_stmt_iterator *, @@ -2235,6 +2320,9 @@ extern bool vect_load_lanes_supported (tree, unsigned HOST_WIDE_INT, bool); extern void vect_permute_store_chain (vec_info *, vec<tree> &, unsigned int, stmt_vec_info, gimple_stmt_iterator *, vec<tree> *); +extern void vect_transpose_store_chain (vec_info *, vec<tree>, unsigned int, + unsigned int, stmt_vec_info, + gimple_stmt_iterator *, vec<tree> *); extern tree vect_setup_realignment (vec_info *, stmt_vec_info, gimple_stmt_iterator *, tree *, enum dr_alignment_support, tree, @@ -2262,7 +2350,8 @@ extern bool check_reduction_path (dump_user_location_t, loop_p, gphi *, tree, enum tree_code); extern bool needs_fold_left_reduction_p (tree, code_helper); /* Drive for loop analysis stage. */ -extern opt_loop_vec_info vect_analyze_loop (class loop *, vec_info_shared *); +extern opt_loop_vec_info vect_analyze_loop (class loop *, vec_info_shared *, + bool result_only_p = false); extern tree vect_build_loop_niters (loop_vec_info, bool * = NULL); extern void vect_gen_vector_loop_niters (loop_vec_info, tree, tree *, tree *, bool); @@ -2331,6 +2420,7 @@ extern bool vect_transform_slp_perm_load (vec_info *, slp_tree, const vec<tree> gimple_stmt_iterator *, poly_uint64, bool, unsigned *, unsigned * = nullptr, bool = false); +extern void vect_transform_back_slp_grouped_stores (bb_vec_info, stmt_vec_info); extern bool vect_slp_analyze_operations (vec_info *); extern void vect_schedule_slp (vec_info *, const vec<slp_instance> &); extern opt_result vect_analyze_slp (vec_info *, unsigned); -- 2.33.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2