Hi Kyrill,
> -----Original Message-----
> From: [email protected] <[email protected]>
> Sent: 06 October 2026 15:02
> To: [email protected]
> Cc: Kyrylo Tkachov <[email protected]>; Tamar Christina
> <[email protected]>; Wilco Dijkstra <[email protected]>
> Subject: [PATCH] aarch64: Use standard RTL for FADDP
>
> 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;
> }
Minor nit, I think I preferred the old approach of nunit /2 for the 128 bits
case
better.
Patch is OK with that change.
Thanks,
Tamar
> )
> 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)