Hi,

Changes from v1:
 - Always Initialize m_max_vect.
 - Adjust docs.

v1 was already accepted so I plan to commit this if it passes CI.
v1's run had a spurious error that I hope will disappear with v2's run.
Will give Kito a chance to comment on the parsing flow adjustment as well, 
still.

Regtested on rv64gcv_zvl512b.

This adds an -mmax-vectorization option to riscv, a verbatim copy from
aarch64.  It is an option for vector code analysis.  Internally it increases
scalar costs by a large factor so every vector approach will be
profitable.  As opposed to -fno-vect-cost-model, we will still compare
the vector approaches amongst each other, though.

In order to handle this argument without an '=' I needed to change the
parsing flow slightly.

Regards
 Robin

gcc/ChangeLog:

        * config/riscv/riscv-target-attr.cc 
(riscv_target_attr_parser::handle_max_vect):
        New parser entry.
        (riscv_target_attr_parser::update_settings): Set max-vect
        option.
        (riscv_process_one_target_attr): Change null-arg handling.
        * config/riscv/riscv.cc (riscv_override_options_internal): Set
        max-vect option.
        * config/riscv/riscv.opt: Add -mmax-vectorization option.
        * doc/extend.texi: Document new option.
        * doc/invoke.texi: Ditto.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/rvv/autovec/max-vect-1.c: New test.
        * gcc.target/riscv/rvv/autovec/max-vect-2.c: New test.
---
 gcc/config/riscv/riscv-target-attr.cc         | 85 +++++++++++++------
 gcc/config/riscv/riscv.cc                     |  6 ++
 gcc/config/riscv/riscv.opt                    |  4 +
 gcc/doc/extend.texi                           | 10 +++
 gcc/doc/invoke.texi                           |  6 ++
 .../gcc.target/riscv/rvv/autovec/max-vect-1.c | 21 +++++
 .../gcc.target/riscv/rvv/autovec/max-vect-2.c | 21 +++++
 7 files changed, 129 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-2.c

diff --git a/gcc/config/riscv/riscv-target-attr.cc 
b/gcc/config/riscv/riscv-target-attr.cc
index e6ea073acc1..eb3e6888095 100644
--- a/gcc/config/riscv/riscv-target-attr.cc
+++ b/gcc/config/riscv/riscv-target-attr.cc
@@ -44,6 +44,7 @@ public:
     , m_cpu_info (nullptr)
     , m_tune (nullptr)
     , m_priority (0)
+    , m_max_vect (false)
   {
   }
 
@@ -51,6 +52,7 @@ public:
   bool handle_cpu (const char *);
   bool handle_tune (const char *);
   bool handle_priority (const char *);
+  bool handle_max_vect (const char *);
 
   void update_settings (struct gcc_options *opts) const;
 private:
@@ -66,31 +68,35 @@ private:
   const  riscv_cpu_info *m_cpu_info;
   const char *m_tune;
   int m_priority;
+  bool m_max_vect;
 };
 }
 
 /* All the information needed to handle a target attribute.
    NAME is the name of the attribute.
-   HANDLER is the function that takes the attribute string as an argument.  */
+   HANDLER is the function that takes the attribute string as an argument.
+   REQUIRES_ARG indicates whether this attribute requires an argument value.  
*/
 
 struct riscv_attribute_info
 {
   const char *name;
   bool (riscv_target_attr_parser::*handler) (const char *);
+  bool requires_arg;
 };
 
 /* The target attributes that we support.  */
 
 static const struct riscv_attribute_info riscv_target_attrs[]
-  = {{"arch", &riscv_target_attr_parser::handle_arch},
-     {"cpu", &riscv_target_attr_parser::handle_cpu},
-     {"tune", &riscv_target_attr_parser::handle_tune},
-     {NULL, NULL}};
+  = {{"arch", &riscv_target_attr_parser::handle_arch, true},
+     {"cpu", &riscv_target_attr_parser::handle_cpu, true},
+     {"tune", &riscv_target_attr_parser::handle_tune, true},
+     {"max-vectorization", &riscv_target_attr_parser::handle_max_vect, false},
+     {NULL, NULL, false}};
 
 static const struct riscv_attribute_info riscv_target_version_attrs[]
-  = {{"arch", &riscv_target_attr_parser::handle_arch},
-     {"priority", &riscv_target_attr_parser::handle_priority},
-     {NULL, NULL}};
+  = {{"arch", &riscv_target_attr_parser::handle_arch, true},
+     {"priority", &riscv_target_attr_parser::handle_priority, true},
+     {NULL, NULL, false}};
 
 bool
 riscv_target_attr_parser::parse_arch (const char *str)
@@ -254,6 +260,17 @@ riscv_target_attr_parser::handle_priority (const char *str)
   return true;
 }
 
+/* Handle max-vectorization.  There are no further options, just
+   enable it.  */
+
+bool
+riscv_target_attr_parser::handle_max_vect (const char *str ATTRIBUTE_UNUSED)
+{
+  m_max_vect = true;
+
+  return true;
+}
+
 void
 riscv_target_attr_parser::update_settings (struct gcc_options *opts) const
 {
@@ -279,6 +296,9 @@ riscv_target_attr_parser::update_settings (struct 
gcc_options *opts) const
 
   if (m_priority)
     opts->x_riscv_fmv_priority = m_priority;
+
+  if (m_max_vect)
+    opts->x_riscv_max_vectorization = true;
 }
 
 /* Parse ARG_STR which contains the definition of one target attribute.
@@ -303,33 +323,50 @@ riscv_process_one_target_attr (char *arg_str,
   char *str_to_check = buf.get();
   strcpy (str_to_check, arg_str);
 
+  /* Split attribute name from argument (if present).  */
   char *arg = strchr (str_to_check, '=');
