LGTM, although I hope this can try to add to the
https://github.com/riscv-non-isa/riscv-c-api-doc but that's not a
blocker issue I think

On Tue, Dec 9, 2025 at 12:47 AM Robin Dapp <[email protected]> wrote:
>
> 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