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)
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
25 #include "coretypes.h"
29 #include "diagnostic.h"
31 #include "riscv-subset.h"
34 class riscv_target_attr_parser
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)
44 , m_cpu_info (nullptr)
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;
57 const char *m_raw_attr_str
;
58 bool parse_arch (const char *);
63 bool m_found_priority_p
;
64 riscv_subset_list
*m_subset_list
;
66 const riscv_cpu_info
*m_cpu_info
;
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
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
},
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
},
96 riscv_target_attr_parser::parse_arch (const char *str
)
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)
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);
131 "unexpected arch for %<target()%> attribute: must start "
136 const char *result
= m_subset_list
->parse_single_ext (token
+ 1);
137 /* Check parse_single_ext has consume all string. */
142 "unexpected arch for %<target()%> attribute: bad "
143 "string found %qs", token
);
147 token
= strtok_r (NULL
, ",", &str_to_check
);
150 m_subset_list
->set_allow_adding_dup (false);
151 m_subset_list
->finalize ();
155 if (m_subset_list
!= nullptr)
157 delete m_subset_list
;
158 m_subset_list
= nullptr;
163 /* Handle the ARCH_STR argument to the arch= target attribute. */
166 riscv_target_attr_parser::handle_arch (const char *str
)
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. */
177 riscv_target_attr_parser::handle_cpu (const char *str
)
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
);
187 error_at (m_loc
, "%<target()%> attribute: unknown CPU %qs", str
);
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
;
202 /* Handle the TUNE_STR argument to the tune= target attribute. */
205 riscv_target_attr_parser::handle_tune (const char *str
)
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);
214 error_at (m_loc
, "%<target()%> attribute: unknown TUNE %qs", str
);
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
);
240 riscv_target_attr_parser::update_settings (struct gcc_options
*opts
) const
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
);
256 opts
->x_riscv_cpu_string
= m_cpu_info
->name
;
259 opts
->x_riscv_tune_string
= m_tune
;
263 opts
->x_riscv_tune_string
= m_cpu_info
->tune
;
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. */
274 riscv_process_one_target_attr (char *arg_str
,
276 riscv_target_attr_parser
&attr_parser
,
277 const struct riscv_attribute_info
*attrs
)
279 size_t len
= strlen (arg_str
);
283 error_at (loc
, "malformed %<target()%> attribute");
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
, '=');
297 "attribute %<target(\"%s\")%> does not accept an argument",
304 for (const auto *attr
= attrs
;
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)
314 return (&attr_parser
->*attr
->handler
) (arg
);
317 error_at (loc
, "Got unknown attribute %<target(\"%s\")%>", str_to_check
);
321 /* Count how many times the character C appears in
322 NULL-terminated string STR. */
325 num_occurrences_in_str (char c
, char *str
)
327 unsigned int res
= 0;
339 /* Parse the string in ARGS that contains the target attribute information
340 and update the global target options space. */
343 riscv_process_target_attr (const char *args
,
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
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;
372 if (!riscv_process_one_target_attr (token
, loc
, attr_parser
, attrs
))
375 token
= strtok_r (NULL
, ";", &str_to_check
);
378 if (num_attrs
!= num_semicolons
+ 1)
380 error_at (loc
, "malformed %<target(\"%s\")%> attribute",
385 /* Apply settings from target attribute. */
386 attr_parser
.update_settings (&global_options
);
391 /* Parse the tree in ARGS that contains the target attribute information
392 and update the global target options space. */
395 riscv_process_target_attr (tree args
,
397 const struct riscv_attribute_info
*attrs
)
399 if (TREE_CODE (args
) == TREE_LIST
)
403 tree head
= TREE_VALUE (args
);
406 if (!riscv_process_target_attr (head
, loc
, attrs
))
409 args
= TREE_CHAIN (args
);
415 if (TREE_CODE (args
) != STRING_CST
)
417 error_at (loc
, "attribute %<target%> argument not a string");
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. */
431 riscv_option_valid_attribute_p (tree fndecl
, tree
, tree args
, int)
433 struct cl_target_option cur_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
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
,
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
);
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
);
473 /* Parse the tree in ARGS that contains the target_version attribute
474 information and update the global target options space. */
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");
486 args
= TREE_VALUE (args
);
489 if (!args
|| TREE_CODE (args
) != STRING_CST
)
491 error ("attribute %<target_version%> argument not a string");
495 const char *str
= TREE_STRING_POINTER (args
);
496 if (strcmp (str
, "default") == 0)
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 ("..."))). */
507 riscv_option_valid_version_attribute_p (tree fndecl
, tree
, tree args
, int)
509 struct cl_target_option cur_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
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
,
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. */
539 riscv_override_options_internal (&global_options
);
540 new_target
= build_target_option_node (&global_options
,
541 &global_options_set
);
547 DECL_FUNCTION_SPECIFIC_TARGET (fndecl
) = new_target
;
549 cl_target_option_restore (&global_options
, &global_options_set
, &cur_target
);