This patch adds a GIMPLE simplification for extracting the first or last
active element from a converted vector.  If the conversion preserves the
number of lanes, the selected logical element is the same before and after the
conversion, so the middle end can extract from the original vector and convert
the scalar result instead.

For example:

  float last_s32_to_f32 (int *restrict a, int *restrict b,
                         int *restrict c, int n)
  {
    float last = 0.0f;
    for (int i = 0; i < n; ++i)
      {
        int x = a[i] + b[i];
        c[i] = x;
        last = (float) x;
      }
    return last;
  }

Before this patch we generate with -Ofast -march=armv8-a+sve:

  vect_last = (vector float) vect_x;
  last = .EXTRACT_LAST (loop_mask, vect_last);

After this patch the extraction happens before the scalar conversion:

  tmp = .EXTRACT_LAST (loop_mask, vect_x);
  last = (float) tmp;

With -Ofast -march=armv8-a+sve this gives the expected SVE scalar extraction and
conversion sequence:

  add     z30.s, z30.s, z31.s
  st1w    z30.s, p7, [x2, x4, lsl 2]
  lastb   s30, p6, z30.s
  scvtf   s0, s30

The idea is that usually a scalar convert is cheaper than vector converts
and un some cases, e.g. zero extensions not needed at all (on AArch64).

Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.

-O3, profiled, profiled -O3 and -O3 -LTO bootstrap tested and no issues.

 Also SPECCPU 2017 and SPECCPU 2026 tested.

OK for master?

Thanks,
Tamar

gcc/ChangeLog:

        * match.pd: Fold lane-preserving converts across vector extracts.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/sve/extract_last_convert_1.c: New test.

---
diff --git a/gcc/match.pd b/gcc/match.pd
index 
db5038b7dccd030a09b67d5070a2ce2b74651184..d3a9bcee3495811d21764d58e4588ce41d657c7f
 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -13084,6 +13084,26 @@ and,
       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
   (convert @0)))
 
+#if GIMPLE
+/* When extracting the first or last active element from a converted vector,
+   extract from the source vector instead and convert the scalar result.
+   This is valid if the conversion preserves the number of lanes, so that the
+   mask selects the same logical element in both vectors, and if the target
+   supports the extract operation on the source vector type.  */
+(for extract (IFN_EXTRACT_FIRST IFN_EXTRACT_LAST)
+(simplify
+ (extract @0 (convert@1 @2))
+ (if (!VECTOR_TYPE_P (type)
+      && VECTOR_TYPE_P (TREE_TYPE (@1))
+      && VECTOR_TYPE_P (TREE_TYPE (@2))
+      && known_eq (TYPE_VECTOR_SUBPARTS (TREE_TYPE (@1)),
+                  TYPE_VECTOR_SUBPARTS (TREE_TYPE (@2)))
+      && direct_internal_fn_supported_p (as_internal_fn (extract),
+                                        TREE_TYPE (@2), OPTIMIZE_FOR_BOTH))
+  (with { tree element_type = TREE_TYPE (TREE_TYPE (@2)); }
+   (convert (extract:element_type @0 @2))))))
+#endif
+
 /* Optimize
    c1 = VEC_PERM_EXPR (a, a, mask)
    c2 = VEC_PERM_EXPR (b, b, mask)
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c 
b/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c
new file mode 100644
index 
0000000000000000000000000000000000000000..d60669667826e4b1bb2d2cd3a2a93c613716eb22
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target aarch64_asm_sve_ok } } */
+/* { dg-options "-O3 -march=armv8-a+sve -mautovec-preference=sve-only --param 
vect-epilogues-nomask=0 -fdump-tree-optimized" } */
+
+float
+last_s32_to_f32 (int *restrict a, int *restrict b,
+                int *restrict c, int n)
+{
+  float last = 0.0f;
+  for (int i = 0; i < n; ++i)
+    {
+      int x = a[i] + b[i];
+      c[i] = x;
+      last = (float) x;
+    }
+  return last;
+}
+
+int
+last_f32_to_s32 (float *restrict a, float *restrict b,
+                float *restrict c, int n)
+{
+  int last = 0;
+  for (int i = 0; i < n; ++i)
+    {
+      float x = a[i] + b[i];
+      c[i] = x;
+      last = (int) x;
+    }
+  return last;
+}
+
+/* { dg-final { scan-tree-dump-times {_[0-9]+ = \.EXTRACT_LAST 
\(loop_mask_[0-9]+, vect_x_[0-9]+\.[0-9]+_[0-9]+\);\n  last_[0-9]+ = \(float\) 
_[0-9]+;} 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {_[0-9]+ = \.EXTRACT_LAST 
\(loop_mask_[0-9]+, vect_x_[0-9]+\.[0-9]+_[0-9]+\);\n  last_[0-9]+ = \(int\) 
_[0-9]+;} 1 "optimized" } } */


