vec_extract's only register alternative writes a core register, so a
single-precision lane comes back through the integer side:
float foo (float32x2_t v)
{ return vget_lane_f32 (v, 0) + vget_lane_f32 (v, 1); }
vmov.32 r3, d0[1]
vmov s15, r3
vadd.f32 s0, s15, s0
Such a lane is a whole S register, so extract it with a subreg move that the
register allocator can coalesce, and the example becomes
vadd.f32 s0, s1, s0
In the vec_extract expanders this covers the vget_lane_f32 and vgetq_lane_f32
intrinsics, the generic v[i] on Advanced SIMD and the generic v[i] on MVE.
The Advanced SIMD D-register extract had no expander, so its insn is renamed
the way the Q-register one already is. The MVE intrinsics expand straight to
mve_vec_extract in arm-mve-builtins-base.cc and are unchanged.
Big-endian keeps the old path: arm_can_change_mode_class refuses to change
the mode of a multi-word value in VFP_REGS there, so the subreg would only
force a spill. Integer lanes keep it too; their destination is a core
register, which vec_extract already reaches in one vmov.
This is the approach Jim Wilson proposed in the PR (attachment 35114),
restricted to the case where the lane really is a register; as he notes
there, a constraint alone still leaves the extra vmov.
An SF subreg can only name s0-s31, so a vector a lane is read from is
kept in d0-d15; with more than eight such vectors live at once the
allocator spills where the core-register path did not.
Assisted-by: Claude Opus 5 (Anthropic)
gcc/ChangeLog:
PR target/60408
* config/arm/neon.md (vec_extract<mode><V_elem_l>): New expander;
extract a single-precision lane with a subreg move.
(neon_vec_extract<mode><V_elem_l>): Rename from
vec_extract<mode><V_elem_l>.
* config/arm/vec-common.md (vec_extract<mode><V_elem_l>): Extract a
single-precision lane with a subreg move.
gcc/testsuite/ChangeLog:
PR target/60408
* gcc.target/arm/pr60408.c: New test.
* gcc.target/arm/pr60408-be.c: New test.
Signed-off-by: Dominic P <[email protected]>
---
Bootstrapped on x86_64-pc-linux-gnu; regression tested on arm-none-eabi
(arm.exp, simd.exp,
mve.exp, advsimd-intrinsics.exp) with no new failures.
Found by reading the vget_lane_f32 expansion in the PR, and measured at
-O1/-O2/-O3/-Os, -marm and -mthumb, -mfpu=neon. Reading all four lanes of a
float32x4_t and summing them is 11 -> 4 instructions, with the intrinsics and
with v[i] alike; a single lane is 3 -> 2. Integer lanes, __fp16 lanes,
vset_lane and big-endian output are byte-identical.
The one case that grows is -mfloat-abi=softfp -Os for a function taking the
vector by value: the vector arrives in a core-register pair, and the allocator
then materialises each lane with its own "vmov sN, rN" rather than moving the
whole vector once with "vmov d7, r0, r1" (4 -> 5 instructions). It is
confined to that ABI boundary -- with the vector loaded from memory or
computed, softfp -Os is 4 -> 4, 6 -> 6 and 12 -> 6 -- and over the same
examples softfp totals 216 -> 177 instructions at -Os and 230 -> 176 at -O2.
gcc/config/arm/neon.md | 19 ++++++-
gcc/config/arm/vec-common.md | 7 ++-
gcc/testsuite/gcc.target/arm/pr60408-be.c | 35 ++++++++++++
gcc/testsuite/gcc.target/arm/pr60408.c | 67 +++++++++++++++++++++++
4 files changed, 126 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/arm/pr60408-be.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr60408.c
diff --git a/gcc/config/arm/neon.md b/gcc/config/arm/neon.md
index 3c908db4538..004a2c3f8cc 100644
--- a/gcc/config/arm/neon.md
+++ b/gcc/config/arm/neon.md
@@ -393,7 +393,24 @@
[(set_attr "type" "neon_load1_all_lanes_q,neon_from_gp_q")]
)
-(define_insn "vec_extract<mode><V_elem_l>"
+(define_expand "vec_extract<mode><V_elem_l>"
+ [(match_operand:<V_elem> 0 "nonimmediate_operand")
+ (match_operand:VD_LANE 1 "s_register_operand")
+ (match_operand:SI 2 "immediate_operand")]
+ "TARGET_NEON"
+{
+ if (<V_elem>mode == SFmode && !BYTES_BIG_ENDIAN)
+ emit_move_insn (operands[0],
+ simplify_gen_subreg (SFmode, operands[1], <MODE>mode,
+ INTVAL (operands[2])
+ * GET_MODE_UNIT_SIZE (<MODE>mode)));
+ else
+ emit_insn (gen_neon_vec_extract<mode><V_elem_l> (operands[0], operands[1],
+ operands[2]));
+ DONE;
+})
+
+(define_insn "neon_vec_extract<mode><V_elem_l>"
[(set (match_operand:<V_elem> 0 "nonimmediate_operand" "=Um,r")
(vec_select:<V_elem>
(match_operand:VD_LANE 1 "s_register_operand" "w,w")
diff --git a/gcc/config/arm/vec-common.md b/gcc/config/arm/vec-common.md
index 87e4ae6901d..0d4b422183a 100644
--- a/gcc/config/arm/vec-common.md
+++ b/gcc/config/arm/vec-common.md
@@ -160,7 +160,12 @@
(match_operand:SI 2 "immediate_operand")]
"TARGET_NEON || TARGET_HAVE_MVE"
{
- if (TARGET_NEON)
+ if (<V_elem>mode == SFmode && !BYTES_BIG_ENDIAN)
+ emit_move_insn (operands[0],
+ simplify_gen_subreg (SFmode, operands[1], <MODE>mode,
+ INTVAL (operands[2])
+ * GET_MODE_UNIT_SIZE (<MODE>mode)));
+ else if (TARGET_NEON)
emit_insn (gen_neon_vec_extract<mode><V_elem_l> (operands[0], operands[1],
operands[2]));
else if (TARGET_HAVE_MVE)
diff --git a/gcc/testsuite/gcc.target/arm/pr60408-be.c
b/gcc/testsuite/gcc.target/arm/pr60408-be.c
new file mode 100644
index 00000000000..d69efa6600b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr60408-be.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_neon_ok } */
+/* { dg-skip-if "avoid conflicts with multilib options" { *-*-* } {
"-mfloat-abi=*" } { "-mfloat-abi=hard" } } */
+/* { dg-options "-O2" } */
+/* { dg-add-options arm_neon } */
+/* { dg-additional-options "-mfloat-abi=hard -mbig-endian" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* A big-endian lane keeps coming back through a core register. */
+
+#include "arm_neon.h"
+
+/*
+**high_lane:
+** vmov\.32 (r[0-9]+), d1\[0\]
+** vmov s0, \1
+** bx lr
+*/
+float
+high_lane (float32x4_t v)
+{
+ return vgetq_lane_f32 (v, 2);
+}
+
+/*
+**sub_lane:
+** vmov\.32 (r[0-9]+), d0\[0\]
+** vmov s0, \1
+** bx lr
+*/
+float
+sub_lane (float32x2_t v)
+{
+ return v[1];
+}
diff --git a/gcc/testsuite/gcc.target/arm/pr60408.c
b/gcc/testsuite/gcc.target/arm/pr60408.c
new file mode 100644
index 00000000000..e2c3b297dc5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr60408.c
@@ -0,0 +1,67 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_neon_ok } */
+/* { dg-require-effective-target arm_little_endian } */
+/* { dg-skip-if "avoid conflicts with multilib options" { *-*-* } {
"-mfloat-abi=*" } { "-mfloat-abi=hard" } } */
+/* { dg-options "-O2" } */
+/* { dg-add-options arm_neon } */
+/* { dg-additional-options "-mfloat-abi=hard" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "arm_neon.h"
+
+/*
+**add_lanes:
+** vadd\.f32 s0, s1, s0
+** bx lr
+*/
+float
+add_lanes (float32x2_t v)
+{
+ return vget_lane_f32 (v, 0) + vget_lane_f32 (v, 1);
+}
+
+/*
+**high_lane:
+** vmov\.f32 s0, s2
+** bx lr
+*/
+float
+high_lane (float32x4_t v)
+{
+ return vgetq_lane_f32 (v, 2);
+}
+
+/*
+**sub_lane:
+** vmov\.f32 s0, s1
+** bx lr
+*/
+float
+sub_lane (float32x2_t v)
+{
+ return v[1];
+}
+
+/*
+**sub_lane_q:
+** vmov\.f32 s0, s3
+** bx lr
+*/
+float
+sub_lane_q (float32x4_t v)
+{
+ return v[3];
+}
+
+/*
+**sum_lanes:
+** vadd\.f32 (s[0-9]+), s0, s1
+** vadd\.f32 \1, \1, s2
+** vadd\.f32 s0, \1, s3
+** bx lr
+*/
+float
+sum_lanes (float32x4_t v)
+{
+ return v[0] + v[1] + v[2] + v[3];
+}
--
2.55.0