1 /* Pass for parsing functions with multiple target attributes. 2 3 Contributed by Evgeny Stupachenko <evstupac@gmail.com> 4 5 Copyright (C) 2015-2019 Free Software Foundation, Inc. 6 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free 11 Software Foundation; either version 3, or (at your option) any later 12 version. 13 14 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with GCC; see the file COPYING3. If not see 21 <http://www.gnu.org/licenses/>. */ 22 23 #include "config.h" 24 #include "system.h" 25 #include "coretypes.h" 26 #include "backend.h" 27 #include "tree.h" 28 #include "stringpool.h" 29 #include "gimple.h" 30 #include "diagnostic-core.h" 31 #include "gimple-ssa.h" 32 #include "cgraph.h" 33 #include "tree-pass.h" 34 #include "target.h" 35 #include "attribs.h" 36 #include "pretty-print.h" 37 #include "gimple-iterator.h" 38 #include "gimple-walk.h" 39 #include "tree-inline.h" 40 #include "intl.h" 41 42 /* Walker callback that replaces all FUNCTION_DECL of a function that's 43 going to be versioned. */ 44 45 static tree 46 replace_function_decl (tree *op, int *walk_subtrees, void *data) 47 { 48 struct walk_stmt_info *wi = (struct walk_stmt_info *) data; 49 cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info; 50 51 if (TREE_CODE (*op) == FUNCTION_DECL 52 && info->this_node->decl == *op) 53 { 54 *op = info->dispatcher_resolver; 55 *walk_subtrees = 0; 56 } 57 58 return NULL; 59 } 60 61 /* If the call in NODE has multiple target attribute with multiple fields, 62 replace it with dispatcher call and create dispatcher (once). */ 63 64 static void 65 create_dispatcher_calls (struct cgraph_node *node) 66 { 67 ipa_ref *ref; 68 69 if (!DECL_FUNCTION_VERSIONED (node->decl) 70 || !is_function_default_version (node->decl)) 71 return; 72 73 if (!targetm.has_ifunc_p ()) 74 { 75 error_at (DECL_SOURCE_LOCATION (node->decl), 76 "the call requires %<ifunc%>, which is not" 77 " supported by this target"); 78 return; 79 } 80 else if (!targetm.get_function_versions_dispatcher) 81 { 82 error_at (DECL_SOURCE_LOCATION (node->decl), 83 "target does not support function version dispatcher"); 84 return; 85 } 86 87 tree idecl = targetm.get_function_versions_dispatcher (node->decl); 88 if (!idecl) 89 { 90 error_at (DECL_SOURCE_LOCATION (node->decl), 91 "default %<target_clones%> attribute was not set"); 92 return; 93 } 94 95 cgraph_node *inode = cgraph_node::get (idecl); 96 gcc_assert (inode); 97 tree resolver_decl = targetm.generate_version_dispatcher_body (inode); 98 99 /* Update aliases. */ 100 inode->alias = true; 101 inode->alias_target = resolver_decl; 102 if (!inode->analyzed) 103 inode->resolve_alias (cgraph_node::get (resolver_decl)); 104 105 auto_vec<cgraph_edge *> edges_to_redirect; 106 /* We need to capture the references by value rather than just pointers to them 107 and remove them right away, as removing them later would invalidate what 108 some other reference pointers point to. */ 109 auto_vec<ipa_ref> references_to_redirect; 110 111 while (node->iterate_referring (0, ref)) 112 { 113 references_to_redirect.safe_push (*ref); 114 ref->remove_reference (); 115 } 116 117 /* We need to remember NEXT_CALLER as it could be modified in the loop. */ 118 for (cgraph_edge *e = node->callers; e ; e = e->next_caller) 119 edges_to_redirect.safe_push (e); 120 121 if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ()) 122 { 123 /* Redirect edges. */ 124 unsigned i; 125 cgraph_edge *e; 126 FOR_EACH_VEC_ELT (edges_to_redirect, i, e) 127 { 128 e->redirect_callee (inode); 129 e->redirect_call_stmt_to_callee (); 130 } 131 132 /* Redirect references. */ 133 FOR_EACH_VEC_ELT (references_to_redirect, i, ref) 134 { 135 if (ref->use == IPA_REF_ADDR) 136 { 137 struct walk_stmt_info wi; 138 memset (&wi, 0, sizeof (wi)); 139 wi.info = (void *)node->function_version (); 140 141 if (dyn_cast<varpool_node *> (ref->referring)) 142 { 143 hash_set<tree> visited_nodes; 144 walk_tree (&DECL_INITIAL (ref->referring->decl), 145 replace_function_decl, &wi, &visited_nodes); 146 } 147 else 148 { 149 gimple_stmt_iterator it = gsi_for_stmt (ref->stmt); 150 if (ref->referring->decl != resolver_decl) 151 walk_gimple_stmt (&it, NULL, replace_function_decl, &wi); 152 } 153 154 symtab_node *source = ref->referring; 155 source->create_reference (inode, IPA_REF_ADDR); 156 } 157 else if (ref->use == IPA_REF_ALIAS) 158 { 159 symtab_node *source = ref->referring; 160 source->create_reference (inode, IPA_REF_ALIAS); 161 if (inode->get_comdat_group ()) 162 source->add_to_same_comdat_group (inode); 163 } 164 else 165 gcc_unreachable (); 166 } 167 } 168 169 symtab->change_decl_assembler_name (node->decl, 170 clone_function_name_numbered ( 171 node->decl, "default")); 172 173 /* FIXME: copy of cgraph_node::make_local that should be cleaned up 174 in next stage1. */ 175 node->make_decl_local (); 176 node->set_section (NULL); 177 node->set_comdat_group (NULL); 178 node->externally_visible = false; 179 node->forced_by_abi = false; 180 node->set_section (NULL); 181 node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY 182 || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP) 183 && !flag_incremental_link); 184 node->resolution = LDPR_PREVAILING_DEF_IRONLY; 185 186 DECL_ARTIFICIAL (node->decl) = 1; 187 node->force_output = true; 188 } 189 190 /* Return length of attribute names string, 191 if arglist chain > 1, -1 otherwise. */ 192 193 static int 194 get_attr_len (tree arglist) 195 { 196 tree arg; 197 int str_len_sum = 0; 198 int argnum = 0; 199 200 for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 201 { 202 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); 203 size_t len = strlen (str); 204 str_len_sum += len + 1; 205 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ',')) 206 argnum++; 207 argnum++; 208 } 209 if (argnum <= 1) 210 return -1; 211 return str_len_sum; 212 } 213 214 /* Create string with attributes separated by comma. 215 Return number of attributes. */ 216 217 static int 218 get_attr_str (tree arglist, char *attr_str) 219 { 220 tree arg; 221 size_t str_len_sum = 0; 222 int argnum = 0; 223 224 for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 225 { 226 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); 227 size_t len = strlen (str); 228 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ',')) 229 argnum++; 230 memcpy (attr_str + str_len_sum, str, len); 231 attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0'; 232 str_len_sum += len + 1; 233 argnum++; 234 } 235 return argnum; 236 } 237 238 /* Return number of attributes separated by comma and put them into ARGS. 239 If there is no DEFAULT attribute return -1. 240 If there is an empty string in attribute return -2. 241 If there are multiple DEFAULT attributes return -3. 242 */ 243 244 static int 245 separate_attrs (char *attr_str, char **attrs, int attrnum) 246 { 247 int i = 0; 248 int default_count = 0; 249 250 for (char *attr = strtok (attr_str, ","); 251 attr != NULL; attr = strtok (NULL, ",")) 252 { 253 if (strcmp (attr, "default") == 0) 254 { 255 default_count++; 256 continue; 257 } 258 attrs[i++] = attr; 259 } 260 if (default_count == 0) 261 return -1; 262 else if (default_count > 1) 263 return -3; 264 else if (i + default_count < attrnum) 265 return -2; 266 267 return i; 268 } 269 270 /* Return true if symbol is valid in assembler name. */ 271 272 static bool 273 is_valid_asm_symbol (char c) 274 { 275 if ('a' <= c && c <= 'z') 276 return true; 277 if ('A' <= c && c <= 'Z') 278 return true; 279 if ('0' <= c && c <= '9') 280 return true; 281 if (c == '_') 282 return true; 283 return false; 284 } 285 286 /* Replace all not valid assembler symbols with '_'. */ 287 288 static void 289 create_new_asm_name (char *old_asm_name, char *new_asm_name) 290 { 291 int i; 292 int old_name_len = strlen (old_asm_name); 293 294 /* Replace all not valid assembler symbols with '_'. */ 295 for (i = 0; i < old_name_len; i++) 296 if (!is_valid_asm_symbol (old_asm_name[i])) 297 new_asm_name[i] = '_'; 298 else 299 new_asm_name[i] = old_asm_name[i]; 300 new_asm_name[old_name_len] = '\0'; 301 } 302 303 /* Creates target clone of NODE. */ 304 305 static cgraph_node * 306 create_target_clone (cgraph_node *node, bool definition, char *name, 307 tree attributes) 308 { 309 cgraph_node *new_node; 310 311 if (definition) 312 { 313 new_node = node->create_version_clone_with_body (vNULL, NULL, 314 NULL, false, 315 NULL, NULL, 316 name, attributes); 317 if (new_node == NULL) 318 return NULL; 319 new_node->force_output = true; 320 } 321 else 322 { 323 tree new_decl = copy_node (node->decl); 324 new_node = cgraph_node::get_create (new_decl); 325 DECL_ATTRIBUTES (new_decl) = attributes; 326 /* Generate a new name for the new version. */ 327 symtab->change_decl_assembler_name (new_node->decl, 328 clone_function_name_numbered ( 329 node->decl, name)); 330 } 331 return new_node; 332 } 333 334 /* If the function in NODE has multiple target attributes 335 create the appropriate clone for each valid target attribute. */ 336 337 static bool 338 expand_target_clones (struct cgraph_node *node, bool definition) 339 { 340 int i; 341 /* Parsing target attributes separated by comma. */ 342 tree attr_target = lookup_attribute ("target_clones", 343 DECL_ATTRIBUTES (node->decl)); 344 /* No targets specified. */ 345 if (!attr_target) 346 return false; 347 348 tree arglist = TREE_VALUE (attr_target); 349 int attr_len = get_attr_len (arglist); 350 351 /* No need to clone for 1 target attribute. */ 352 if (attr_len == -1) 353 { 354 warning_at (DECL_SOURCE_LOCATION (node->decl), 355 0, "single %<target_clones%> attribute is ignored"); 356 return false; 357 } 358 359 if (node->definition 360 && !tree_versionable_function_p (node->decl)) 361 { 362 auto_diagnostic_group d; 363 error_at (DECL_SOURCE_LOCATION (node->decl), 364 "clones for %<target_clones%> attribute cannot be created"); 365 const char *reason = NULL; 366 if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl))) 367 reason = G_("function %q+F can never be copied " 368 "because it has %<noclone%> attribute"); 369 else 370 reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl)); 371 if (reason) 372 inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl); 373 return false; 374 } 375 376 char *attr_str = XNEWVEC (char, attr_len); 377 int attrnum = get_attr_str (arglist, attr_str); 378 char **attrs = XNEWVEC (char *, attrnum); 379 380 attrnum = separate_attrs (attr_str, attrs, attrnum); 381 switch (attrnum) 382 { 383 case -1: 384 error_at (DECL_SOURCE_LOCATION (node->decl), 385 "%<default%> target was not set"); 386 break; 387 case -2: 388 error_at (DECL_SOURCE_LOCATION (node->decl), 389 "an empty string cannot be in %<target_clones%> attribute"); 390 break; 391 case -3: 392 error_at (DECL_SOURCE_LOCATION (node->decl), 393 "multiple %<default%> targets were set"); 394 break; 395 default: 396 break; 397 } 398 399 if (attrnum < 0) 400 { 401 XDELETEVEC (attrs); 402 XDELETEVEC (attr_str); 403 return false; 404 } 405 406 cgraph_function_version_info *decl1_v = NULL; 407 cgraph_function_version_info *decl2_v = NULL; 408 cgraph_function_version_info *before = NULL; 409 cgraph_function_version_info *after = NULL; 410 decl1_v = node->function_version (); 411 if (decl1_v == NULL) 412 decl1_v = node->insert_new_function_version (); 413 before = decl1_v; 414 DECL_FUNCTION_VERSIONED (node->decl) = 1; 415 416 for (i = 0; i < attrnum; i++) 417 { 418 char *attr = attrs[i]; 419 char *suffix = XNEWVEC (char, strlen (attr) + 1); 420 421 create_new_asm_name (attr, suffix); 422 /* Create new target clone. */ 423 tree attributes = make_attribute ("target", attr, 424 DECL_ATTRIBUTES (node->decl)); 425 426 cgraph_node *new_node = create_target_clone (node, definition, suffix, 427 attributes); 428 if (new_node == NULL) 429 return false; 430 new_node->local.local = false; 431 XDELETEVEC (suffix); 432 433 decl2_v = new_node->function_version (); 434 if (decl2_v != NULL) 435 continue; 436 decl2_v = new_node->insert_new_function_version (); 437 438 /* Chain decl2_v and decl1_v. All semantically identical versions 439 will be chained together. */ 440 after = decl2_v; 441 while (before->next != NULL) 442 before = before->next; 443 while (after->prev != NULL) 444 after = after->prev; 445 446 before->next = after; 447 after->prev = before; 448 DECL_FUNCTION_VERSIONED (new_node->decl) = 1; 449 } 450 451 XDELETEVEC (attrs); 452 XDELETEVEC (attr_str); 453 454 /* Setting new attribute to initial function. */ 455 tree attributes = make_attribute ("target", "default", 456 DECL_ATTRIBUTES (node->decl)); 457 DECL_ATTRIBUTES (node->decl) = attributes; 458 node->local.local = false; 459 return true; 460 } 461 462 /* When NODE is a target clone, consider all callees and redirect 463 to a clone with equal target attributes. That prevents multiple 464 multi-versioning dispatches and a call-chain can be optimized. */ 465 466 static void 467 redirect_to_specific_clone (cgraph_node *node) 468 { 469 cgraph_function_version_info *fv = node->function_version (); 470 if (fv == NULL) 471 return; 472 473 tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl)); 474 if (attr_target == NULL_TREE) 475 return; 476 477 /* We need to remember NEXT_CALLER as it could be modified in the loop. */ 478 for (cgraph_edge *e = node->callees; e ; e = e->next_callee) 479 { 480 cgraph_function_version_info *fv2 = e->callee->function_version (); 481 if (!fv2) 482 continue; 483 484 tree attr_target2 = lookup_attribute ("target", 485 DECL_ATTRIBUTES (e->callee->decl)); 486 487 /* Function is not calling proper target clone. */ 488 if (!attribute_list_equal (attr_target, attr_target2)) 489 { 490 while (fv2->prev != NULL) 491 fv2 = fv2->prev; 492 493 /* Try to find a clone with equal target attribute. */ 494 for (; fv2 != NULL; fv2 = fv2->next) 495 { 496 cgraph_node *callee = fv2->this_node; 497 attr_target2 = lookup_attribute ("target", 498 DECL_ATTRIBUTES (callee->decl)); 499 if (attribute_list_equal (attr_target, attr_target2)) 500 { 501 e->redirect_callee (callee); 502 e->redirect_call_stmt_to_callee (); 503 break; 504 } 505 } 506 } 507 } 508 } 509 510 static unsigned int 511 ipa_target_clone (void) 512 { 513 struct cgraph_node *node; 514 auto_vec<cgraph_node *> to_dispatch; 515 516 FOR_EACH_FUNCTION (node) 517 if (expand_target_clones (node, node->definition)) 518 to_dispatch.safe_push (node); 519 520 for (unsigned i = 0; i < to_dispatch.length (); i++) 521 create_dispatcher_calls (to_dispatch[i]); 522 523 FOR_EACH_FUNCTION (node) 524 redirect_to_specific_clone (node); 525 526 return 0; 527 } 528 529 namespace { 530 531 const pass_data pass_data_target_clone = 532 { 533 SIMPLE_IPA_PASS, /* type */ 534 "targetclone", /* name */ 535 OPTGROUP_NONE, /* optinfo_flags */ 536 TV_NONE, /* tv_id */ 537 ( PROP_ssa | PROP_cfg ), /* properties_required */ 538 0, /* properties_provided */ 539 0, /* properties_destroyed */ 540 0, /* todo_flags_start */ 541 TODO_update_ssa /* todo_flags_finish */ 542 }; 543 544 class pass_target_clone : public simple_ipa_opt_pass 545 { 546 public: 547 pass_target_clone (gcc::context *ctxt) 548 : simple_ipa_opt_pass (pass_data_target_clone, ctxt) 549 {} 550 551 /* opt_pass methods: */ 552 virtual bool gate (function *); 553 virtual unsigned int execute (function *) { return ipa_target_clone (); } 554 }; 555 556 bool 557 pass_target_clone::gate (function *) 558 { 559 return true; 560 } 561 562 } // anon namespace 563 564 simple_ipa_opt_pass * 565 make_pass_target_clone (gcc::context *ctxt) 566 { 567 return new pass_target_clone (ctxt); 568 } 569