From: Kyrylo Tkachov <[email protected]>
The FADDP patterns use UNSPEC_FADDV, so RTL optimizations cannot see that
FADDP adds pairs of elements. ADDP already uses standard RTL. Describe
FADDP in the same way and let it share the ADDP patterns, which get
parameterized names.
For two-lane vectors, FADDP and the scalar FADDP reduction add the two
elements of each input. RTL simplification describes the low element of
a vector as a lowpart subreg, so use that form for these sums:
(plus:SF (vec_select:SF (reg:V2SF x) (parallel [(const_int 1)]))
(subreg:SF (reg:V2SF x) 0))
The insn condition checks that the subreg is the lowpart of the vector
operand. The instruction only reads the vector operand.
Combine can now use FADDP for the sum of the two elements of a vector, and
pack two such sums into one FADDP. For example, with -O2:
float32x2_t
f (float32x2_t a, float32x2_t b)
{
return (float32x2_t) { vaddv_f32 (a), vaddv_f32 (b) };
}
Before:
faddp s1, v1.2s
faddp s0, v0.2s
zip1 v0.2s, v0.2s, v1.2s
After:
faddp v0.2s, v0.2s, v1.2s
For x[0] + x[1], where x is a float32x2_t:
Before:
dup s31, v0.s[1]
fadd s0, s31, s0
After:
faddp s0, v0.2s
Tests check lane extraction from FADDP results for both byte orders. The
runtime test also passes under big-endian QEMU.
Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill
Cc: Tamar Christina <[email protected]>
Cc: Wilco Dijkstra <[email protected]>
gcc/ChangeLog:
* config/aarch64/aarch64-protos.h (aarch64_simd_lowpart_of_p):
Declare.
* config/aarch64/aarch64.cc (aarch64_simd_lowpart_of_p): New
function.
* config/aarch64/iterators.md (VD_ADDP, VQ_ADDP, VDQ_ADDP): New mode
iterators.
(addp_type): New mode attribute.
* config/aarch64/aarch64-simd.md (aarch64_faddp<mode><vczle><vczbe>):
Remove.
(reduc_plus_scal_<mode>): Use standard RTL for V2F.
(aarch64_faddp_scal<mode>): New pattern.
(aarch64_addp<mode>_insn): Rename to...
(@aarch64_addp<mode>_concat): ...this. Handle FADDP.
(aarch64_addp<mode><vczle><vczbe>_insn): Rename to...
(@aarch64_addp<mode>_select<vczle><vczbe>): ...this. Handle FADDP.
(@aarch64_faddp_scal_pair<mode><vczle><vczbe>): New pattern.
(*aarch64_addp_same_reg<mode>): Handle FADDP.
(aarch64_addp<mode>): Rename to...
(aarch64_<f>addp<mode>): ...this. Handle FADDP.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/faddp-concat-1.c: New test.
* gcc.target/aarch64/faddp-concat-3.c: Likewise.
* gcc.target/aarch64/faddp-concat-be.c: Likewise.
* gcc.target/aarch64/simd/faddp_lane_1.c: Likewise.
* gcc.target/aarch64/simd/faddp_lane_2.c: Likewise.
* gcc.target/aarch64/simd/faddp_reinterpret_1.c: Likewise.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/config/aarch64/aarch64-protos.h | 1 +
gcc/config/aarch64/aarch64-simd.md | 183 ++++++++++++------
gcc/config/aarch64/aarch64.cc | 9 +
gcc/config/aarch64/iterators.md | 20 ++
.../gcc.target/aarch64/faddp-concat-1.c | 64 ++++++
.../gcc.target/aarch64/faddp-concat-3.c | 25 +++
.../gcc.target/aarch64/faddp-concat-be.c | 51 +++++
.../gcc.target/aarch64/simd/faddp_lane_1.c | 76 ++++++++
.../gcc.target/aarch64/simd/faddp_lane_2.c | 53 +++++
.../aarch64/simd/faddp_reinterpret_1.c | 51 +++++
10 files changed, 471 insertions(+), 62 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/aarch64/faddp-concat-1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/faddp-concat-3.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/faddp-concat-be.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_2.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/simd/faddp_reinterpret_1.c
diff --git a/gcc/config/aarch64/aarch64-protos.h
b/gcc/config/aarch64/aarch64-protos.h
index e8ae3d42794..b825ff5b584 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -963,6 +963,7 @@ bool aarch64_regno_ok_for_index_p (int, bool);
bool aarch64_reinterpret_float_as_int (rtx value, unsigned HOST_WIDE_INT
*fail);
bool aarch64_simd_check_vect_par_cnst_half (rtx op, machine_mode mode,
bool high);
+bool aarch64_simd_lowpart_of_p (rtx, rtx);
bool aarch64_parallel_select_half_p (machine_mode, rtx);
bool aarch64_pars_overlap_p (rtx, rtx);
bool aarch64_simd_scalar_immediate_valid_for_move (rtx, scalar_int_mode);
diff --git a/gcc/config/aarch64/aarch64-simd.md
b/gcc/config/aarch64/aarch64-simd.md
index f928c5958cd..e1ce90bfafb 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -3760,16 +3760,6 @@
;; 'across lanes' add.
-(define_insn "aarch64_faddp<mode><vczle><vczbe>"
- [(set (match_operand:VHSDF 0 "register_operand" "=w")
- (unspec:VHSDF [(match_operand:VHSDF 1 "register_operand" "w")
- (match_operand:VHSDF 2 "register_operand" "w")]
- UNSPEC_FADDV))]
- "TARGET_SIMD"
- "faddp\t%0.<Vtype>, %1.<Vtype>, %2.<Vtype>"
- [(set_attr "type" "neon_fp_reduc_add_<stype><q>")]
-)
-
(define_insn "reduc_plus_scal_<mode>"
[(set (match_operand:<VEL> 0 "register_operand" "=w")
(unspec:<VEL> [(match_operand:VDQV 1 "register_operand" "w")]
@@ -3799,12 +3789,31 @@
[(set_attr "type" "neon_reduc_add<VDQV_E:q>")]
)
-(define_insn "reduc_plus_scal_<mode>"
- [(set (match_operand:<VEL> 0 "register_operand" "=w")
- (unspec:<VEL> [(match_operand:V2F 1 "register_operand" "w")]
- UNSPEC_FADDV))]
- "TARGET_SIMD"
- "faddp\\t%<Vetype>0, %1.<Vtype>"
+(define_expand "reduc_plus_scal_<mode>"
+ [(match_operand:<VEL> 0 "register_operand")
+ (match_operand:V2F 1 "register_operand")]
+ "TARGET_SIMD"
+ {
+ rtx hi = aarch64_gen_stepped_int_parallel (1, ENDIAN_LANE_N (2, 1), 1);
+ rtx x = force_reg (<MODE>mode, operands[1]);
+ emit_insn (gen_aarch64_faddp_scal<mode> (operands[0], x, hi,
+ gen_lowpart (<VEL>mode, x)));
+ DONE;
+ }
+)
+
+;; The sum of the two elements of a vector. RTL simplification describes
+;; the low element as a lowpart of the vector, so operand 3 is that lowpart
+;; of operand 1.
+(define_insn "aarch64_faddp_scal<mode>"
+ [(set (match_operand:<VEL> 0 "register_operand" "=w")
+ (plus:<VEL>
+ (vec_select:<VEL>
+ (match_operand:V2F 1 "register_operand" "w")
+ (match_operand:V2F 2 "vect_par_cnst_hi_half"))
+ (match_operand:<VEL> 3 "register_operand" "w")))]
+ "TARGET_SIMD && aarch64_simd_lowpart_of_p (operands[3], operands[1])"
+ "faddp\t%<Vetype>0, %1.<Vtype>"
[(set_attr "type" "neon_fp_reduc_add_<Vetype><q>")]
)
@@ -8304,27 +8313,27 @@
[(set_attr "type" "neon_fp_compare_<stype><q>")]
)
-;; addp
+;; addp and faddp
-;; ADDP with two registers semantically concatenates them and performs
-;; a pairwise addition on the result. For 128-bit input modes represent this
-;; as a concatenation of the pairwise addition results of the two input
-;; registers. This allow us to avoid using intermediate 256-bit modes.
-;; The RTL uses GCC's lane numbering, so on big-endian targets operand 1
-;; provides the architecturally high half of the result.
-(define_insn "aarch64_addp<mode>_insn"
- [(set (match_operand:VQ_I 0 "register_operand" "=w")
- (vec_concat:VQ_I
+;; ADDP and FADDP with two registers semantically concatenate them and
+;; perform a pairwise addition on the result. For 128-bit input modes
+;; represent this as a concatenation of the pairwise addition results of the
+;; two input registers. This allows us to avoid using intermediate 256-bit
+;; modes. The RTL uses GCC's lane numbering, so on big-endian targets
+;; operand 1 provides the architecturally high half of the result.
+(define_insn "@aarch64_addp<mode>_concat"
+ [(set (match_operand:VQ_ADDP 0 "register_operand" "=w")
+ (vec_concat:VQ_ADDP
(plus:<VHALF>
(vec_select:<VHALF>
- (match_operand:VQ_I 1 "register_operand" "w")
- (match_operand:VQ_I 3 "vect_par_cnst_even_or_odd_half"))
+ (match_operand:VQ_ADDP 1 "register_operand" "w")
+ (match_operand:VQ_ADDP 3 "vect_par_cnst_even_or_odd_half"))
(vec_select:<VHALF>
(match_dup 1)
- (match_operand:VQ_I 4 "vect_par_cnst_even_or_odd_half")))
+ (match_operand:VQ_ADDP 4 "vect_par_cnst_even_or_odd_half")))
(plus:<VHALF>
(vec_select:<VHALF>
- (match_operand:VQ_I 2 "register_operand" "w")
+ (match_operand:VQ_ADDP 2 "register_operand" "w")
(match_dup 3))
(vec_select:<VHALF>
(match_dup 2)
@@ -8332,26 +8341,27 @@
"TARGET_SIMD && !rtx_equal_p (operands[3], operands[4])"
{
if (BYTES_BIG_ENDIAN)
- return "addp\t%<v>0<Vmtype>, %<v>2<Vmtype>, %<v>1<Vmtype>";
- return "addp\t%<v>0<Vmtype>, %<v>1<Vmtype>, %<v>2<Vmtype>";
+ return "<f>addp\t%<v>0<Vmtype>, %<v>2<Vmtype>, %<v>1<Vmtype>";
+ return "<f>addp\t%<v>0<Vmtype>, %<v>1<Vmtype>, %<v>2<Vmtype>";
}
- [(set_attr "type" "neon_reduc_add<q>")]
+ [(set_attr "type" "<addp_type>")]
)
-;; For 64-bit input modes an ADDP is represented as a concatenation
+;; For 64-bit input modes an ADDP or FADDP is represented as a concatenation
;; of the input registers into an 128-bit register which is then fed
;; into a pairwise add. That way we avoid having to create intermediate
-;; 32-bit vector modes. As above, operand 1 provides the architecturally
-;; high half of the result on big-endian targets.
-(define_insn "aarch64_addp<mode><vczle><vczbe>_insn"
- [(set (match_operand:VD_BHSI 0 "register_operand" "=w")
- (plus:VD_BHSI
- (vec_select:VD_BHSI
+;; 32-bit vector modes. vpadd_f32 uses @aarch64_faddp_scal_pair<mode>
+;; instead. As above, operand 1 provides the architecturally high half of
+;; the result on big-endian targets.
+(define_insn "@aarch64_addp<mode>_select<vczle><vczbe>"
+ [(set (match_operand:VD_ADDP 0 "register_operand" "=w")
+ (plus:VD_ADDP
+ (vec_select:VD_ADDP
(vec_concat:<VDBL>
- (match_operand:VD_BHSI 1 "register_operand" "w")
- (match_operand:VD_BHSI 2 "register_operand" "w"))
+ (match_operand:VD_ADDP 1 "register_operand" "w")
+ (match_operand:VD_ADDP 2 "register_operand" "w"))
(match_operand:<VDBL> 3 "vect_par_cnst_even_or_odd_half"))
- (vec_select:VD_BHSI
+ (vec_select:VD_ADDP
(vec_concat:<VDBL>
(match_dup 1)
(match_dup 2))
@@ -8359,10 +8369,39 @@
"TARGET_SIMD && !rtx_equal_p (operands[3], operands[4])"
{
if (BYTES_BIG_ENDIAN)
- return "addp\t%<v>0<Vmtype>, %<v>2<Vmtype>, %<v>1<Vmtype>";
- return "addp\t%<v>0<Vmtype>, %<v>1<Vmtype>, %<v>2<Vmtype>";
+ return "<f>addp\t%<v>0<Vmtype>, %<v>2<Vmtype>, %<v>1<Vmtype>";
+ return "<f>addp\t%<v>0<Vmtype>, %<v>1<Vmtype>, %<v>2<Vmtype>";
+ }
+ [(set_attr "type" "<addp_type>")]
+)
+
+;; FADDP of two-lane vectors, represented as the concatenation of the sums of
+;; the two elements of each input. RTL simplification describes the low
+;; element of a vector as a lowpart of the vector, so operands 4 and 5 are
+;; those lowparts of operands 1 and 2. As above, operand 1 provides the
+;; architecturally high half of the result on big-endian targets.
+(define_insn "@aarch64_faddp_scal_pair<mode><vczle><vczbe>"
+ [(set (match_operand:V2F 0 "register_operand" "=w")
+ (vec_concat:V2F
+ (plus:<VEL>
+ (vec_select:<VEL>
+ (match_operand:V2F 1 "register_operand" "w")
+ (match_operand:V2F 3 "vect_par_cnst_hi_half"))
+ (match_operand:<VEL> 4 "register_operand" "w"))
+ (plus:<VEL>
+ (vec_select:<VEL>
+ (match_operand:V2F 2 "register_operand" "w")
+ (match_dup 3))
+ (match_operand:<VEL> 5 "register_operand" "w"))))]
+ "TARGET_SIMD
+ && aarch64_simd_lowpart_of_p (operands[4], operands[1])
+ && aarch64_simd_lowpart_of_p (operands[5], operands[2])"
+ {
+ if (BYTES_BIG_ENDIAN)
+ return "faddp\t%0.<Vtype>, %2.<Vtype>, %1.<Vtype>";
+ return "faddp\t%0.<Vtype>, %1.<Vtype>, %2.<Vtype>";
}
- [(set_attr "type" "neon_reduc_add<q>")]
+ [(set_attr "type" "<addp_type>")]
)
;; A common usecase of 64-bit ADDP is to have both operands come from the same
@@ -8372,11 +8411,11 @@
[(set (match_operand:<VHALF> 0 "register_operand" "=w")
(plus:<VHALF>
(vec_select:<VHALF>
- (match_operand:VQ_I 1 "register_operand" "w")
- (match_operand:VQ_I 2 "vect_par_cnst_even_or_odd_half"))
+ (match_operand:VQ_ADDP 1 "register_operand" "w")
+ (match_operand:VQ_ADDP 2 "vect_par_cnst_even_or_odd_half"))
(vec_select:<VHALF>
(match_dup 1)
- (match_operand:VQ_I 3 "vect_par_cnst_even_or_odd_half"))))]
+ (match_operand:VQ_ADDP 3 "vect_par_cnst_even_or_odd_half"))))]
"TARGET_SIMD && !rtx_equal_p (operands[2], operands[3])"
"#"
"&& 1"
@@ -8388,29 +8427,49 @@
else
scratch = lowpart_subreg (<MODE>mode, operands[0], <VHALF>mode);
- emit_insn (gen_aarch64_addp<mode>_insn (scratch, operands[1], operands[1],
- operands[2], operands[3]));
+ emit_insn (gen_aarch64_addp_concat (<MODE>mode, scratch, operands[1],
+ operands[1], operands[2],
+ operands[3]));
emit_move_insn (operands[0], gen_lowpart (<VHALF>mode, scratch));
DONE;
}
)
-(define_expand "aarch64_addp<mode>"
- [(match_operand:VDQ_I 0 "register_operand")
- (match_operand:VDQ_I 1 "register_operand")
- (match_operand:VDQ_I 2 "register_operand")]
+(define_expand "aarch64_<f>addp<mode>"
+ [(match_operand:VDQ_ADDP 0 "register_operand")
+ (match_operand:VDQ_ADDP 1 "register_operand")
+ (match_operand:VDQ_ADDP 2 "register_operand")]
"TARGET_SIMD"
{
- int nunits = GET_MODE_NUNITS (<MODE>mode).to_constant ();
- if (known_eq (GET_MODE_BITSIZE (<MODE>mode), 128))
- nunits /= 2;
- rtx par_even = aarch64_gen_stepped_int_parallel (nunits, 0, 2);
- rtx par_odd = aarch64_gen_stepped_int_parallel (nunits, 1, 2);
/* The intrinsic uses architectural lane numbering. */
if (BYTES_BIG_ENDIAN)
std::swap (operands[1], operands[2]);
- emit_insn (gen_aarch64_addp<mode>_insn (operands[0], operands[1],
- operands[2], par_even, par_odd));
+ int nunits = GET_MODE_NUNITS (<MODE>mode).to_constant ();
+ if (nunits == 2 && FLOAT_MODE_P (<MODE>mode))
+ {
+ rtx hi = aarch64_gen_stepped_int_parallel (1, ENDIAN_LANE_N (2, 1), 1);
+ rtx x = force_reg (<MODE>mode, operands[1]);
+ rtx y = force_reg (<MODE>mode, operands[2]);
+ emit_insn (gen_aarch64_faddp_scal_pair (<MODE>mode, operands[0], x, y,
+ hi, gen_lowpart (<VEL>mode, x),
+ gen_lowpart (<VEL>mode, y)));
+ }
+ else if (known_eq (GET_MODE_BITSIZE (<MODE>mode), 128))
+ {
+ rtx even = aarch64_gen_stepped_int_parallel (nunits / 2, 0, 2);
+ rtx odd = aarch64_gen_stepped_int_parallel (nunits / 2, 1, 2);
+ emit_insn (gen_aarch64_addp_concat (<MODE>mode, operands[0],
+ operands[1], operands[2], even,
+ odd));
+ }
+ else
+ {
+ rtx even = aarch64_gen_stepped_int_parallel (nunits, 0, 2);
+ rtx odd = aarch64_gen_stepped_int_parallel (nunits, 1, 2);
+ emit_insn (gen_aarch64_addp_select (<MODE>mode, operands[0],
+ operands[1], operands[2], even,
+ odd));
+ }
DONE;
}
)
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 6693cec98bd..d7b5775a66d 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -25552,6 +25552,15 @@ aarch64_simd_check_vect_par_cnst_half (rtx op,
machine_mode mode,
return true;
}
+/* Return true if X is the low element of vector V, in the lowpart form
+ that RTL simplification uses for it. */
+
+bool
+aarch64_simd_lowpart_of_p (rtx x, rtx v)
+{
+ return rtx_equal_p (x, lowpart_subreg (GET_MODE (x), v, GET_MODE (v)));
+}
+
/* Return a PARALLEL containing NELTS elements, with element I equal
to BASE + I * STEP. */
diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md
index 568786e3714..66db3f4a5fd 100644
--- a/gcc/config/aarch64/iterators.md
+++ b/gcc/config/aarch64/iterators.md
@@ -238,6 +238,15 @@
;; Advanced SIMD Float modes with 2 elements.
(define_mode_iterator V2F [V2SF V2DF])
+;; 64-bit modes of ADDP and FADDP.
+(define_mode_iterator VD_ADDP [VD_BHSI (V4HF "TARGET_SIMD_F16INST") V2SF])
+
+;; 128-bit modes of ADDP and FADDP.
+(define_mode_iterator VQ_ADDP [VQ_I (V8HF "TARGET_SIMD_F16INST") V4SF V2DF])
+
+;; Modes of ADDP and FADDP.
+(define_mode_iterator VDQ_ADDP [VDQ_I VHSDF])
+
;; All Advanced SIMD modes on which we support any arithmetic operations.
(define_mode_iterator VALL [V8QI V16QI V4HI V8HI V2SI V4SI V2DI V2SF V4SF
V2DF])
@@ -2715,6 +2724,17 @@
(V2SF "f") (V4SF "f")
(V2DF "f") (DF "f")])
+;; The type of ADDP and FADDP.
+(define_mode_attr addp_type [(V8QI "neon_reduc_add") (V16QI "neon_reduc_add_q")
+ (V4HI "neon_reduc_add") (V8HI "neon_reduc_add_q")
+ (V2SI "neon_reduc_add") (V4SI "neon_reduc_add_q")
+ (V2DI "neon_reduc_add_q")
+ (V4HF "neon_fp_reduc_add_s")
+ (V8HF "neon_fp_reduc_add_s_q")
+ (V2SF "neon_fp_reduc_add_s")
+ (V4SF "neon_fp_reduc_add_s_q")
+ (V2DF "neon_fp_reduc_add_d_q")])
+
;; Defined to '_fp' for types whose element type is a float type.
(define_mode_attr fp [(V8QI "") (V16QI "")
(V4HI "") (V8HI "")
diff --git a/gcc/testsuite/gcc.target/aarch64/faddp-concat-1.c
b/gcc/testsuite/gcc.target/aarch64/faddp-concat-1.c
new file mode 100644
index 00000000000..8b5f69cd6e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/faddp-concat-1.c
@@ -0,0 +1,64 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" } } */
+/* { dg-options "-O2" } */
+
+#include <arm_neon.h>
+
+/*
+** pair_f32: { target aarch64_little_endian }
+** faddp\tv0\.2s, v0\.2s, v1\.2s
+** ret
+*/
+/*
+** pair_f32: { target aarch64_big_endian }
+** faddp\tv0\.2s, v1\.2s, v0\.2s
+** ret
+*/
+__attribute__ ((noipa)) float32x2_t
+pair_f32 (float32x2_t a, float32x2_t b)
+{
+ return (float32x2_t) { vaddv_f32 (a), vaddv_f32 (b) };
+}
+
+/*
+** pair_f64: { target aarch64_little_endian }
+** faddp\tv0\.2d, v0\.2d, v1\.2d
+** ret
+*/
+/*
+** pair_f64: { target aarch64_big_endian }
+** faddp\tv0\.2d, v1\.2d, v0\.2d
+** ret
+*/
+__attribute__ ((noipa)) float64x2_t
+pair_f64 (float64x2_t a, float64x2_t b)
+{
+ return (float64x2_t) { vaddvq_f64 (a), vaddvq_f64 (b) };
+}
+
+/*
+** pair_mixed: { target aarch64_little_endian }
+** faddp\tv0\.2s, v0\.2s, v1\.2s
+** ret
+*/
+/*
+** pair_mixed: { target aarch64_big_endian }
+** faddp\tv0\.2s, v1\.2s, v0\.2s
+** ret
+*/
+__attribute__ ((noipa)) float32x2_t
+pair_mixed (float32x2_t a, float32x2_t b)
+{
+ return (float32x2_t) { vaddv_f32 (a), b[0] + b[1] };
+}
+
+/*
+** sum_lanes:
+** faddp\ts0, v0\.2s
+** ret
+*/
+__attribute__ ((noipa)) float32_t
+sum_lanes (float32x2_t a)
+{
+ return a[0] + a[1];
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/faddp-concat-3.c
b/gcc/testsuite/gcc.target/aarch64/faddp-concat-3.c
new file mode 100644
index 00000000000..3fcaa9f0935
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/faddp-concat-3.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" } } */
+/* { dg-options "-O2" } */
+
+#include <arm_neon.h>
+
+/*
+** shared_reductions:
+** faddp\ts[0-9]+, v[0-9]+\.2s
+** faddp\ts[0-9]+, v[0-9]+\.2s
+** (?:str|zip1|ins)\t[^\n]+
+** (?:str|zip1|ins)\t[^\n]+
+** (?:str|zip1|ins)\t[^\n]+
+** ret
+*/
+float32x2_t
+shared_reductions (float32x2_t a, float32x2_t b,
+ volatile float *p, volatile float *q)
+{
+ float x = vaddv_f32 (a);
+ float y = vaddv_f32 (b);
+ *p = x;
+ *q = y;
+ return (float32x2_t) { x, y };
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/faddp-concat-be.c
b/gcc/testsuite/gcc.target/aarch64/faddp-concat-be.c
new file mode 100644
index 00000000000..84b7a08fbd1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/faddp-concat-be.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target aarch64_mbig_endian } */
+/* Avoid a dependence on big-endian C library headers. */
+/* { dg-options "-O2 -mbig-endian -ffreestanding" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_neon.h>
+
+/*
+** pair_f32:
+** faddp\tv0\.2s, v1\.2s, v0\.2s
+** ret
+*/
+float32x2_t
+pair_f32 (float32x2_t a, float32x2_t b)
+{
+ return (float32x2_t) { vaddv_f32 (a), vaddv_f32 (b) };
+}
+
+/*
+** pair_f64:
+** faddp\tv0\.2d, v1\.2d, v0\.2d
+** ret
+*/
+float64x2_t
+pair_f64 (float64x2_t a, float64x2_t b)
+{
+ return (float64x2_t) { vaddvq_f64 (a), vaddvq_f64 (b) };
+}
+
+/*
+** pair_mixed:
+** faddp\tv0\.2s, v1\.2s, v0\.2s
+** ret
+*/
+float32x2_t
+pair_mixed (float32x2_t a, float32x2_t b)
+{
+ return (float32x2_t) { vaddv_f32 (a), b[0] + b[1] };
+}
+
+/*
+** sum_lanes:
+** faddp\ts0, v0\.2s
+** ret
+*/
+float32_t
+sum_lanes (float32x2_t a)
+{
+ return a[0] + a[1];
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_1.c
b/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_1.c
new file mode 100644
index 00000000000..5ae2231697f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_1.c
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include <arm_neon.h>
+
+__attribute__ ((noipa)) float32_t
+lane0_q (float32x4_t a, float32x4_t b)
+{
+ return vgetq_lane_f32 (vpaddq_f32 (a, b), 0);
+}
+
+__attribute__ ((noipa)) float32_t
+lane3_q (float32x4_t a, float32x4_t b)
+{
+ return vgetq_lane_f32 (vpaddq_f32 (a, b), 3);
+}
+
+__attribute__ ((noipa)) float32x2_t
+low_q (float32x4_t a, float32x4_t b)
+{
+ return vget_low_f32 (vpaddq_f32 (a, b));
+}
+
+__attribute__ ((noipa)) float32_t
+lane1_d (float32x2_t a, float32x2_t b)
+{
+ return vget_lane_f32 (vpadd_f32 (a, b), 1);
+}
+
+__attribute__ ((noipa)) float64_t
+lane0_q64 (float64x2_t a, float64x2_t b)
+{
+ return vgetq_lane_f64 (vpaddq_f64 (a, b), 0);
+}
+
+__attribute__ ((noipa)) float32x4_t
+const_q (void)
+{
+ float32_t a[] = { 1, 2, 4, 8 };
+ float32_t b[] = { 16, 32, 64, 128 };
+ return vpaddq_f32 (vld1q_f32 (a), vld1q_f32 (b));
+}
+
+__attribute__ ((noipa)) float32_t
+sum_d (float32x2_t a)
+{
+ return vaddv_f32 (a);
+}
+
+int
+main (void)
+{
+ float32_t a32[] = { 1, 2, 4, 8 };
+ float32_t b32[] = { 16, 32, 64, 128 };
+ float64_t a64[] = { 1, 2 };
+ float64_t b64[] = { 4, 8 };
+ float32x4_t a = vld1q_f32 (a32);
+ float32x4_t b = vld1q_f32 (b32);
+ float32_t res[4];
+
+ if (lane0_q (a, b) != 3 || lane3_q (a, b) != 192)
+ __builtin_abort ();
+ vst1_f32 (res, low_q (a, b));
+ if (res[0] != 3 || res[1] != 12)
+ __builtin_abort ();
+ if (lane1_d (vld1_f32 (a32), vld1_f32 (b32)) != 48)
+ __builtin_abort ();
+ if (lane0_q64 (vld1q_f64 (a64), vld1q_f64 (b64)) != 3)
+ __builtin_abort ();
+ vst1q_f32 (res, const_q ());
+ if (res[0] != 3 || res[1] != 12 || res[2] != 48 || res[3] != 192)
+ __builtin_abort ();
+ if (sum_d (vld1_f32 (b32)) != 48)
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_2.c
b/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_2.c
new file mode 100644
index 00000000000..1c29c94c2d0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd/faddp_lane_2.c
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target aarch64_mbig_endian } */
+/* Avoid a dependence on big-endian C library headers. */
+/* { dg-options "-O2 -mbig-endian -ffreestanding" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <arm_neon.h>
+
+/* The architectural lanes of the low half depend only on A. */
+
+/*
+** lane0_q:
+** faddp v0\.4s, v0\.4s, v0\.4s
+** ret
+*/
+float32_t
+lane0_q (float32x4_t a, float32x4_t b)
+{
+ return vgetq_lane_f32 (vpaddq_f32 (a, b), 0);
+}
+
+/*
+** low_q:
+** faddp v0\.4s, v0\.4s, v0\.4s
+** ret
+*/
+float32x2_t
+low_q (float32x4_t a, float32x4_t b)
+{
+ return vget_low_f32 (vpaddq_f32 (a, b));
+}
+
+/*
+** full_d:
+** faddp v0\.2s, v0\.2s, v1\.2s
+** ret
+*/
+float32x2_t
+full_d (float32x2_t a, float32x2_t b)
+{
+ return vpadd_f32 (a, b);
+}
+
+/*
+** full_q64:
+** faddp v0\.2d, v0\.2d, v1\.2d
+** ret
+*/
+float64x2_t
+full_q64 (float64x2_t a, float64x2_t b)
+{
+ return vpaddq_f64 (a, b);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/faddp_reinterpret_1.c
b/gcc/testsuite/gcc.target/aarch64/simd/faddp_reinterpret_1.c
new file mode 100644
index 00000000000..1422706591d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd/faddp_reinterpret_1.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <arm_neon.h>
+
+/* The FADDP inputs are subregs of vectors with a different element mode. */
+
+/*
+** sum_f32:
+** faddp s0, v0\.2s
+** ret
+*/
+float32_t
+sum_f32 (int32x2_t x)
+{
+ return vaddv_f32 (vreinterpret_f32_s32 (x));
+}
+
+/*
+** sum_f64:
+** faddp d0, v0\.2d
+** ret
+*/
+float64_t
+sum_f64 (int64x2_t x)
+{
+ return vaddvq_f64 (vreinterpretq_f64_s64 (x));
+}
+
+/*
+** pair_f32:
+** faddp v0\.2s, v0\.2s, v1\.2s
+** ret
+*/
+float32x2_t
+pair_f32 (int32x2_t x, int32x2_t y)
+{
+ return vpadd_f32 (vreinterpret_f32_s32 (x), vreinterpret_f32_s32 (y));
+}
+
+/*
+** pair_f64:
+** faddp v0\.2d, v0\.2d, v1\.2d
+** ret
+*/
+float64x2_t
+pair_f64 (int64x2_t x, int64x2_t y)
+{
+ return vpaddq_f64 (vreinterpretq_f64_s64 (x), vreinterpretq_f64_s64 (y));
+}
--
2.50.1 (Apple Git-155)