[PR testsuite/116860] Testsuite adjustment for recently added tests
[official-gcc.git] / gcc / config / riscv / riscv-target-attr.cc
blob615f1b9c6ce1788174a6f67fe07598e3a955470a
1 /* Subroutines used for parsing target attribute for RISC-V.
2 Copyright (C) 2023-2025 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
20 #define IN_TARGET_CODE 1
22 #define INCLUDE_STRING
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "target.h"
27 #include "tree.h"
28 #include "tm_p.h"
29 #include "diagnostic.h"
30 #include "opts.h"
31 #include "riscv-subset.h"
33 namespace {
34 class riscv_target_attr_parser
36 public:
37 riscv_target_attr_parser (location_t loc)
38 : m_found_arch_p (false)
39 , m_found_tune_p (false)
40 , m_found_cpu_p (false)
41 , m_found_priority_p (false)
42 , m_subset_list (nullptr)
43 , m_loc (loc)
44 , m_cpu_info (nullptr)
45 , m_tune (nullptr)
46 , m_priority (0)
50 bool handle_arch (const char *);
51 bool handle_cpu (const char *);
52 bool handle_tune (const char *);
53 bool handle_priority (const char *);
55 void update_settings (struct gcc_options *opts) const;
56 private:
57 const char *m_raw_attr_str;
58 bool parse_arch (const char *);
60 bool m_found_arch_p;
61 bool m_found_tune_p;
62 bool m_found_cpu_p;
63 bool m_found_priority_p;
64 riscv_subset_list *m_subset_list;
65 location_t m_loc;
66 const riscv_cpu_info *m_cpu_info;
67 const char *m_tune;
68 int m_priority;
72 /* All the information needed to handle a target attribute.
73 NAME is the name of the attribute.
74 HANDLER is the function that takes the attribute string as an argument. */
76 struct riscv_attribute_info
78 const char *name;
79 bool (riscv_target_attr_parser::*handler) (const char *);
82 /* The target attributes that we support. */
84 static const struct riscv_attribute_info riscv_target_attrs[]
85 = {{"arch", &riscv_target_attr_parser::handle_arch},
86 {"cpu", &riscv_target_attr_parser::handle_cpu},
87 {"tune", &riscv_target_attr_parser::handle_tune},
88 {NULL, NULL}};
90 static const struct riscv_attribute_info riscv_target_version_attrs[]
91 = {{"arch", &riscv_target_attr_parser::handle_arch},
92 {"priority", &riscv_target_attr_parser::handle_priority},
93 {NULL, NULL}};
95 bool
96 riscv_target_attr_parser::parse_arch (const char *str)
98 if (m_subset_list)
99 delete m_subset_list;
100 /* Check if it's setting full arch string. */
101 if (strncmp ("rv", str, strlen ("rv")) == 0)
103 m_subset_list = riscv_subset_list::parse (str, m_loc);
105 if (m_subset_list == nullptr)
106 goto fail;
108 return true;
110 else
112 /* Parsing the extension list like "+<ext>[,+<ext>]*". */
113 size_t len = strlen (str);
114 std::unique_ptr<char[]> buf (new char[len+1]);
115 char *str_to_check = buf.get ();
116 strcpy (str_to_check, str);
117 const char *token = strtok_r (str_to_check, ",", &str_to_check);
118 const char *local_arch_str = global_options.x_riscv_arch_string;
119 m_subset_list = local_arch_str
120 ? riscv_subset_list::parse (local_arch_str, m_loc)
121 : riscv_cmdline_subset_list ()->clone ();
122 m_subset_list->set_loc (m_loc);
123 m_subset_list->set_allow_adding_dup (true);
125 while (token)
127 if (token[0] != '+')
129 error_at (
130 m_loc,
131 "unexpected arch for %<target()%> attribute: must start "
132 "with + or rv");
133 goto fail;
136 const char *result = m_subset_list->parse_single_ext (token + 1);
137 /* Check parse_single_ext has consume all string. */
138 if (*result != '\0')
140 error_at (
141 m_loc,
142 "unexpected arch for %<target()%> attribute: bad "
143 "string found %qs", token);
144 goto fail;
147 token = strtok_r (NULL, ",", &str_to_check);
150 m_subset_list->set_allow_adding_dup (false);
151 m_subset_list->finalize ();
152 return true;
154 fail:
155 if (m_subset_list != nullptr)
157 delete m_subset_list;
158 m_subset_list = nullptr;
160 return false;
163 /* Handle the ARCH_STR argument to the arch= target attribute. */
165 bool
166 riscv_target_attr_parser::handle_arch (const char *str)
168 if (m_found_arch_p)
169 error_at (m_loc, "%<target()%> attribute: arch appears more than once");
170 m_found_arch_p = true;
171 return parse_arch (str);
174 /* Handle the CPU_STR argument to the cpu= target attribute. */
176 bool
177 riscv_target_attr_parser::handle_cpu (const char *str)
179 if (m_found_cpu_p)
180 error_at (m_loc, "%<target()%> attribute: cpu appears more than once");
182 m_found_cpu_p = true;
183 const riscv_cpu_info *cpu_info = riscv_find_cpu (str);
185 if (!cpu_info)
187 error_at (m_loc, "%<target()%> attribute: unknown CPU %qs", str);
188 return false;
191 if (m_subset_list == nullptr)
193 const char *arch_str = cpu_info->arch;
194 m_subset_list = riscv_subset_list::parse (arch_str, m_loc);
195 gcc_assert (m_subset_list);
198 m_cpu_info = cpu_info;
199 return true;
202 /* Handle the TUNE_STR argument to the tune= target attribute. */
204 bool
205 riscv_target_attr_parser::handle_tune (const char *str)
207 if (m_found_tune_p)
208 error_at (m_loc, "%<target()%> attribute: tune appears more than once");
209 m_found_tune_p = true;
210 const struct riscv_tune_info *tune = riscv_parse_tune (str, true);
212 if (tune == nullptr)
214 error_at (m_loc, "%<target()%> attribute: unknown TUNE %qs", str);
215 return false;
218 m_tune = tune->name;
220 return true;
223 bool
224 riscv_target_attr_parser::handle_priority (const char *str)
226 if (m_found_priority_p)
227 error_at (m_loc, "%<target()%> attribute: priority appears more than once");
228 m_found_priority_p = true;
230 if (sscanf (str, "%d", &m_priority) != 1)
232 error_at (m_loc, "%<target()%> attribute: invalid priority %qs", str);
233 return false;
236 return true;
239 void
240 riscv_target_attr_parser::update_settings (struct gcc_options *opts) const
242 if (m_subset_list)
244 std::string local_arch = m_subset_list->to_string (true);
245 const char* local_arch_str = local_arch.c_str ();
246 struct cl_target_option *default_opts
247 = TREE_TARGET_OPTION (target_option_default_node);
248 if (opts->x_riscv_arch_string != default_opts->x_riscv_arch_string)
249 free (CONST_CAST (void *, (const void *) opts->x_riscv_arch_string));
250 opts->x_riscv_arch_string = xstrdup (local_arch_str);
252 riscv_set_arch_by_subset_list (m_subset_list, opts);
255 if (m_cpu_info)
256 opts->x_riscv_cpu_string = m_cpu_info->name;
258 if (m_tune)
259 opts->x_riscv_tune_string = m_tune;
260 else
262 if (m_cpu_info)
263 opts->x_riscv_tune_string = m_cpu_info->tune;
266 if (m_priority)
267 opts->x_riscv_fmv_priority = m_priority;
270 /* Parse ARG_STR which contains the definition of one target attribute.
271 Show appropriate errors if any or return true if the attribute is valid. */
273 static bool
274 riscv_process_one_target_attr (char *arg_str,
275 location_t loc,
276 riscv_target_attr_parser &attr_parser,
277 const struct riscv_attribute_info *attrs)
279 size_t len = strlen (arg_str);
281 if (len == 0)
283 error_at (loc, "malformed %<target()%> attribute");
284 return false;
287 std::unique_ptr<char[]> buf (new char[len+1]);
288 char *str_to_check = buf.get();
289 strcpy (str_to_check, arg_str);
291 char *arg = strchr (str_to_check, '=');
293 if (!arg)
295 error_at (
296 loc,
297 "attribute %<target(\"%s\")%> does not accept an argument",
298 str_to_check);
299 return false;
302 arg[0] = '\0';
303 ++arg;
304 for (const auto *attr = attrs;
305 attr->name;
306 ++attr)
308 /* If the names don't match up, or the user has given an argument
309 to an attribute that doesn't accept one, or didn't give an argument
310 to an attribute that expects one, fail to match. */
311 if (strncmp (str_to_check, attr->name, strlen (attr->name)) != 0)
312 continue;
314 return (&attr_parser->*attr->handler) (arg);
317 error_at (loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check);
318 return false;
321 /* Count how many times the character C appears in
322 NULL-terminated string STR. */
324 static unsigned int
325 num_occurrences_in_str (char c, char *str)
327 unsigned int res = 0;
328 while (*str != '\0')
330 if (*str == c)
331 res++;
333 str++;
336 return res;
339 /* Parse the string in ARGS that contains the target attribute information
340 and update the global target options space. */
342 bool
343 riscv_process_target_attr (const char *args,
344 location_t loc,
345 const struct riscv_attribute_info *attrs)
347 size_t len = strlen (args);
349 /* No need to emit warning or error on empty string here, generic code already
350 handle this case. */
351 if (len == 0)
353 return false;
356 std::unique_ptr<char[]> buf (new char[len+1]);
357 char *str_to_check = buf.get ();
358 strcpy (str_to_check, args);
360 /* Used to catch empty spaces between semi-colons i.e.
361 attribute ((target ("attr1;;attr2"))). */
362 unsigned int num_semicolons = num_occurrences_in_str (';', str_to_check);
364 /* Handle multiple target attributes separated by ';'. */
365 char *token = strtok_r (str_to_check, ";", &str_to_check);
367 riscv_target_attr_parser attr_parser (loc);
368 unsigned int num_attrs = 0;
369 while (token)
371 num_attrs++;
372 if (!riscv_process_one_target_attr (token, loc, attr_parser, attrs))
373 return false;
375 token = strtok_r (NULL, ";", &str_to_check);
378 if (num_attrs != num_semicolons + 1)
380 error_at (loc, "malformed %<target(\"%s\")%> attribute",
381 args);
382 return false;
385 /* Apply settings from target attribute. */
386 attr_parser.update_settings (&global_options);
388 return true;
391 /* Parse the tree in ARGS that contains the target attribute information
392 and update the global target options space. */
394 static bool
395 riscv_process_target_attr (tree args,
396 location_t loc,
397 const struct riscv_attribute_info *attrs)
399 if (TREE_CODE (args) == TREE_LIST)
403 tree head = TREE_VALUE (args);
404 if (head)
406 if (!riscv_process_target_attr (head, loc, attrs))
407 return false;
409 args = TREE_CHAIN (args);
410 } while (args);
412 return true;
415 if (TREE_CODE (args) != STRING_CST)
417 error_at (loc, "attribute %<target%> argument not a string");
418 return false;
421 return riscv_process_target_attr (TREE_STRING_POINTER (args), loc, attrs);
424 /* Implement TARGET_OPTION_VALID_ATTRIBUTE_P.
425 This is used to process attribute ((target ("..."))).
426 Note, that riscv_set_current_function() has not been called before,
427 so we need must not mess with the current global_options, which
428 likely belong to another function. */
430 bool
431 riscv_option_valid_attribute_p (tree fndecl, tree, tree args, int)
433 struct cl_target_option cur_target;
434 bool ret;
435 tree new_target;
436 tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
437 location_t loc = DECL_SOURCE_LOCATION (fndecl);
439 /* Save the current target options to restore at the end. */
440 cl_target_option_save (&cur_target, &global_options, &global_options_set);
442 /* If fndecl already has some target attributes applied to it, unpack
443 them so that we add this attribute on top of them, rather than
444 overwriting them. */
445 if (existing_target)
447 struct cl_target_option *existing_options
448 = TREE_TARGET_OPTION (existing_target);
450 if (existing_options)
451 cl_target_option_restore (&global_options, &global_options_set,
452 existing_options);
454 else
455 cl_target_option_restore (&global_options, &global_options_set,
456 TREE_TARGET_OPTION (target_option_default_node));
458 /* Now we can parse the attributes and set &global_options accordingly. */
459 ret = riscv_process_target_attr (args, loc, riscv_target_attrs);
460 if (ret)
462 riscv_override_options_internal (&global_options);
463 new_target = build_target_option_node (&global_options,
464 &global_options_set);
465 DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
468 /* Restore current target options to original state. */
469 cl_target_option_restore (&global_options, &global_options_set, &cur_target);
470 return ret;
473 /* Parse the tree in ARGS that contains the target_version attribute
474 information and update the global target options space. */
476 bool
477 riscv_process_target_version_attr (tree args, location_t loc)
479 if (TREE_CODE (args) == TREE_LIST)
481 if (TREE_CHAIN (args))
483 error ("attribute %<target_version%> has multiple values");
484 return false;
486 args = TREE_VALUE (args);
489 if (!args || TREE_CODE (args) != STRING_CST)
491 error ("attribute %<target_version%> argument not a string");
492 return false;
495 const char *str = TREE_STRING_POINTER (args);
496 if (strcmp (str, "default") == 0)
497 return true;
499 return riscv_process_target_attr (str, loc, riscv_target_version_attrs);
503 /* Implement TARGET_OPTION_VALID_VERSION_ATTRIBUTE_P. This is used to
504 process attribute ((target_version ("..."))). */
506 bool
507 riscv_option_valid_version_attribute_p (tree fndecl, tree, tree args, int)
509 struct cl_target_option cur_target;
510 bool ret;
511 tree new_target;
512 tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
513 location_t loc = DECL_SOURCE_LOCATION (fndecl);
515 /* Save the current target options to restore at the end. */
516 cl_target_option_save (&cur_target, &global_options, &global_options_set);
518 /* If fndecl already has some target attributes applied to it, unpack
519 them so that we add this attribute on top of them, rather than
520 overwriting them. */
521 if (existing_target)
523 struct cl_target_option *existing_options
524 = TREE_TARGET_OPTION (existing_target);
526 if (existing_options)
527 cl_target_option_restore (&global_options, &global_options_set,
528 existing_options);
530 else
531 cl_target_option_restore (&global_options, &global_options_set,
532 TREE_TARGET_OPTION (target_option_current_node));
534 ret = riscv_process_target_version_attr (args, loc);
536 /* Set up any additional state. */
537 if (ret)
539 riscv_override_options_internal (&global_options);
540 new_target = build_target_option_node (&global_options,
541 &global_options_set);
543 else
544 new_target = NULL;
546 if (fndecl && ret)
547 DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
549 cl_target_option_restore (&global_options, &global_options_set, &cur_target);
551 return ret;