summaryrefslogtreecommitdiff
path: root/0099-Enable-Transposed-SLP.patch
diff options
context:
space:
mode:
Diffstat (limited to '0099-Enable-Transposed-SLP.patch')
-rw-r--r--0099-Enable-Transposed-SLP.patch5624
1 files changed, 5624 insertions, 0 deletions
diff --git a/0099-Enable-Transposed-SLP.patch b/0099-Enable-Transposed-SLP.patch
new file mode 100644
index 0000000..b4e8b24
--- /dev/null
+++ b/0099-Enable-Transposed-SLP.patch
@@ -0,0 +1,5624 @@
+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> &current_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
+