-- 
diff --git a/gcc/match.pd b/gcc/match.pd
index db5038b7dccd030a09b67d5070a2ce2b74651184..d3a9bcee3495811d21764d58e4588ce41d657c7f 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -13084,6 +13084,26 @@ and,
       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
   (convert @0)))
 
+#if GIMPLE
+/* When extracting the first or last active element from a converted vector,
+   extract from the source vector instead and convert the scalar result.
+   This is valid if the conversion preserves the number of lanes, so that the
+   mask selects the same logical element in both vectors, and if the target
+   supports the extract operation on the source vector type.  */
+(for extract (IFN_EXTRACT_FIRST IFN_EXTRACT_LAST)
+(simplify
+ (extract @0 (convert@1 @2))
+ (if (!VECTOR_TYPE_P (type)
+      && VECTOR_TYPE_P (TREE_TYPE (@1))
+      && VECTOR_TYPE_P (TREE_TYPE (@2))
+      && known_eq (TYPE_VECTOR_SUBPARTS (TREE_TYPE (@1)),
+		   TYPE_VECTOR_SUBPARTS (TREE_TYPE (@2)))
+      && direct_internal_fn_supported_p (as_internal_fn (extract),
+					 TREE_TYPE (@2), OPTIMIZE_FOR_BOTH))
+  (with { tree element_type = TREE_TYPE (TREE_TYPE (@2)); }
+   (convert (extract:element_type @0 @2))))))
+#endif
+
 /* Optimize
    c1 = VEC_PERM_EXPR (a, a, mask)
    c2 = VEC_PERM_EXPR (b, b, mask)
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c b/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c
new file mode 100644
index 0000000000000000000000000000000000000000..d60669667826e4b1bb2d2cd3a2a93c613716eb22
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/extract_last_convert_1.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target aarch64_asm_sve_ok } } */
+/* { dg-options "-O3 -march=armv8-a+sve -mautovec-preference=sve-only --param vect-epilogues-nomask=0 -fdump-tree-optimized" } */
+
+float
+last_s32_to_f32 (int *restrict a, int *restrict b,
+		 int *restrict c, int n)
+{
+  float last = 0.0f;
+  for (int i = 0; i < n; ++i)
+    {
+      int x = a[i] + b[i];
+      c[i] = x;
+      last = (float) x;
+    }
+  return last;
+}
+
+int
+last_f32_to_s32 (float *restrict a, float *restrict b,
+		 float *restrict c, int n)
+{
+  int last = 0;
+  for (int i = 0; i < n; ++i)
+    {
+      float x = a[i] + b[i];
+      c[i] = x;
+      last = (int) x;
+    }
+  return last;
+}
+
+/* { dg-final { scan-tree-dump-times {_[0-9]+ = \.EXTRACT_LAST \(loop_mask_[0-9]+, vect_x_[0-9]+\.[0-9]+_[0-9]+\);\n  last_[0-9]+ = \(float\) _[0-9]+;} 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times {_[0-9]+ = \.EXTRACT_LAST \(loop_mask_[0-9]+, vect_x_[0-9]+\.[0-9]+_[0-9]+\);\n  last_[0-9]+ = \(int\) _[0-9]+;} 1 "optimized" } } */

Reply via email to