[gcc r17-3228] vect: Support another form of COMPLEX_FMS which the testcases point out [PR126589]
Tamar Christina via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:d9b294889b14069d6f6c5ce985f17bf8bbf34894 commit r17-3228-gd9b294889b14069d6f6c5ce985f17bf8bbf34894 Author: Tamar Christina <[email protected]> Date: Wed Aug 12 14:15:43 2026 +0100 vect: Support another form of COMPLEX_FMS which the testcases point out [PR126589] 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] gcc/ChangeLog: PR tree-optimization/126589 * tree-vect-slp-patterns.cc (complex_fms_pattern::matches): Check another form of FMS. gcc/testsuite/ChangeLog: PR tree-optimization/126589 * 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: --- gcc/testsuite/gcc.dg/vect/complex/complex-mul2.c | 67 ++++++++++++++ .../fast-math-complex-mls-manual-invalid-float.c | 27 ++++++ .../fast-math-complex-mls-outofplace-double.c | 17 ++++ .../fast-math-complex-mls-outofplace-float.c | 17 ++++ .../gfortran.target/aarch64/pr122408_1.f90 | 10 +- gcc/tree-vect-slp-patterns.cc | 101 ++++++++++++++++++--- 6 files changed, 223 insertions(+), 16 deletions(-) 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 000000000000..6a45ebdbec8c --- /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 000000000000..3062878edf6e --- /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 000000000000..3253b3b2d344 --- /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 000000000000..51e6b0d87628 --- /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 b06950d1baae..d62330d4a776 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 f77993100ad1..aae000ee5292 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;