-
-  if (!arg)
+  if (arg)
     {
-      if (loc)
-       error_at (*loc, "attribute %<target(\"%s\")%> does not "
-                 "accept an argument", str_to_check);
-      return false;
+      *arg = '\0';
+      ++arg;
+      /* Check for empty argument after '='.  */
+      if (*arg == '\0')
+       {
+         if (loc)
+           error_at (*loc, "attribute %<target(\"%s\")%> has empty argument",
+                     str_to_check);
+         return false;
+       }
     }
 
-  arg[0] = '\0';
-  ++arg;
-  for (const auto *attr = attrs;
-       attr->name;
-       ++attr)
+  /* Find matching attribute.  */
+  for (const auto *attr = attrs; attr->name; ++attr)
     {
-      /* If the names don't match up, or the user has given an argument
-        to an attribute that doesn't accept one, or didn't give an argument
-        to an attribute that expects one, fail to match.  */
-      if (strncmp (str_to_check, attr->name, strlen (attr->name)) != 0)
+      if (strcmp (str_to_check, attr->name) != 0)
        continue;
 
+      /* Validate argument presence matches expectations.  */
+      if (attr->requires_arg && !arg)
+       {
+         if (loc)
+           error_at (*loc, "attribute %<target(\"%s\")%> expects "
+                     "an argument", str_to_check);
+         return false;
+       }
+
+      if (!attr->requires_arg && arg)
+       {
+         if (loc)
+           error_at (*loc, "attribute %<target(\"%s\")%> does not "
+                     "accept an argument", str_to_check);
+         return false;
+       }
+
       return (&attr_parser->*attr->handler) (arg);
     }
 
   if (loc)
-    error_at (*loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check);
+    error_at (*loc, "unknown attribute %<target(\"%s\")%>", str_to_check);
   return false;
 }
 
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index c86cbc03857..9ac7f08bc33 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -12214,6 +12214,12 @@ riscv_override_options_internal (struct gcc_options 
*opts)
   /* Convert -march and -mrvv-vector-bits to a chunks count.  */
   riscv_vector_chunks = riscv_convert_vector_chunks (opts);
 
+  /* Set scalar costing to a high value such that we always pick
+     vectorization.  Increase scalar costing by 100x.  */
+  if (opts->x_riscv_max_vectorization)
+    SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+                        param_vect_scalar_cost_multiplier, 10000);
+
   if (opts->x_flag_cf_protection != CF_NONE)
     {
       if ((opts->x_flag_cf_protection & CF_RETURN) == CF_RETURN
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index 15793d0d004..1b70c0c0699 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -344,6 +344,10 @@ Target Undocumented RejectNegative Joined 
Enum(vsetvl_strategy) Var(vsetvl_strat
 Target Undocumented Uinteger Var(riscv_two_source_permutes) Init(0)
 -param=riscv-two-source-permutes Enable permutes with two source vectors.
 
+mmax-vectorization
+Target Var(riscv_max_vectorization) Save
+Override the scalar cost model such that vectorization is always profitable.
+
 Enum
 Name(stringop_strategy) Type(enum stringop_strategy_enum)
 Valid arguments to -mstringop-strategy=:
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 916452a932d..5f36510135c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -6007,6 +6007,16 @@ Specifies the core for which to tune the performance of 
this function and also
 whose architectural features to use.  The behavior and valid arguments are the
 same as for the @option{-mcpu=} command-line option.
 
+@cindex @code{max-vectorization} function attribute, RISC-V
+@item max-vectorization
+@itemx no-max-vectorization
+@code{max-vectorization} tells GCC's vectorizer to treat all vector
+loops as being more profitable than the original scalar loops when
+optimizing the current function.  @code{no-max-vectorization} disables
+this behavior.
+This corresponds to the behavior of the command-line options
+@option{-mmax-vectorization} and @option{-mno-max-vectorization}.
+
 @end table
 
 The above target attributes can be specified as follows:
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2da2a27acd6..654c2ee25b8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -32142,6 +32142,12 @@ Do not or do generate unaligned vector memory 
accesses.  The default is set
 to off unless the processor we are optimizing for explicitly supports
 element-misaligned vector memory access.
 
+@item -mmax-vectorization
+@itemx -mno-max-vectorization
+Enable or disable an override to vectorizer cost model making vectorization
+always appear profitable.  Unlike @option{-fno-vect-cost-model} or
+@option{-fvect-cost-model=unlimited} this option does not turn off cost
+comparison between different vector modes.
 
 @opindex mcmodel=
 @opindex mcmodel=medlow
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-1.c 
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-1.c
new file mode 100644
index 00000000000..923c1f8fb9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-1.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -fdump-tree-vect-details" } */
+
+void __attribute__ (( target ("max-vectorization")))
+foo (char *restrict a, int *restrict b, short *restrict c,
+     int *restrict d, int stride)
+{
+  if (stride <= 1)
+    return;
+
+  for (int i = 0; i < 3; i++)
+    {
+      int res = c[i];
+      int t = b[d[i]];
+      if (a[c[i]] != 0)
+        res = t * b[d[i]];
+      c[i] = res;
+    }
+}
+
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-2.c 
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-2.c
new file mode 100644
index 00000000000..fc5c2ada224
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/max-vect-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -mmax-vectorization 
-fdump-tree-vect-details" } */
+
+void
+foo (char *restrict a, int *restrict b, short *restrict c,
+     int *restrict d, int stride)
+{
+  if (stride <= 1)
+    return;
+
+  for (int i = 0; i < 3; i++)
+    {
+      int res = c[i];
+      int t = b[d[i]];
+      if (a[c[i]] != 0)
+        res = t * b[d[i]];
+      c[i] = res;
+    }
+}
+
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
-- 
2.51.1


Reply via email to