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 > >
