1 /* Subroutines used for parsing target attribute for RISC-V. 2 Copyright (C) 2023-2024 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 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. 10 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. 15 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/>. */ 19 20 #define IN_TARGET_CODE 1 21 22 #define INCLUDE_MEMORY 23 #define INCLUDE_STRING 24 #include "config.h" 25 #include "system.h" 26 #include "coretypes.h" 27 #include "target.h" 28 #include "tree.h" 29 #include "tm_p.h" 30 #include "diagnostic.h" 31 #include "opts.h" 32 #include "riscv-subset.h" 33 34 namespace { 35 class riscv_target_attr_parser 36 { 37 public: 38 riscv_target_attr_parser (location_t loc) 39 : m_found_arch_p (false) 40 , m_found_tune_p (false) 41 , m_found_cpu_p (false) 42 , m_subset_list (nullptr) 43 , m_loc (loc) 44 , m_cpu_info (nullptr) 45 , m_tune (nullptr) 46 { 47 } 48 49 bool handle_arch (const char *); 50 bool handle_cpu (const char *); 51 bool handle_tune (const char *); 52 53 void update_settings (struct gcc_options *opts) const; 54 private: 55 const char *m_raw_attr_str; 56 bool parse_arch (const char *); 57 58 bool m_found_arch_p; 59 bool m_found_tune_p; 60 bool m_found_cpu_p; 61 riscv_subset_list *m_subset_list; 62 location_t m_loc; 63 const riscv_cpu_info *m_cpu_info; 64 const char *m_tune; 65 }; 66 } 67 68 /* All the information needed to handle a target attribute. 69 NAME is the name of the attribute. 70 HANDLER is the function that takes the attribute string as an argument. */ 71 72 struct riscv_attribute_info 73 { 74 const char *name; 75 bool (riscv_target_attr_parser::*handler) (const char *); 76 }; 77 78 /* The target attributes that we support. */ 79 80 static const struct riscv_attribute_info riscv_attributes[] 81 = {{"arch", &riscv_target_attr_parser::handle_arch}, 82 {"cpu", &riscv_target_attr_parser::handle_cpu}, 83 {"tune", &riscv_target_attr_parser::handle_tune}}; 84 85 bool 86 riscv_target_attr_parser::parse_arch (const char *str) 87 { 88 if (m_subset_list) 89 delete m_subset_list; 90 /* Check if it's setting full arch string. */ 91 if (strncmp ("rv", str, strlen ("rv")) == 0) 92 { 93 m_subset_list = riscv_subset_list::parse (str, m_loc); 94 95 if (m_subset_list == nullptr) 96 goto fail; 97 98 return true; 99 } 100 else 101 { 102 /* Parsing the extension list like "+<ext>[,+<ext>]*". */ 103 size_t len = strlen (str); 104 std::unique_ptr<char[]> buf (new char[len+1]); 105 char *str_to_check = buf.get (); 106 strcpy (str_to_check, str); 107 const char *token = strtok_r (str_to_check, ",", &str_to_check); 108 const char *local_arch_str = global_options.x_riscv_arch_string; 109 m_subset_list = local_arch_str 110 ? riscv_subset_list::parse (local_arch_str, m_loc) 111 : riscv_cmdline_subset_list ()->clone (); 112 m_subset_list->set_loc (m_loc); 113 m_subset_list->set_allow_adding_dup (true); 114 115 while (token) 116 { 117 if (token[0] != '+') 118 { 119 error_at ( 120 m_loc, 121 "unexpected arch for %<target()%> attribute: must start " 122 "with + or rv"); 123 goto fail; 124 } 125 126 const char *result = m_subset_list->parse_single_ext (token + 1); 127 /* Check parse_single_ext has consume all string. */ 128 if (*result != '\0') 129 { 130 error_at ( 131 m_loc, 132 "unexpected arch for %<target()%> attribute: bad " 133 "string found %<%s%>", token); 134 goto fail; 135 } 136 137 token = strtok_r (NULL, ",", &str_to_check); 138 } 139 140 m_subset_list->set_allow_adding_dup (false); 141 m_subset_list->finalize (); 142 return true; 143 } 144 fail: 145 if (m_subset_list != nullptr) 146 { 147 delete m_subset_list; 148 m_subset_list = nullptr; 149 } 150 return false; 151 } 152 153 /* Handle the ARCH_STR argument to the arch= target attribute. */ 154 155 bool 156 riscv_target_attr_parser::handle_arch (const char *str) 157 { 158 if (m_found_arch_p) 159 error_at (m_loc, "%<target()%> attribute: arch appears more than once"); 160 m_found_arch_p = true; 161 return parse_arch (str); 162 } 163 164 /* Handle the CPU_STR argument to the cpu= target attribute. */ 165 166 bool 167 riscv_target_attr_parser::handle_cpu (const char *str) 168 { 169 if (m_found_cpu_p) 170 error_at (m_loc, "%<target()%> attribute: cpu appears more than once"); 171 172 m_found_cpu_p = true; 173 const riscv_cpu_info *cpu_info = riscv_find_cpu (str); 174 175 if (!cpu_info) 176 { 177 error_at (m_loc, "%<target()%> attribute: unknown CPU %qs", str); 178 return false; 179 } 180 181 if (m_subset_list == nullptr) 182 { 183 const char *arch_str = cpu_info->arch; 184 m_subset_list = riscv_subset_list::parse (arch_str, m_loc); 185 gcc_assert (m_subset_list); 186 } 187 188 m_cpu_info = cpu_info; 189 return true; 190 } 191 192 /* Handle the TUNE_STR argument to the tune= target attribute. */ 193 194 bool 195 riscv_target_attr_parser::handle_tune (const char *str) 196 { 197 if (m_found_tune_p) 198 error_at (m_loc, "%<target()%> attribute: tune appears more than once"); 199 m_found_tune_p = true; 200 const struct riscv_tune_info *tune = riscv_parse_tune (str, true); 201 202 if (tune == nullptr) 203 { 204 error_at (m_loc, "%<target()%> attribute: unknown TUNE %qs", str); 205 return false; 206 } 207 208 m_tune = tune->name; 209 210 return true; 211 } 212 213 void 214 riscv_target_attr_parser::update_settings (struct gcc_options *opts) const 215 { 216 if (m_subset_list) 217 { 218 std::string local_arch = m_subset_list->to_string (true); 219 const char* local_arch_str = local_arch.c_str (); 220 struct cl_target_option *default_opts 221 = TREE_TARGET_OPTION (target_option_default_node); 222 if (opts->x_riscv_arch_string != default_opts->x_riscv_arch_string) 223 free (CONST_CAST (void *, (const void *) opts->x_riscv_arch_string)); 224 opts->x_riscv_arch_string = xstrdup (local_arch_str); 225 226 riscv_set_arch_by_subset_list (m_subset_list, opts); 227 } 228 229 if (m_cpu_info) 230 opts->x_riscv_cpu_string = m_cpu_info->name; 231 232 if (m_tune) 233 opts->x_riscv_tune_string = m_tune; 234 else 235 { 236 if (m_cpu_info) 237 opts->x_riscv_tune_string = m_cpu_info->tune; 238 } 239 } 240 241 /* Parse ARG_STR which contains the definition of one target attribute. 242 Show appropriate errors if any or return true if the attribute is valid. */ 243 244 static bool 245 riscv_process_one_target_attr (char *arg_str, 246 location_t loc, 247 riscv_target_attr_parser &attr_parser) 248 { 249 size_t len = strlen (arg_str); 250 251 if (len == 0) 252 { 253 error_at (loc, "malformed %<target()%> attribute"); 254 return false; 255 } 256 257 std::unique_ptr<char[]> buf (new char[len+1]); 258 char *str_to_check = buf.get(); 259 strcpy (str_to_check, arg_str); 260 261 char *arg = strchr (str_to_check, '='); 262 263 if (!arg) 264 { 265 error_at ( 266 loc, 267 "attribute %<target(\"%s\")%> does not accept an argument", 268 str_to_check); 269 return false; 270 } 271 272 arg[0] = '\0'; 273 ++arg; 274 for (const auto &attr : riscv_attributes) 275 { 276 /* If the names don't match up, or the user has given an argument 277 to an attribute that doesn't accept one, or didn't give an argument 278 to an attribute that expects one, fail to match. */ 279 if (strncmp (str_to_check, attr.name, strlen (attr.name)) != 0) 280 continue; 281 282 return (&attr_parser->*attr.handler) (arg); 283 } 284 285 error_at (loc, "Got unknown attribute %<target(\"%s\")%>", str_to_check); 286 return false; 287 } 288 289 /* Count how many times the character C appears in 290 NULL-terminated string STR. */ 291 292 static unsigned int 293 num_occurences_in_str (char c, char *str) 294 { 295 unsigned int res = 0; 296 while (*str != '\0') 297 { 298 if (*str == c) 299 res++; 300 301 str++; 302 } 303 304 return res; 305 } 306 307 /* Parse the tree in ARGS that contains the target attribute information 308 and update the global target options space. */ 309 310 static bool 311 riscv_process_target_attr (tree args, location_t loc) 312 { 313 if (TREE_CODE (args) == TREE_LIST) 314 { 315 do 316 { 317 tree head = TREE_VALUE (args); 318 if (head) 319 { 320 if (!riscv_process_target_attr (head, loc)) 321 return false; 322 } 323 args = TREE_CHAIN (args); 324 } while (args); 325 326 return true; 327 } 328 329 if (TREE_CODE (args) != STRING_CST) 330 { 331 error_at (loc, "attribute %<target%> argument not a string"); 332 return false; 333 } 334 335 size_t len = strlen (TREE_STRING_POINTER (args)); 336 337 /* No need to emit warning or error on empty string here, generic code already 338 handle this case. */ 339 if (len == 0) 340 { 341 return false; 342 } 343 344 std::unique_ptr<char[]> buf (new char[len+1]); 345 char *str_to_check = buf.get (); 346 strcpy (str_to_check, TREE_STRING_POINTER (args)); 347 348 /* Used to catch empty spaces between semi-colons i.e. 349 attribute ((target ("attr1;;attr2"))). */ 350 unsigned int num_semicolons = num_occurences_in_str (';', str_to_check); 351 352 /* Handle multiple target attributes separated by ';'. */ 353 char *token = strtok_r (str_to_check, ";", &str_to_check); 354 355 riscv_target_attr_parser attr_parser (loc); 356 unsigned int num_attrs = 0; 357 while (token) 358 { 359 num_attrs++; 360 if (!riscv_process_one_target_attr (token, loc, attr_parser)) 361 return false; 362 363 token = strtok_r (NULL, ";", &str_to_check); 364 } 365 366 if (num_attrs != num_semicolons + 1) 367 { 368 error_at (loc, "malformed %<target(\"%s\")%> attribute", 369 TREE_STRING_POINTER (args)); 370 return false; 371 } 372 373 /* Apply settings from target attribute. */ 374 attr_parser.update_settings (&global_options); 375 376 return true; 377 } 378 379 /* Implement TARGET_OPTION_VALID_ATTRIBUTE_P. 380 This is used to process attribute ((target ("..."))). 381 Note, that riscv_set_current_function() has not been called before, 382 so we need must not mess with the current global_options, which 383 likely belong to another function. */ 384 385 bool 386 riscv_option_valid_attribute_p (tree fndecl, tree, tree args, int) 387 { 388 struct cl_target_option cur_target; 389 bool ret; 390 tree new_target; 391 tree existing_target = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); 392 location_t loc = DECL_SOURCE_LOCATION (fndecl); 393 394 /* Save the current target options to restore at the end. */ 395 cl_target_option_save (&cur_target, &global_options, &global_options_set); 396 397 /* If fndecl already has some target attributes applied to it, unpack 398 them so that we add this attribute on top of them, rather than 399 overwriting them. */ 400 if (existing_target) 401 { 402 struct cl_target_option *existing_options 403 = TREE_TARGET_OPTION (existing_target); 404 405 if (existing_options) 406 cl_target_option_restore (&global_options, &global_options_set, 407 existing_options); 408 } 409 else 410 cl_target_option_restore (&global_options, &global_options_set, 411 TREE_TARGET_OPTION (target_option_default_node)); 412 413 /* Now we can parse the attributes and set &global_options accordingly. */ 414 ret = riscv_process_target_attr (args, loc); 415 if (ret) 416 { 417 riscv_override_options_internal (&global_options); 418 new_target = build_target_option_node (&global_options, 419 &global_options_set); 420 DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target; 421 } 422 423 /* Restore current target options to original state. */ 424 cl_target_option_restore (&global_options, &global_options_set, &cur_target); 425 return ret; 426 } 427