This adds support for matching FMS where the reassoc has left the top level
operation as a PLUS_MINUS.
i.e. matching
c.real + (a.imag * b.imag - a.real * b.real)
c.imag - (a.real * b.imag + a.imag * b.real)
which happens when the accumulator is not the same as the destination.
i.e.
d[i] c[i] - (a[i] * b[i])
vs
c[i] -= a[i] * b[i]
Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.
Any comments?
Thanks,
Tamar
gcc/ChangeLog:
* tree-vect-slp-patterns.cc (complex_fms_pattern::matches): Check
another form of FMS.
gcc/testsuite/ChangeLog:
* gfortran.target/aarch64/pr122408_1.f90: update scans to check for FMS.
* gcc.dg/vect/complex/complex-mul2.c: New test.
* gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c: New
test.
* gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c: New
test.
* gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c: New
test.
---
diff --git a/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c
b/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c
new file mode 100644
index
0000000000000000000000000000000000000000..6a45ebdbec8c91dfae98f40f910c7a5d3f2ffbf5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c
@@ -0,0 +1,67 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros" } */
+/* { dg-require-effective-target vect_complex_add_double } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+extern void abort (void);
+
+#define N 16
+
+#define DEF(NAME, TYPE, EXPR) \
+ __attribute__((noipa)) void \
+ NAME (_Complex TYPE *__restrict d, _Complex TYPE *__restrict c,\
+ _Complex TYPE *__restrict a, _Complex TYPE *__restrict b,\
+ int n) \
+ { \
+ for (int i = 0; i < n; i++) \
+ d[i] = EXPR; \
+ } \
+ \
+ __attribute__((noipa, optimize ("no-tree-vectorize"))) void \
+ NAME##_ref (_Complex TYPE *__restrict d, _Complex TYPE *__restrict c,\
+ _Complex TYPE *__restrict a, _Complex TYPE *__restrict b,\
+ int n) \
+ { \
+ for (int i = 0; i < n; i++) \
+ d[i] = EXPR; \
+ }
+
+DEF (fms_f, float, c[i] - a[i] * b[i])
+DEF (fms_d, double, c[i] - a[i] * b[i])
+DEF (fmsconj_f, float, c[i] - a[i] * ~b[i])
+DEF (mul_f, float, a[i] * b[i])
+
+#define CHECK(NAME, TYPE) \
+ do { \
+ _Complex TYPE a[N], b[N], c[N], d[N], ref[N]; \
+ for (int i = 0; i < N; i++) \
+ { \
+ __real__ a[i] = i + 1; \
+ __imag__ a[i] = 2 * i + 3; \
+ __real__ b[i] = 3 * i - 1; \
+ __imag__ b[i] = i + 5; \
+ __real__ c[i] = 100 + i; \
+ __imag__ c[i] = 200 - i; \
+ } \
+ NAME (d, c, a, b, N); \
+ NAME##_ref (ref, c, a, b, N); \
+ _Pragma("novect") \
+ for (int i = 0; i < N; i++) \
+ if (__real__ d[i] != __real__ ref[i] \
+ || __imag__ d[i] != __imag__ ref[i]) \
+ abort (); \
+ } while (0)
+
+int
+main (void)
+{
+ CHECK (fms_f, float);
+ CHECK (fms_d, double);
+ CHECK (fmsconj_f, float);
+ CHECK (mul_f, float);
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect"
} } */
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS_CONJ"
"vect" } } */
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_MUL \\(" "vect"
} } */
diff --git
a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c
b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c
new file mode 100644
index
0000000000000000000000000000000000000000..3062878edf6e7dea5b0b51be132d5506b274cb14
--- /dev/null
+++
b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros" } */
+/* { dg-require-effective-target vect_complex_add_float } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+manual_invalid_fms (float *__restrict d, float *__restrict c,
+ float *__restrict a, float *__restrict b)
+{
+ for (int r = 0; r < 100; r += 2)
+ {
+ int i = r + 1;
+ float cr = c[r];
+ float ci = c[i];
+ float ar = a[r];
+ float ai = a[i];
+ float br = b[r];
+ float bi = b[i];
+ d[r] = cr + ar * br - ai * bi;
+ d[i] = ci - (ar * bi + ai * br);
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS pattern" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
+/* { dg-final { scan-tree-dump-not "add new stmt: \[^\n\r]*COMPLEX_FMS" "vect"
} } */
diff --git
a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c
b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c
new file mode 100644
index
0000000000000000000000000000000000000000..3253b3b2d344f450daa96a38f0134328cad6ea92
--- /dev/null
+++
b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros
-fdump-tree-vect-details" } */
+/* { dg-require-effective-target vect_complex_add_double } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+fms_out (_Complex double *__restrict d, _Complex double *__restrict c,
+ _Complex double *__restrict a, _Complex double *__restrict b,
+ int n)
+{
+ for (int i = 0; i < n; ++i)
+ d[i] = c[i] - a[i] * b[i];
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect"
} } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
diff --git
a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c
b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c
new file mode 100644
index
0000000000000000000000000000000000000000..51e6b0d8762842ab006115c7299305857fb3a362
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros
-fdump-tree-vect-details" } */
+/* { dg-require-effective-target vect_complex_add_float } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+fms_out (_Complex float *__restrict d, _Complex float *__restrict c,
+ _Complex float *__restrict a, _Complex float *__restrict b,
+ int n)
+{
+ for (int i = 0; i < n; ++i)
+ d[i] = c[i] - a[i] * b[i];
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect"
{ xfail arm*-*-* } } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
diff --git a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
index
b06950d1baae7d8aca2e1c96c8e6e942781b71bd..d62330d4a7760235bf25179b072d986bbe87c653
100644
--- a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
+++ b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
@@ -57,10 +57,10 @@ subroutine c_sub_a_conjb(n, a, c, b) ! C -= A * conj(B)
end do
end subroutine c_sub_a_conjb
-! The two plain multiplies form .COMPLEX_MUL (#0 + #90) and the two conjugate
-! multiplies form .COMPLEX_MUL_CONJ (#0 + #270). PR122408 is about detecting
-! the conjugate form, so the #270 and #180 counts are the ones that guard it.
-! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d,
v[0-9]+.2d, #0} 4 } }
+! The accumulations form .COMPLEX_FMA (#0 + #90) and .COMPLEX_FMS (#180 +
+! #270), with the conjugate forms swapping the #90 and #270 rotations.
PR122408
+! is about detecting the conjugate form, so the #90 and #270 counts guard it.
+! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d,
v[0-9]+.2d, #0} 2 } }
! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d,
v[0-9]+.2d, #90} 2 } }
! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d,
v[0-9]+.2d, #270} 2 } }
-! { dg-final { scan-assembler-not {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d,
#180} } }
+! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d,
v[0-9]+.2d, #180} 2 } }
diff --git a/gcc/tree-vect-slp-patterns.cc b/gcc/tree-vect-slp-patterns.cc
index
f77993100ad19ce5502900022a68b39201d24a54..aae000ee5292ccb1521928e43f514a14386a7827
100644
--- a/gcc/tree-vect-slp-patterns.cc
+++ b/gcc/tree-vect-slp-patterns.cc
@@ -1305,7 +1305,94 @@ complex_fms_pattern::matches (complex_operation_t op,
slp_compat_nodes_map_t *compat_cache,
slp_tree * ref_node, vec<slp_tree> *ops)
{
- internal_fn ifn = IFN_LAST;
+ /* It's only valid to form FMSs with -ffp-contract=fast. */
+ if (!SLP_TREE_VECTYPE (*ref_node)
+ || (flag_fp_contract_mode != FP_CONTRACT_FAST
+ && FLOAT_TYPE_P (SLP_TREE_VECTYPE (*ref_node))))
+ return IFN_LAST;
+
+ /* Match c - a * b when SLP has built the result as:
+
+ c.real + (a.imag * b.imag - a.real * b.real)
+ c.imag - (a.real * b.imag + a.imag * b.real)
+
+ This represents the same operation as the existing FMS matcher below,
+ but with the accumulator outside the complex product node. */
+ if (op == PLUS_MINUS)
+ {
+ auto plus_ops = SLP_TREE_CHILDREN ((*ops)[0]);
+ auto minus_ops = SLP_TREE_CHILDREN ((*ops)[1]);
+ if (plus_ops.length () != 2 || minus_ops.length () != 2)
+ return IFN_LAST;
+
+ slp_tree acc = minus_ops[0];
+ slp_tree prod = minus_ops[1];
+ if (!((plus_ops[0] == acc && plus_ops[1] == prod)
+ || (plus_ops[1] == acc && plus_ops[0] == prod)))
+ return IFN_LAST;
+ if (linear_loads_p (perm_cache, acc) != PERM_EVENODD)
+ return IFN_LAST;
+
+ auto_vec<slp_tree> prod_ops;
+ if (vect_detect_pair_op (prod, true, &prod_ops) != MINUS_PLUS)
+ return IFN_LAST;
+ if (prod_ops.length () != 2)
+ return IFN_LAST;
+
+ auto prod_left = SLP_TREE_CHILDREN (prod_ops[0]);
+ auto prod_right = SLP_TREE_CHILDREN (prod_ops[1]);
+ if (prod_left.length () != 2
+ || prod_right.length () != 2
+ || !vect_match_expression_p (prod_left[0], MULT_EXPR)
+ || !vect_match_expression_p (prod_left[1], MULT_EXPR)
+ || !vect_match_expression_p (prod_right[0], MULT_EXPR)
+ || !vect_match_expression_p (prod_right[1], MULT_EXPR))
+ return IFN_LAST;
+
+ auto_vec<slp_tree> left_op, right_op;
+ left_op.safe_splice (SLP_TREE_CHILDREN (prod_left[0]));
+ right_op.safe_splice (SLP_TREE_CHILDREN (prod_left[1]));
+
+ enum _conj_status status;
+ auto_vec<slp_tree> res_ops;
+ if (!vect_validate_multiplication_commutative (perm_cache, compat_cache,
+ right_op, left_op, true,
+ res_ops, &status))
+ return IFN_LAST;
+
+ internal_fn ifn = status == CONJ_NONE ? IFN_COMPLEX_FMS
+ : IFN_COMPLEX_FMS_CONJ;
+ if (!vect_pattern_validate_optab (ifn, *ref_node))
+ return IFN_LAST;
+
+ ops->truncate (0);
+ ops->create (4);
+
+ complex_perm_kinds_t kind = linear_loads_p (perm_cache, res_ops[0]);
+ if (kind == PERM_EVENODD || kind == PERM_TOP)
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[3]);
+ }
+ else if (kind == PERM_EVENEVEN && status != CONJ_SND)
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[2]);
+ }
+ else
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[3]);
+ }
+
+ return ifn;
+ }
/* We need to ignore the two_operands nodes that may also match,
for that we can check if they have any scalar statements and also
@@ -1318,11 +1405,6 @@ complex_fms_pattern::matches (complex_operation_t op,
if (!vect_match_expression_p (root, MINUS_EXPR))
return IFN_LAST;
- /* It's only valid to form FMSs with -ffp-contract=fast. */
- if (flag_fp_contract_mode != FP_CONTRACT_FAST
- && FLOAT_TYPE_P (SLP_TREE_VECTYPE (*ref_node)))
- return IFN_LAST;
-
/* TODO: Support invariants here, with the new layout CADD now
can match before we get a chance to try CFMS. */
auto nodes = SLP_TREE_CHILDREN (root);
@@ -1352,11 +1434,8 @@ complex_fms_pattern::matches (complex_operation_t op,
res_ops, &status))
return IFN_LAST;
- if (status == CONJ_NONE)
- ifn = IFN_COMPLEX_FMS;
- else
- ifn = IFN_COMPLEX_FMS_CONJ;
-
+ internal_fn ifn = status == CONJ_NONE ? IFN_COMPLEX_FMS
+ : IFN_COMPLEX_FMS_CONJ;
if (!vect_pattern_validate_optab (ifn, *ref_node))
return IFN_LAST;
--
diff --git a/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c b/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a45ebdbec8c91dfae98f40f910c7a5d3f2ffbf5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c
@@ -0,0 +1,67 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros" } */
+/* { dg-require-effective-target vect_complex_add_double } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+extern void abort (void);
+
+#define N 16
+
+#define DEF(NAME, TYPE, EXPR) \
+ __attribute__((noipa)) void \
+ NAME (_Complex TYPE *__restrict d, _Complex TYPE *__restrict c,\
+ _Complex TYPE *__restrict a, _Complex TYPE *__restrict b,\
+ int n) \
+ { \
+ for (int i = 0; i < n; i++) \
+ d[i] = EXPR; \
+ } \
+ \
+ __attribute__((noipa, optimize ("no-tree-vectorize"))) void \
+ NAME##_ref (_Complex TYPE *__restrict d, _Complex TYPE *__restrict c,\
+ _Complex TYPE *__restrict a, _Complex TYPE *__restrict b,\
+ int n) \
+ { \
+ for (int i = 0; i < n; i++) \
+ d[i] = EXPR; \
+ }
+
+DEF (fms_f, float, c[i] - a[i] * b[i])
+DEF (fms_d, double, c[i] - a[i] * b[i])
+DEF (fmsconj_f, float, c[i] - a[i] * ~b[i])
+DEF (mul_f, float, a[i] * b[i])
+
+#define CHECK(NAME, TYPE) \
+ do { \
+ _Complex TYPE a[N], b[N], c[N], d[N], ref[N]; \
+ for (int i = 0; i < N; i++) \
+ { \
+ __real__ a[i] = i + 1; \
+ __imag__ a[i] = 2 * i + 3; \
+ __real__ b[i] = 3 * i - 1; \
+ __imag__ b[i] = i + 5; \
+ __real__ c[i] = 100 + i; \
+ __imag__ c[i] = 200 - i; \
+ } \
+ NAME (d, c, a, b, N); \
+ NAME##_ref (ref, c, a, b, N); \
+ _Pragma("novect") \
+ for (int i = 0; i < N; i++) \
+ if (__real__ d[i] != __real__ ref[i] \
+ || __imag__ d[i] != __imag__ ref[i]) \
+ abort (); \
+ } while (0)
+
+int
+main (void)
+{
+ CHECK (fms_f, float);
+ CHECK (fms_d, double);
+ CHECK (fmsconj_f, float);
+ CHECK (mul_f, float);
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect" } } */
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_MUL \\(" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c
new file mode 100644
index 0000000000000000000000000000000000000000..3062878edf6e7dea5b0b51be132d5506b274cb14
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-manual-invalid-float.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros" } */
+/* { dg-require-effective-target vect_complex_add_float } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+manual_invalid_fms (float *__restrict d, float *__restrict c,
+ float *__restrict a, float *__restrict b)
+{
+ for (int r = 0; r < 100; r += 2)
+ {
+ int i = r + 1;
+ float cr = c[r];
+ float ci = c[i];
+ float ar = a[r];
+ float ai = a[i];
+ float br = b[r];
+ float bi = b[i];
+ d[r] = cr + ar * br - ai * bi;
+ d[i] = ci - (ar * bi + ai * br);
+ }
+}
+
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS pattern" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
+/* { dg-final { scan-tree-dump-not "add new stmt: \[^\n\r]*COMPLEX_FMS" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c
new file mode 100644
index 0000000000000000000000000000000000000000..3253b3b2d344f450daa96a38f0134328cad6ea92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-double.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros -fdump-tree-vect-details" } */
+/* { dg-require-effective-target vect_complex_add_double } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+fms_out (_Complex double *__restrict d, _Complex double *__restrict c,
+ _Complex double *__restrict a, _Complex double *__restrict b,
+ int n)
+{
+ for (int i = 0; i < n; ++i)
+ d[i] = c[i] - a[i] * b[i];
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c
new file mode 100644
index 0000000000000000000000000000000000000000..51e6b0d8762842ab006115c7299305857fb3a362
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/complex/fast-math-complex-mls-outofplace-float.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O3 -fcx-limited-range -fno-signed-zeros -fdump-tree-vect-details" } */
+/* { dg-require-effective-target vect_complex_add_float } */
+/* { dg-add-options arm_v8_3a_complex_neon } */
+
+void
+fms_out (_Complex float *__restrict d, _Complex float *__restrict c,
+ _Complex float *__restrict a, _Complex float *__restrict b,
+ int n)
+{
+ for (int i = 0; i < n; ++i)
+ d[i] = c[i] - a[i] * b[i];
+}
+
+/* { dg-final { scan-tree-dump "add new stmt: \[^\n\r]*COMPLEX_FMS \\(" "vect" { xfail arm*-*-* } } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_FMS_CONJ" "vect" } } */
+/* { dg-final { scan-tree-dump-not "Found COMPLEX_MUL" "vect" } } */
diff --git a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90 b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
index b06950d1baae7d8aca2e1c96c8e6e942781b71bd..d62330d4a7760235bf25179b072d986bbe87c653 100644
--- a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
+++ b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
@@ -57,10 +57,10 @@ subroutine c_sub_a_conjb(n, a, c, b) ! C -= A * conj(B)
end do
end subroutine c_sub_a_conjb
-! The two plain multiplies form .COMPLEX_MUL (#0 + #90) and the two conjugate
-! multiplies form .COMPLEX_MUL_CONJ (#0 + #270). PR122408 is about detecting
-! the conjugate form, so the #270 and #180 counts are the ones that guard it.
-! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #0} 4 } }
+! The accumulations form .COMPLEX_FMA (#0 + #90) and .COMPLEX_FMS (#180 +
+! #270), with the conjugate forms swapping the #90 and #270 rotations. PR122408
+! is about detecting the conjugate form, so the #90 and #270 counts guard it.
+! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #0} 2 } }
! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #90} 2 } }
! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #270} 2 } }
-! { dg-final { scan-assembler-not {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #180} } }
+! { dg-final { scan-assembler-times {fcmla\s+v[0-9]+.2d, v[0-9]+.2d, v[0-9]+.2d, #180} 2 } }
diff --git a/gcc/tree-vect-slp-patterns.cc b/gcc/tree-vect-slp-patterns.cc
index f77993100ad19ce5502900022a68b39201d24a54..aae000ee5292ccb1521928e43f514a14386a7827 100644
--- a/gcc/tree-vect-slp-patterns.cc
+++ b/gcc/tree-vect-slp-patterns.cc
@@ -1305,7 +1305,94 @@ complex_fms_pattern::matches (complex_operation_t op,
slp_compat_nodes_map_t *compat_cache,
slp_tree * ref_node, vec<slp_tree> *ops)
{
- internal_fn ifn = IFN_LAST;
+ /* It's only valid to form FMSs with -ffp-contract=fast. */
+ if (!SLP_TREE_VECTYPE (*ref_node)
+ || (flag_fp_contract_mode != FP_CONTRACT_FAST
+ && FLOAT_TYPE_P (SLP_TREE_VECTYPE (*ref_node))))
+ return IFN_LAST;
+
+ /* Match c - a * b when SLP has built the result as:
+
+ c.real + (a.imag * b.imag - a.real * b.real)
+ c.imag - (a.real * b.imag + a.imag * b.real)
+
+ This represents the same operation as the existing FMS matcher below,
+ but with the accumulator outside the complex product node. */
+ if (op == PLUS_MINUS)
+ {
+ auto plus_ops = SLP_TREE_CHILDREN ((*ops)[0]);
+ auto minus_ops = SLP_TREE_CHILDREN ((*ops)[1]);
+ if (plus_ops.length () != 2 || minus_ops.length () != 2)
+ return IFN_LAST;
+
+ slp_tree acc = minus_ops[0];
+ slp_tree prod = minus_ops[1];
+ if (!((plus_ops[0] == acc && plus_ops[1] == prod)
+ || (plus_ops[1] == acc && plus_ops[0] == prod)))
+ return IFN_LAST;
+ if (linear_loads_p (perm_cache, acc) != PERM_EVENODD)
+ return IFN_LAST;
+
+ auto_vec<slp_tree> prod_ops;
+ if (vect_detect_pair_op (prod, true, &prod_ops) != MINUS_PLUS)
+ return IFN_LAST;
+ if (prod_ops.length () != 2)
+ return IFN_LAST;
+
+ auto prod_left = SLP_TREE_CHILDREN (prod_ops[0]);
+ auto prod_right = SLP_TREE_CHILDREN (prod_ops[1]);
+ if (prod_left.length () != 2
+ || prod_right.length () != 2
+ || !vect_match_expression_p (prod_left[0], MULT_EXPR)
+ || !vect_match_expression_p (prod_left[1], MULT_EXPR)
+ || !vect_match_expression_p (prod_right[0], MULT_EXPR)
+ || !vect_match_expression_p (prod_right[1], MULT_EXPR))
+ return IFN_LAST;
+
+ auto_vec<slp_tree> left_op, right_op;
+ left_op.safe_splice (SLP_TREE_CHILDREN (prod_left[0]));
+ right_op.safe_splice (SLP_TREE_CHILDREN (prod_left[1]));
+
+ enum _conj_status status;
+ auto_vec<slp_tree> res_ops;
+ if (!vect_validate_multiplication_commutative (perm_cache, compat_cache,
+ right_op, left_op, true,
+ res_ops, &status))
+ return IFN_LAST;
+
+ internal_fn ifn = status == CONJ_NONE ? IFN_COMPLEX_FMS
+ : IFN_COMPLEX_FMS_CONJ;
+ if (!vect_pattern_validate_optab (ifn, *ref_node))
+ return IFN_LAST;
+
+ ops->truncate (0);
+ ops->create (4);
+
+ complex_perm_kinds_t kind = linear_loads_p (perm_cache, res_ops[0]);
+ if (kind == PERM_EVENODD || kind == PERM_TOP)
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[3]);
+ }
+ else if (kind == PERM_EVENEVEN && status != CONJ_SND)
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[2]);
+ }
+ else
+ {
+ ops->quick_push (acc);
+ ops->quick_push (res_ops[1]);
+ ops->quick_push (res_ops[0]);
+ ops->quick_push (res_ops[3]);
+ }
+
+ return ifn;
+ }
/* We need to ignore the two_operands nodes that may also match,
for that we can check if they have any scalar statements and also
@@ -1318,11 +1405,6 @@ complex_fms_pattern::matches (complex_operation_t op,
if (!vect_match_expression_p (root, MINUS_EXPR))
return IFN_LAST;
- /* It's only valid to form FMSs with -ffp-contract=fast. */
- if (flag_fp_contract_mode != FP_CONTRACT_FAST
- && FLOAT_TYPE_P (SLP_TREE_VECTYPE (*ref_node)))
- return IFN_LAST;
-
/* TODO: Support invariants here, with the new layout CADD now
can match before we get a chance to try CFMS. */
auto nodes = SLP_TREE_CHILDREN (root);
@@ -1352,11 +1434,8 @@ complex_fms_pattern::matches (complex_operation_t op,
res_ops, &status))
return IFN_LAST;
- if (status == CONJ_NONE)
- ifn = IFN_COMPLEX_FMS;
- else
- ifn = IFN_COMPLEX_FMS_CONJ;
-
+ internal_fn ifn = status == CONJ_NONE ? IFN_COMPLEX_FMS
+ : IFN_COMPLEX_FMS_CONJ;
if (!vect_pattern_validate_optab (ifn, *ref_node))
return IFN_LAST;