1 /* Pass for parsing functions with multiple target attributes. 2 3 Contributed by Evgeny Stupachenko <evstupac@gmail.com> 4 5 Copyright (C) 2015-2016 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 38 /* If the call in NODE has multiple target attribute with multiple fields, 39 replace it with dispatcher call and create dispatcher (once). */ 40 41 static void 42 create_dispatcher_calls (struct cgraph_node *node) 43 { 44 cgraph_edge *e; 45 cgraph_edge *e_next; 46 47 /* We need to remember NEXT_CALLER as it could be modified in the loop. */ 48 for (e = node->callers; e ;e = (e == NULL) ? e_next : e->next_caller) 49 { 50 tree resolver_decl; 51 tree idecl; 52 tree decl; 53 gimple *call = e->call_stmt; 54 struct cgraph_node *inode; 55 56 /* Checking if call of function is call of versioned function. 57 Versioned function are not inlined, so there is no need to 58 check for inline. */ 59 if (!call 60 || !(decl = gimple_call_fndecl (call)) 61 || !DECL_FUNCTION_VERSIONED (decl)) 62 continue; 63 64 if (!targetm.has_ifunc_p ()) 65 { 66 error_at (gimple_location (call), 67 "the call requires ifunc, which is not" 68 " supported by this target"); 69 break; 70 } 71 else if (!targetm.get_function_versions_dispatcher) 72 { 73 error_at (gimple_location (call), 74 "target does not support function version dispatcher"); 75 break; 76 } 77 78 e_next = e->next_caller; 79 idecl = targetm.get_function_versions_dispatcher (decl); 80 if (!idecl) 81 { 82 error_at (gimple_location (call), 83 "default target_clones attribute was not set"); 84 break; 85 } 86 inode = cgraph_node::get (idecl); 87 gcc_assert (inode); 88 resolver_decl = targetm.generate_version_dispatcher_body (inode); 89 90 /* Update aliases. */ 91 inode->alias = true; 92 inode->alias_target = resolver_decl; 93 if (!inode->analyzed) 94 inode->resolve_alias (cgraph_node::get (resolver_decl)); 95 96 e->redirect_callee (inode); 97 e->redirect_call_stmt_to_callee (); 98 /* Since REDIRECT_CALLEE modifies NEXT_CALLER field we move to 99 previously set NEXT_CALLER. */ 100 e = NULL; 101 } 102 } 103 104 /* Return length of attribute names string, 105 if arglist chain > 1, -1 otherwise. */ 106 107 static int 108 get_attr_len (tree arglist) 109 { 110 tree arg; 111 int str_len_sum = 0; 112 int argnum = 0; 113 114 for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 115 { 116 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); 117 size_t len = strlen (str); 118 str_len_sum += len + 1; 119 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ',')) 120 argnum++; 121 argnum++; 122 } 123 if (argnum <= 1) 124 return -1; 125 return str_len_sum; 126 } 127 128 /* Create string with attributes separated by comma. 129 Return number of attributes. */ 130 131 static int 132 get_attr_str (tree arglist, char *attr_str) 133 { 134 tree arg; 135 size_t str_len_sum = 0; 136 int argnum = 0; 137 138 for (arg = arglist; arg; arg = TREE_CHAIN (arg)) 139 { 140 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); 141 size_t len = strlen (str); 142 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ',')) 143 argnum++; 144 memcpy (attr_str + str_len_sum, str, len); 145 attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0'; 146 str_len_sum += len + 1; 147 argnum++; 148 } 149 return argnum; 150 } 151 152 /* Return number of attributes separated by comma and put them into ARGS. 153 If there is no DEFAULT attribute return -1. */ 154 155 static int 156 separate_attrs (char *attr_str, char **attrs) 157 { 158 int i = 0; 159 bool has_default = false; 160 161 for (char *attr = strtok (attr_str, ","); 162 attr != NULL; attr = strtok (NULL, ",")) 163 { 164 if (strcmp (attr, "default") == 0) 165 { 166 has_default = true; 167 continue; 168 } 169 attrs[i++] = attr; 170 } 171 if (!has_default) 172 return -1; 173 return i; 174 } 175 176 /* Return true if symbol is valid in assembler name. */ 177 178 static bool 179 is_valid_asm_symbol (char c) 180 { 181 if ('a' <= c && c <= 'z') 182 return true; 183 if ('A' <= c && c <= 'Z') 184 return true; 185 if ('0' <= c && c <= '9') 186 return true; 187 if (c == '_') 188 return true; 189 return false; 190 } 191 192 /* Replace all not valid assembler symbols with '_'. */ 193 194 static void 195 create_new_asm_name (char *old_asm_name, char *new_asm_name) 196 { 197 int i; 198 int old_name_len = strlen (old_asm_name); 199 200 /* Replace all not valid assembler symbols with '_'. */ 201 for (i = 0; i < old_name_len; i++) 202 if (!is_valid_asm_symbol (old_asm_name[i])) 203 new_asm_name[i] = '_'; 204 else 205 new_asm_name[i] = old_asm_name[i]; 206 new_asm_name[old_name_len] = '\0'; 207 } 208 209 /* Creates target clone of NODE. */ 210 211 static cgraph_node * 212 create_target_clone (cgraph_node *node, bool definition, char *name) 213 { 214 cgraph_node *new_node; 215 216 if (definition) 217 { 218 new_node = node->create_version_clone_with_body (vNULL, NULL, 219 NULL, false, 220 NULL, NULL, 221 name); 222 new_node->force_output = true; 223 } 224 else 225 { 226 tree new_decl = copy_node (node->decl); 227 new_node = cgraph_node::get_create (new_decl); 228 /* Generate a new name for the new version. */ 229 symtab->change_decl_assembler_name (new_node->decl, 230 clone_function_name (node->decl, 231 name)); 232 } 233 return new_node; 234 } 235 236 /* If the function in NODE has multiple target attributes 237 create the appropriate clone for each valid target attribute. */ 238 239 static bool 240 expand_target_clones (struct cgraph_node *node, bool definition) 241 { 242 int i; 243 /* Parsing target attributes separated by comma. */ 244 tree attr_target = lookup_attribute ("target_clones", 245 DECL_ATTRIBUTES (node->decl)); 246 /* No targets specified. */ 247 if (!attr_target) 248 return false; 249 250 tree arglist = TREE_VALUE (attr_target); 251 int attr_len = get_attr_len (arglist); 252 253 /* No need to clone for 1 target attribute. */ 254 if (attr_len == -1) 255 { 256 warning_at (DECL_SOURCE_LOCATION (node->decl), 257 0, 258 "single target_clones attribute is ignored"); 259 return false; 260 } 261 262 char *attr_str = XNEWVEC (char, attr_len); 263 int attrnum = get_attr_str (arglist, attr_str); 264 char **attrs = XNEWVEC (char *, attrnum); 265 266 attrnum = separate_attrs (attr_str, attrs); 267 if (attrnum == -1) 268 { 269 error_at (DECL_SOURCE_LOCATION (node->decl), 270 "default target was not set"); 271 XDELETEVEC (attrs); 272 XDELETEVEC (attr_str); 273 return false; 274 } 275 276 cgraph_function_version_info *decl1_v = NULL; 277 cgraph_function_version_info *decl2_v = NULL; 278 cgraph_function_version_info *before = NULL; 279 cgraph_function_version_info *after = NULL; 280 decl1_v = node->function_version (); 281 if (decl1_v == NULL) 282 decl1_v = node->insert_new_function_version (); 283 before = decl1_v; 284 DECL_FUNCTION_VERSIONED (node->decl) = 1; 285 286 for (i = 0; i < attrnum; i++) 287 { 288 char *attr = attrs[i]; 289 char *suffix = XNEWVEC (char, strlen (attr) + 1); 290 291 create_new_asm_name (attr, suffix); 292 /* Create new target clone. */ 293 cgraph_node *new_node = create_target_clone (node, definition, suffix); 294 new_node->local.local = false; 295 XDELETEVEC (suffix); 296 297 /* Set new attribute for the clone. */ 298 tree attributes = make_attribute ("target", attr, 299 DECL_ATTRIBUTES (new_node->decl)); 300 DECL_ATTRIBUTES (new_node->decl) = attributes; 301 location_t saved_loc = input_location; 302 input_location = DECL_SOURCE_LOCATION (node->decl); 303 if (!targetm.target_option.valid_attribute_p (new_node->decl, NULL, 304 TREE_VALUE (attributes), 305 0)) 306 { 307 input_location = saved_loc; 308 continue; 309 } 310 311 input_location = saved_loc; 312 decl2_v = new_node->function_version (); 313 if (decl2_v != NULL) 314 continue; 315 decl2_v = new_node->insert_new_function_version (); 316 317 /* Chain decl2_v and decl1_v. All semantically identical versions 318 will be chained together. */ 319 after = decl2_v; 320 while (before->next != NULL) 321 before = before->next; 322 while (after->prev != NULL) 323 after = after->prev; 324 325 before->next = after; 326 after->prev = before; 327 DECL_FUNCTION_VERSIONED (new_node->decl) = 1; 328 } 329 330 XDELETEVEC (attrs); 331 XDELETEVEC (attr_str); 332 333 /* Setting new attribute to initial function. */ 334 tree attributes = make_attribute ("target", "default", 335 DECL_ATTRIBUTES (node->decl)); 336 DECL_ATTRIBUTES (node->decl) = attributes; 337 node->local.local = false; 338 location_t saved_loc = input_location; 339 input_location = DECL_SOURCE_LOCATION (node->decl); 340 bool ret 341 = targetm.target_option.valid_attribute_p (node->decl, NULL, 342 TREE_VALUE (attributes), 0); 343 input_location = saved_loc; 344 return ret; 345 } 346 347 static unsigned int 348 ipa_target_clone (void) 349 { 350 struct cgraph_node *node; 351 352 bool target_clone_pass = false; 353 FOR_EACH_FUNCTION (node) 354 target_clone_pass |= expand_target_clones (node, node->definition); 355 356 if (target_clone_pass) 357 FOR_EACH_FUNCTION (node) 358 create_dispatcher_calls (node); 359 360 return 0; 361 } 362 363 namespace { 364 365 const pass_data pass_data_target_clone = 366 { 367 SIMPLE_IPA_PASS, /* type */ 368 "targetclone", /* name */ 369 OPTGROUP_NONE, /* optinfo_flags */ 370 TV_NONE, /* tv_id */ 371 ( PROP_ssa | PROP_cfg ), /* properties_required */ 372 0, /* properties_provided */ 373 0, /* properties_destroyed */ 374 0, /* todo_flags_start */ 375 TODO_update_ssa /* todo_flags_finish */ 376 }; 377 378 class pass_target_clone : public simple_ipa_opt_pass 379 { 380 public: 381 pass_target_clone (gcc::context *ctxt) 382 : simple_ipa_opt_pass (pass_data_target_clone, ctxt) 383 {} 384 385 /* opt_pass methods: */ 386 virtual bool gate (function *); 387 virtual unsigned int execute (function *) { return ipa_target_clone (); } 388 }; 389 390 bool 391 pass_target_clone::gate (function *) 392 { 393 return true; 394 } 395 396 } // anon namespace 397 398 simple_ipa_opt_pass * 399 make_pass_target_clone (gcc::context *ctxt) 400 { 401 return new pass_target_clone (ctxt); 402 } 403