1 /* String length optimization 2 Copyright (C) 2011-2013 Free Software Foundation, Inc. 3 Contributed by Jakub Jelinek <jakub@redhat.com> 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 #include "config.h" 22 #include "system.h" 23 #include "coretypes.h" 24 #include "tree-flow.h" 25 #include "tree-pass.h" 26 #include "domwalk.h" 27 #include "alloc-pool.h" 28 #include "tree-ssa-propagate.h" 29 #include "gimple-pretty-print.h" 30 #include "params.h" 31 #include "expr.h" 32 33 /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value 34 is an index into strinfo vector, negative value stands for 35 string length of a string literal (~strlen). */ 36 static vec<int> ssa_ver_to_stridx; 37 38 /* Number of currently active string indexes plus one. */ 39 static int max_stridx; 40 41 /* String information record. */ 42 typedef struct strinfo_struct 43 { 44 /* String length of this string. */ 45 tree length; 46 /* Any of the corresponding pointers for querying alias oracle. */ 47 tree ptr; 48 /* Statement for delayed length computation. */ 49 gimple stmt; 50 /* Pointer to '\0' if known, if NULL, it can be computed as 51 ptr + length. */ 52 tree endptr; 53 /* Reference count. Any changes to strinfo entry possibly shared 54 with dominating basic blocks need unshare_strinfo first, except 55 for dont_invalidate which affects only the immediately next 56 maybe_invalidate. */ 57 int refcount; 58 /* Copy of index. get_strinfo (si->idx) should return si; */ 59 int idx; 60 /* These 3 fields are for chaining related string pointers together. 61 E.g. for 62 bl = strlen (b); dl = strlen (d); strcpy (a, b); c = a + bl; 63 strcpy (c, d); e = c + dl; 64 strinfo(a) -> strinfo(c) -> strinfo(e) 65 All have ->first field equal to strinfo(a)->idx and are doubly 66 chained through prev/next fields. The later strinfos are required 67 to point into the same string with zero or more bytes after 68 the previous pointer and all bytes in between the two pointers 69 must be non-zero. Functions like strcpy or memcpy are supposed 70 to adjust all previous strinfo lengths, but not following strinfo 71 lengths (those are uncertain, usually invalidated during 72 maybe_invalidate, except when the alias oracle knows better). 73 Functions like strcat on the other side adjust the whole 74 related strinfo chain. 75 They are updated lazily, so to use the chain the same first fields 76 and si->prev->next == si->idx needs to be verified. */ 77 int first; 78 int next; 79 int prev; 80 /* A flag whether the string is known to be written in the current 81 function. */ 82 bool writable; 83 /* A flag for the next maybe_invalidate that this strinfo shouldn't 84 be invalidated. Always cleared by maybe_invalidate. */ 85 bool dont_invalidate; 86 } *strinfo; 87 88 /* Pool for allocating strinfo_struct entries. */ 89 static alloc_pool strinfo_pool; 90 91 /* Vector mapping positive string indexes to strinfo, for the 92 current basic block. The first pointer in the vector is special, 93 it is either NULL, meaning the vector isn't shared, or it is 94 a basic block pointer to the owner basic_block if shared. 95 If some other bb wants to modify the vector, the vector needs 96 to be unshared first, and only the owner bb is supposed to free it. */ 97 static vec<strinfo, va_heap, vl_embed> *stridx_to_strinfo; 98 99 /* One OFFSET->IDX mapping. */ 100 struct stridxlist 101 { 102 struct stridxlist *next; 103 HOST_WIDE_INT offset; 104 int idx; 105 }; 106 107 /* Hash table entry, mapping a DECL to a chain of OFFSET->IDX mappings. */ 108 struct decl_stridxlist_map 109 { 110 struct tree_map_base base; 111 struct stridxlist list; 112 }; 113 114 /* Hash table for mapping decls to a chained list of offset -> idx 115 mappings. */ 116 static htab_t decl_to_stridxlist_htab; 117 118 /* Obstack for struct stridxlist and struct decl_stridxlist_map. */ 119 static struct obstack stridx_obstack; 120 121 /* Last memcpy statement if it could be adjusted if the trailing 122 '\0' written is immediately overwritten, or 123 *x = '\0' store that could be removed if it is immediately overwritten. */ 124 struct laststmt_struct 125 { 126 gimple stmt; 127 tree len; 128 int stridx; 129 } laststmt; 130 131 /* Hash a from tree in a decl_stridxlist_map. */ 132 133 static unsigned int 134 decl_to_stridxlist_hash (const void *item) 135 { 136 return DECL_UID (((const struct decl_stridxlist_map *) item)->base.from); 137 } 138 139 /* Helper function for get_stridx. */ 140 141 static int 142 get_addr_stridx (tree exp) 143 { 144 HOST_WIDE_INT off; 145 struct decl_stridxlist_map ent, *e; 146 struct stridxlist *list; 147 tree base; 148 149 if (decl_to_stridxlist_htab == NULL) 150 return 0; 151 152 base = get_addr_base_and_unit_offset (exp, &off); 153 if (base == NULL || !DECL_P (base)) 154 return 0; 155 156 ent.base.from = base; 157 e = (struct decl_stridxlist_map *) 158 htab_find_with_hash (decl_to_stridxlist_htab, &ent, DECL_UID (base)); 159 if (e == NULL) 160 return 0; 161 162 list = &e->list; 163 do 164 { 165 if (list->offset == off) 166 return list->idx; 167 list = list->next; 168 } 169 while (list); 170 return 0; 171 } 172 173 /* Return string index for EXP. */ 174 175 static int 176 get_stridx (tree exp) 177 { 178 tree s, o; 179 180 if (TREE_CODE (exp) == SSA_NAME) 181 return ssa_ver_to_stridx[SSA_NAME_VERSION (exp)]; 182 183 if (TREE_CODE (exp) == ADDR_EXPR) 184 { 185 int idx = get_addr_stridx (TREE_OPERAND (exp, 0)); 186 if (idx != 0) 187 return idx; 188 } 189 190 s = string_constant (exp, &o); 191 if (s != NULL_TREE 192 && (o == NULL_TREE || host_integerp (o, 0)) 193 && TREE_STRING_LENGTH (s) > 0) 194 { 195 HOST_WIDE_INT offset = o ? tree_low_cst (o, 0) : 0; 196 const char *p = TREE_STRING_POINTER (s); 197 int max = TREE_STRING_LENGTH (s) - 1; 198 199 if (p[max] == '\0' && offset >= 0 && offset <= max) 200 return ~(int) strlen (p + offset); 201 } 202 return 0; 203 } 204 205 /* Return true if strinfo vector is shared with the immediate dominator. */ 206 207 static inline bool 208 strinfo_shared (void) 209 { 210 return vec_safe_length (stridx_to_strinfo) 211 && (*stridx_to_strinfo)[0] != NULL; 212 } 213 214 /* Unshare strinfo vector that is shared with the immediate dominator. */ 215 216 static void 217 unshare_strinfo_vec (void) 218 { 219 strinfo si; 220 unsigned int i = 0; 221 222 gcc_assert (strinfo_shared ()); 223 stridx_to_strinfo = vec_safe_copy (stridx_to_strinfo); 224 for (i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i) 225 if (si != NULL) 226 si->refcount++; 227 (*stridx_to_strinfo)[0] = NULL; 228 } 229 230 /* Attempt to create a string index for exp, ADDR_EXPR's operand. 231 Return a pointer to the location where the string index can 232 be stored (if 0) or is stored, or NULL if this can't be tracked. */ 233 234 static int * 235 addr_stridxptr (tree exp) 236 { 237 void **slot; 238 struct decl_stridxlist_map ent; 239 struct stridxlist *list; 240 HOST_WIDE_INT off; 241 242 tree base = get_addr_base_and_unit_offset (exp, &off); 243 if (base == NULL_TREE || !DECL_P (base)) 244 return NULL; 245 246 if (decl_to_stridxlist_htab == NULL) 247 { 248 decl_to_stridxlist_htab 249 = htab_create (64, decl_to_stridxlist_hash, tree_map_base_eq, NULL); 250 gcc_obstack_init (&stridx_obstack); 251 } 252 ent.base.from = base; 253 slot = htab_find_slot_with_hash (decl_to_stridxlist_htab, &ent, 254 DECL_UID (base), INSERT); 255 if (*slot) 256 { 257 int i; 258 list = &((struct decl_stridxlist_map *)*slot)->list; 259 for (i = 0; i < 16; i++) 260 { 261 if (list->offset == off) 262 return &list->idx; 263 if (list->next == NULL) 264 break; 265 } 266 if (i == 16) 267 return NULL; 268 list->next = XOBNEW (&stridx_obstack, struct stridxlist); 269 list = list->next; 270 } 271 else 272 { 273 struct decl_stridxlist_map *e 274 = XOBNEW (&stridx_obstack, struct decl_stridxlist_map); 275 e->base.from = base; 276 *slot = (void *) e; 277 list = &e->list; 278 } 279 list->next = NULL; 280 list->offset = off; 281 list->idx = 0; 282 return &list->idx; 283 } 284 285 /* Create a new string index, or return 0 if reached limit. */ 286 287 static int 288 new_stridx (tree exp) 289 { 290 int idx; 291 if (max_stridx >= PARAM_VALUE (PARAM_MAX_TRACKED_STRLENS)) 292 return 0; 293 if (TREE_CODE (exp) == SSA_NAME) 294 { 295 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (exp)) 296 return 0; 297 idx = max_stridx++; 298 ssa_ver_to_stridx[SSA_NAME_VERSION (exp)] = idx; 299 return idx; 300 } 301 if (TREE_CODE (exp) == ADDR_EXPR) 302 { 303 int *pidx = addr_stridxptr (TREE_OPERAND (exp, 0)); 304 if (pidx != NULL) 305 { 306 gcc_assert (*pidx == 0); 307 *pidx = max_stridx++; 308 return *pidx; 309 } 310 } 311 return 0; 312 } 313 314 /* Like new_stridx, but for ADDR_EXPR's operand instead. */ 315 316 static int 317 new_addr_stridx (tree exp) 318 { 319 int *pidx; 320 if (max_stridx >= PARAM_VALUE (PARAM_MAX_TRACKED_STRLENS)) 321 return 0; 322 pidx = addr_stridxptr (exp); 323 if (pidx != NULL) 324 { 325 gcc_assert (*pidx == 0); 326 *pidx = max_stridx++; 327 return *pidx; 328 } 329 return 0; 330 } 331 332 /* Create a new strinfo. */ 333 334 static strinfo 335 new_strinfo (tree ptr, int idx, tree length) 336 { 337 strinfo si = (strinfo) pool_alloc (strinfo_pool); 338 si->length = length; 339 si->ptr = ptr; 340 si->stmt = NULL; 341 si->endptr = NULL_TREE; 342 si->refcount = 1; 343 si->idx = idx; 344 si->first = 0; 345 si->prev = 0; 346 si->next = 0; 347 si->writable = false; 348 si->dont_invalidate = false; 349 return si; 350 } 351 352 /* Decrease strinfo refcount and free it if not referenced anymore. */ 353 354 static inline void 355 free_strinfo (strinfo si) 356 { 357 if (si && --si->refcount == 0) 358 pool_free (strinfo_pool, si); 359 } 360 361 /* Return strinfo vector entry IDX. */ 362 363 static inline strinfo 364 get_strinfo (int idx) 365 { 366 if (vec_safe_length (stridx_to_strinfo) <= (unsigned int) idx) 367 return NULL; 368 return (*stridx_to_strinfo)[idx]; 369 } 370 371 /* Set strinfo in the vector entry IDX to SI. */ 372 373 static inline void 374 set_strinfo (int idx, strinfo si) 375 { 376 if (vec_safe_length (stridx_to_strinfo) && (*stridx_to_strinfo)[0]) 377 unshare_strinfo_vec (); 378 if (vec_safe_length (stridx_to_strinfo) <= (unsigned int) idx) 379 vec_safe_grow_cleared (stridx_to_strinfo, idx + 1); 380 (*stridx_to_strinfo)[idx] = si; 381 } 382 383 /* Return string length, or NULL if it can't be computed. */ 384 385 static tree 386 get_string_length (strinfo si) 387 { 388 if (si->length) 389 return si->length; 390 391 if (si->stmt) 392 { 393 gimple stmt = si->stmt, lenstmt; 394 tree callee, lhs, fn, tem; 395 location_t loc; 396 gimple_stmt_iterator gsi; 397 398 gcc_assert (is_gimple_call (stmt)); 399 callee = gimple_call_fndecl (stmt); 400 gcc_assert (callee && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL); 401 lhs = gimple_call_lhs (stmt); 402 gcc_assert (builtin_decl_implicit_p (BUILT_IN_STPCPY)); 403 /* unshare_strinfo is intentionally not called here. The (delayed) 404 transformation of strcpy or strcat into stpcpy is done at the place 405 of the former strcpy/strcat call and so can affect all the strinfos 406 with the same stmt. If they were unshared before and transformation 407 has been already done, the handling of BUILT_IN_STPCPY{,_CHK} should 408 just compute the right length. */ 409 switch (DECL_FUNCTION_CODE (callee)) 410 { 411 case BUILT_IN_STRCAT: 412 case BUILT_IN_STRCAT_CHK: 413 gsi = gsi_for_stmt (stmt); 414 fn = builtin_decl_implicit (BUILT_IN_STRLEN); 415 gcc_assert (lhs == NULL_TREE); 416 tem = unshare_expr (gimple_call_arg (stmt, 0)); 417 lenstmt = gimple_build_call (fn, 1, tem); 418 lhs = make_ssa_name (TREE_TYPE (TREE_TYPE (fn)), lenstmt); 419 gimple_call_set_lhs (lenstmt, lhs); 420 gimple_set_vuse (lenstmt, gimple_vuse (stmt)); 421 gsi_insert_before (&gsi, lenstmt, GSI_SAME_STMT); 422 tem = gimple_call_arg (stmt, 0); 423 if (!ptrofftype_p (TREE_TYPE (lhs))) 424 { 425 lhs = convert_to_ptrofftype (lhs); 426 lhs = force_gimple_operand_gsi (&gsi, lhs, true, NULL_TREE, 427 true, GSI_SAME_STMT); 428 } 429 lenstmt 430 = gimple_build_assign_with_ops 431 (POINTER_PLUS_EXPR, 432 make_ssa_name (TREE_TYPE (gimple_call_arg (stmt, 0)), NULL), 433 tem, lhs); 434 gsi_insert_before (&gsi, lenstmt, GSI_SAME_STMT); 435 gimple_call_set_arg (stmt, 0, gimple_assign_lhs (lenstmt)); 436 lhs = NULL_TREE; 437 /* FALLTHRU */ 438 case BUILT_IN_STRCPY: 439 case BUILT_IN_STRCPY_CHK: 440 if (gimple_call_num_args (stmt) == 2) 441 fn = builtin_decl_implicit (BUILT_IN_STPCPY); 442 else 443 fn = builtin_decl_explicit (BUILT_IN_STPCPY_CHK); 444 gcc_assert (lhs == NULL_TREE); 445 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 446 { 447 fprintf (dump_file, "Optimizing: "); 448 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 449 } 450 gimple_call_set_fndecl (stmt, fn); 451 lhs = make_ssa_name (TREE_TYPE (TREE_TYPE (fn)), stmt); 452 gimple_call_set_lhs (stmt, lhs); 453 update_stmt (stmt); 454 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 455 { 456 fprintf (dump_file, "into: "); 457 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 458 } 459 /* FALLTHRU */ 460 case BUILT_IN_STPCPY: 461 case BUILT_IN_STPCPY_CHK: 462 gcc_assert (lhs != NULL_TREE); 463 loc = gimple_location (stmt); 464 si->endptr = lhs; 465 si->stmt = NULL; 466 lhs = fold_convert_loc (loc, size_type_node, lhs); 467 si->length = fold_convert_loc (loc, size_type_node, si->ptr); 468 si->length = fold_build2_loc (loc, MINUS_EXPR, size_type_node, 469 lhs, si->length); 470 break; 471 default: 472 gcc_unreachable (); 473 break; 474 } 475 } 476 477 return si->length; 478 } 479 480 /* Invalidate string length information for strings whose length 481 might change due to stores in stmt. */ 482 483 static bool 484 maybe_invalidate (gimple stmt) 485 { 486 strinfo si; 487 unsigned int i; 488 bool nonempty = false; 489 490 for (i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i) 491 if (si != NULL) 492 { 493 if (!si->dont_invalidate) 494 { 495 ao_ref r; 496 ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE); 497 if (stmt_may_clobber_ref_p_1 (stmt, &r)) 498 { 499 set_strinfo (i, NULL); 500 free_strinfo (si); 501 continue; 502 } 503 } 504 si->dont_invalidate = false; 505 nonempty = true; 506 } 507 return nonempty; 508 } 509 510 /* Unshare strinfo record SI, if it has recount > 1 or 511 if stridx_to_strinfo vector is shared with some other 512 bbs. */ 513 514 static strinfo 515 unshare_strinfo (strinfo si) 516 { 517 strinfo nsi; 518 519 if (si->refcount == 1 && !strinfo_shared ()) 520 return si; 521 522 nsi = new_strinfo (si->ptr, si->idx, si->length); 523 nsi->stmt = si->stmt; 524 nsi->endptr = si->endptr; 525 nsi->first = si->first; 526 nsi->prev = si->prev; 527 nsi->next = si->next; 528 nsi->writable = si->writable; 529 set_strinfo (si->idx, nsi); 530 free_strinfo (si); 531 return nsi; 532 } 533 534 /* Return first strinfo in the related strinfo chain 535 if all strinfos in between belong to the chain, otherwise 536 NULL. */ 537 538 static strinfo 539 verify_related_strinfos (strinfo origsi) 540 { 541 strinfo si = origsi, psi; 542 543 if (origsi->first == 0) 544 return NULL; 545 for (; si->prev; si = psi) 546 { 547 if (si->first != origsi->first) 548 return NULL; 549 psi = get_strinfo (si->prev); 550 if (psi == NULL) 551 return NULL; 552 if (psi->next != si->idx) 553 return NULL; 554 } 555 if (si->idx != si->first) 556 return NULL; 557 return si; 558 } 559 560 /* Note that PTR, a pointer SSA_NAME initialized in the current stmt, points 561 to a zero-length string and if possible chain it to a related strinfo 562 chain whose part is or might be CHAINSI. */ 563 564 static strinfo 565 zero_length_string (tree ptr, strinfo chainsi) 566 { 567 strinfo si; 568 int idx; 569 gcc_checking_assert (TREE_CODE (ptr) == SSA_NAME 570 && get_stridx (ptr) == 0); 571 572 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)) 573 return NULL; 574 if (chainsi != NULL) 575 { 576 si = verify_related_strinfos (chainsi); 577 if (si) 578 { 579 chainsi = si; 580 for (; chainsi->next; chainsi = si) 581 { 582 if (chainsi->endptr == NULL_TREE) 583 { 584 chainsi = unshare_strinfo (chainsi); 585 chainsi->endptr = ptr; 586 } 587 si = get_strinfo (chainsi->next); 588 if (si == NULL 589 || si->first != chainsi->first 590 || si->prev != chainsi->idx) 591 break; 592 } 593 gcc_assert (chainsi->length || chainsi->stmt); 594 if (chainsi->endptr == NULL_TREE) 595 { 596 chainsi = unshare_strinfo (chainsi); 597 chainsi->endptr = ptr; 598 } 599 if (chainsi->length && integer_zerop (chainsi->length)) 600 { 601 if (chainsi->next) 602 { 603 chainsi = unshare_strinfo (chainsi); 604 chainsi->next = 0; 605 } 606 ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)] = chainsi->idx; 607 return chainsi; 608 } 609 } 610 else if (chainsi->first || chainsi->prev || chainsi->next) 611 { 612 chainsi = unshare_strinfo (chainsi); 613 chainsi->first = 0; 614 chainsi->prev = 0; 615 chainsi->next = 0; 616 } 617 } 618 idx = new_stridx (ptr); 619 if (idx == 0) 620 return NULL; 621 si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0)); 622 set_strinfo (idx, si); 623 si->endptr = ptr; 624 if (chainsi != NULL) 625 { 626 chainsi = unshare_strinfo (chainsi); 627 if (chainsi->first == 0) 628 chainsi->first = chainsi->idx; 629 chainsi->next = idx; 630 if (chainsi->endptr == NULL_TREE) 631 chainsi->endptr = ptr; 632 si->prev = chainsi->idx; 633 si->first = chainsi->first; 634 si->writable = chainsi->writable; 635 } 636 return si; 637 } 638 639 /* For strinfo ORIGSI whose length has been just updated 640 update also related strinfo lengths (add ADJ to each, 641 but don't adjust ORIGSI). */ 642 643 static void 644 adjust_related_strinfos (location_t loc, strinfo origsi, tree adj) 645 { 646 strinfo si = verify_related_strinfos (origsi); 647 648 if (si == NULL) 649 return; 650 651 while (1) 652 { 653 strinfo nsi; 654 655 if (si != origsi) 656 { 657 tree tem; 658 659 si = unshare_strinfo (si); 660 if (si->length) 661 { 662 tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj); 663 si->length = fold_build2_loc (loc, PLUS_EXPR, 664 TREE_TYPE (si->length), si->length, 665 tem); 666 } 667 else if (si->stmt != NULL) 668 /* Delayed length computation is unaffected. */ 669 ; 670 else 671 gcc_unreachable (); 672 673 si->endptr = NULL_TREE; 674 si->dont_invalidate = true; 675 } 676 if (si->next == 0) 677 return; 678 nsi = get_strinfo (si->next); 679 if (nsi == NULL 680 || nsi->first != si->first 681 || nsi->prev != si->idx) 682 return; 683 si = nsi; 684 } 685 } 686 687 /* Find if there are other SSA_NAME pointers equal to PTR 688 for which we don't track their string lengths yet. If so, use 689 IDX for them. */ 690 691 static void 692 find_equal_ptrs (tree ptr, int idx) 693 { 694 if (TREE_CODE (ptr) != SSA_NAME) 695 return; 696 while (1) 697 { 698 gimple stmt = SSA_NAME_DEF_STMT (ptr); 699 if (!is_gimple_assign (stmt)) 700 return; 701 ptr = gimple_assign_rhs1 (stmt); 702 switch (gimple_assign_rhs_code (stmt)) 703 { 704 case SSA_NAME: 705 break; 706 CASE_CONVERT: 707 if (!POINTER_TYPE_P (TREE_TYPE (ptr))) 708 return; 709 if (TREE_CODE (ptr) == SSA_NAME) 710 break; 711 if (TREE_CODE (ptr) != ADDR_EXPR) 712 return; 713 /* FALLTHRU */ 714 case ADDR_EXPR: 715 { 716 int *pidx = addr_stridxptr (TREE_OPERAND (ptr, 0)); 717 if (pidx != NULL && *pidx == 0) 718 *pidx = idx; 719 return; 720 } 721 default: 722 return; 723 } 724 725 /* We might find an endptr created in this pass. Grow the 726 vector in that case. */ 727 if (ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr)) 728 ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names); 729 730 if (ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)] != 0) 731 return; 732 ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)] = idx; 733 } 734 } 735 736 /* If the last .MEM setter statement before STMT is 737 memcpy (x, y, strlen (y) + 1), the only .MEM use of it is STMT 738 and STMT is known to overwrite x[strlen (x)], adjust the last memcpy to 739 just memcpy (x, y, strlen (y)). SI must be the zero length 740 strinfo. */ 741 742 static void 743 adjust_last_stmt (strinfo si, gimple stmt, bool is_strcat) 744 { 745 tree vuse, callee, len; 746 struct laststmt_struct last = laststmt; 747 strinfo lastsi, firstsi; 748 749 laststmt.stmt = NULL; 750 laststmt.len = NULL_TREE; 751 laststmt.stridx = 0; 752 753 if (last.stmt == NULL) 754 return; 755 756 vuse = gimple_vuse (stmt); 757 if (vuse == NULL_TREE 758 || SSA_NAME_DEF_STMT (vuse) != last.stmt 759 || !has_single_use (vuse)) 760 return; 761 762 gcc_assert (last.stridx > 0); 763 lastsi = get_strinfo (last.stridx); 764 if (lastsi == NULL) 765 return; 766 767 if (lastsi != si) 768 { 769 if (lastsi->first == 0 || lastsi->first != si->first) 770 return; 771 772 firstsi = verify_related_strinfos (si); 773 if (firstsi == NULL) 774 return; 775 while (firstsi != lastsi) 776 { 777 strinfo nextsi; 778 if (firstsi->next == 0) 779 return; 780 nextsi = get_strinfo (firstsi->next); 781 if (nextsi == NULL 782 || nextsi->prev != firstsi->idx 783 || nextsi->first != si->first) 784 return; 785 firstsi = nextsi; 786 } 787 } 788 789 if (!is_strcat) 790 { 791 if (si->length == NULL_TREE || !integer_zerop (si->length)) 792 return; 793 } 794 795 if (is_gimple_assign (last.stmt)) 796 { 797 gimple_stmt_iterator gsi; 798 799 if (!integer_zerop (gimple_assign_rhs1 (last.stmt))) 800 return; 801 if (stmt_could_throw_p (last.stmt)) 802 return; 803 gsi = gsi_for_stmt (last.stmt); 804 unlink_stmt_vdef (last.stmt); 805 release_defs (last.stmt); 806 gsi_remove (&gsi, true); 807 return; 808 } 809 810 if (!gimple_call_builtin_p (last.stmt, BUILT_IN_NORMAL)) 811 return; 812 813 callee = gimple_call_fndecl (last.stmt); 814 switch (DECL_FUNCTION_CODE (callee)) 815 { 816 case BUILT_IN_MEMCPY: 817 case BUILT_IN_MEMCPY_CHK: 818 break; 819 default: 820 return; 821 } 822 823 len = gimple_call_arg (last.stmt, 2); 824 if (host_integerp (len, 1)) 825 { 826 if (!host_integerp (last.len, 1) 827 || integer_zerop (len) 828 || (unsigned HOST_WIDE_INT) tree_low_cst (len, 1) 829 != (unsigned HOST_WIDE_INT) tree_low_cst (last.len, 1) + 1) 830 return; 831 /* Don't adjust the length if it is divisible by 4, it is more efficient 832 to store the extra '\0' in that case. */ 833 if ((((unsigned HOST_WIDE_INT) tree_low_cst (len, 1)) & 3) == 0) 834 return; 835 } 836 else if (TREE_CODE (len) == SSA_NAME) 837 { 838 gimple def_stmt = SSA_NAME_DEF_STMT (len); 839 if (!is_gimple_assign (def_stmt) 840 || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR 841 || gimple_assign_rhs1 (def_stmt) != last.len 842 || !integer_onep (gimple_assign_rhs2 (def_stmt))) 843 return; 844 } 845 else 846 return; 847 848 gimple_call_set_arg (last.stmt, 2, last.len); 849 update_stmt (last.stmt); 850 } 851 852 /* Handle a strlen call. If strlen of the argument is known, replace 853 the strlen call with the known value, otherwise remember that strlen 854 of the argument is stored in the lhs SSA_NAME. */ 855 856 static void 857 handle_builtin_strlen (gimple_stmt_iterator *gsi) 858 { 859 int idx; 860 tree src; 861 gimple stmt = gsi_stmt (*gsi); 862 tree lhs = gimple_call_lhs (stmt); 863 864 if (lhs == NULL_TREE) 865 return; 866 867 src = gimple_call_arg (stmt, 0); 868 idx = get_stridx (src); 869 if (idx) 870 { 871 strinfo si = NULL; 872 tree rhs; 873 874 if (idx < 0) 875 rhs = build_int_cst (TREE_TYPE (lhs), ~idx); 876 else 877 { 878 rhs = NULL_TREE; 879 si = get_strinfo (idx); 880 if (si != NULL) 881 rhs = get_string_length (si); 882 } 883 if (rhs != NULL_TREE) 884 { 885 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 886 { 887 fprintf (dump_file, "Optimizing: "); 888 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 889 } 890 rhs = unshare_expr (rhs); 891 if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) 892 rhs = fold_convert_loc (gimple_location (stmt), 893 TREE_TYPE (lhs), rhs); 894 if (!update_call_from_tree (gsi, rhs)) 895 gimplify_and_update_call_from_tree (gsi, rhs); 896 stmt = gsi_stmt (*gsi); 897 update_stmt (stmt); 898 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 899 { 900 fprintf (dump_file, "into: "); 901 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 902 } 903 if (si != NULL 904 && TREE_CODE (si->length) != SSA_NAME 905 && TREE_CODE (si->length) != INTEGER_CST 906 && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) 907 { 908 si = unshare_strinfo (si); 909 si->length = lhs; 910 } 911 return; 912 } 913 } 914 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) 915 return; 916 if (idx == 0) 917 idx = new_stridx (src); 918 else if (get_strinfo (idx) != NULL) 919 return; 920 if (idx) 921 { 922 strinfo si = new_strinfo (src, idx, lhs); 923 set_strinfo (idx, si); 924 find_equal_ptrs (src, idx); 925 } 926 } 927 928 /* Handle a strchr call. If strlen of the first argument is known, replace 929 the strchr (x, 0) call with the endptr or x + strlen, otherwise remember 930 that lhs of the call is endptr and strlen of the argument is endptr - x. */ 931 932 static void 933 handle_builtin_strchr (gimple_stmt_iterator *gsi) 934 { 935 int idx; 936 tree src; 937 gimple stmt = gsi_stmt (*gsi); 938 tree lhs = gimple_call_lhs (stmt); 939 940 if (lhs == NULL_TREE) 941 return; 942 943 if (!integer_zerop (gimple_call_arg (stmt, 1))) 944 return; 945 946 src = gimple_call_arg (stmt, 0); 947 idx = get_stridx (src); 948 if (idx) 949 { 950 strinfo si = NULL; 951 tree rhs; 952 953 if (idx < 0) 954 rhs = build_int_cst (size_type_node, ~idx); 955 else 956 { 957 rhs = NULL_TREE; 958 si = get_strinfo (idx); 959 if (si != NULL) 960 rhs = get_string_length (si); 961 } 962 if (rhs != NULL_TREE) 963 { 964 location_t loc = gimple_location (stmt); 965 966 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 967 { 968 fprintf (dump_file, "Optimizing: "); 969 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 970 } 971 if (si != NULL && si->endptr != NULL_TREE) 972 { 973 rhs = unshare_expr (si->endptr); 974 if (!useless_type_conversion_p (TREE_TYPE (lhs), 975 TREE_TYPE (rhs))) 976 rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); 977 } 978 else 979 { 980 rhs = fold_convert_loc (loc, sizetype, unshare_expr (rhs)); 981 rhs = fold_build2_loc (loc, POINTER_PLUS_EXPR, 982 TREE_TYPE (src), src, rhs); 983 if (!useless_type_conversion_p (TREE_TYPE (lhs), 984 TREE_TYPE (rhs))) 985 rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); 986 } 987 if (!update_call_from_tree (gsi, rhs)) 988 gimplify_and_update_call_from_tree (gsi, rhs); 989 stmt = gsi_stmt (*gsi); 990 update_stmt (stmt); 991 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 992 { 993 fprintf (dump_file, "into: "); 994 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 995 } 996 if (si != NULL 997 && si->endptr == NULL_TREE 998 && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) 999 { 1000 si = unshare_strinfo (si); 1001 si->endptr = lhs; 1002 } 1003 zero_length_string (lhs, si); 1004 return; 1005 } 1006 } 1007 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) 1008 return; 1009 if (TREE_CODE (src) != SSA_NAME || !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (src)) 1010 { 1011 if (idx == 0) 1012 idx = new_stridx (src); 1013 else if (get_strinfo (idx) != NULL) 1014 { 1015 zero_length_string (lhs, NULL); 1016 return; 1017 } 1018 if (idx) 1019 { 1020 location_t loc = gimple_location (stmt); 1021 tree lhsu = fold_convert_loc (loc, size_type_node, lhs); 1022 tree srcu = fold_convert_loc (loc, size_type_node, src); 1023 tree length = fold_build2_loc (loc, MINUS_EXPR, 1024 size_type_node, lhsu, srcu); 1025 strinfo si = new_strinfo (src, idx, length); 1026 si->endptr = lhs; 1027 set_strinfo (idx, si); 1028 find_equal_ptrs (src, idx); 1029 zero_length_string (lhs, si); 1030 } 1031 } 1032 else 1033 zero_length_string (lhs, NULL); 1034 } 1035 1036 /* Handle a strcpy-like ({st{r,p}cpy,__st{r,p}cpy_chk}) call. 1037 If strlen of the second argument is known, strlen of the first argument 1038 is the same after this call. Furthermore, attempt to convert it to 1039 memcpy. */ 1040 1041 static void 1042 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) 1043 { 1044 int idx, didx; 1045 tree src, dst, srclen, len, lhs, args, type, fn, oldlen; 1046 bool success; 1047 gimple stmt = gsi_stmt (*gsi); 1048 strinfo si, dsi, olddsi, zsi; 1049 location_t loc; 1050 1051 src = gimple_call_arg (stmt, 1); 1052 dst = gimple_call_arg (stmt, 0); 1053 lhs = gimple_call_lhs (stmt); 1054 idx = get_stridx (src); 1055 si = NULL; 1056 if (idx > 0) 1057 si = get_strinfo (idx); 1058 1059 didx = get_stridx (dst); 1060 olddsi = NULL; 1061 oldlen = NULL_TREE; 1062 if (didx > 0) 1063 olddsi = get_strinfo (didx); 1064 else if (didx < 0) 1065 return; 1066 1067 if (olddsi != NULL) 1068 adjust_last_stmt (olddsi, stmt, false); 1069 1070 srclen = NULL_TREE; 1071 if (si != NULL) 1072 srclen = get_string_length (si); 1073 else if (idx < 0) 1074 srclen = build_int_cst (size_type_node, ~idx); 1075 1076 loc = gimple_location (stmt); 1077 if (srclen == NULL_TREE) 1078 switch (bcode) 1079 { 1080 case BUILT_IN_STRCPY: 1081 case BUILT_IN_STRCPY_CHK: 1082 if (lhs != NULL_TREE || !builtin_decl_implicit_p (BUILT_IN_STPCPY)) 1083 return; 1084 break; 1085 case BUILT_IN_STPCPY: 1086 case BUILT_IN_STPCPY_CHK: 1087 if (lhs == NULL_TREE) 1088 return; 1089 else 1090 { 1091 tree lhsuint = fold_convert_loc (loc, size_type_node, lhs); 1092 srclen = fold_convert_loc (loc, size_type_node, dst); 1093 srclen = fold_build2_loc (loc, MINUS_EXPR, size_type_node, 1094 lhsuint, srclen); 1095 } 1096 break; 1097 default: 1098 gcc_unreachable (); 1099 } 1100 1101 if (didx == 0) 1102 { 1103 didx = new_stridx (dst); 1104 if (didx == 0) 1105 return; 1106 } 1107 if (olddsi != NULL) 1108 { 1109 oldlen = olddsi->length; 1110 dsi = unshare_strinfo (olddsi); 1111 dsi->length = srclen; 1112 /* Break the chain, so adjust_related_strinfo on later pointers in 1113 the chain won't adjust this one anymore. */ 1114 dsi->next = 0; 1115 dsi->stmt = NULL; 1116 dsi->endptr = NULL_TREE; 1117 } 1118 else 1119 { 1120 dsi = new_strinfo (dst, didx, srclen); 1121 set_strinfo (didx, dsi); 1122 find_equal_ptrs (dst, didx); 1123 } 1124 dsi->writable = true; 1125 dsi->dont_invalidate = true; 1126 1127 if (dsi->length == NULL_TREE) 1128 { 1129 strinfo chainsi; 1130 1131 /* If string length of src is unknown, use delayed length 1132 computation. If string lenth of dst will be needed, it 1133 can be computed by transforming this strcpy call into 1134 stpcpy and subtracting dst from the return value. */ 1135 1136 /* Look for earlier strings whose length could be determined if 1137 this strcpy is turned into an stpcpy. */ 1138 1139 if (dsi->prev != 0 && (chainsi = verify_related_strinfos (dsi)) != NULL) 1140 { 1141 for (; chainsi && chainsi != dsi; chainsi = get_strinfo (chainsi->next)) 1142 { 1143 /* When setting a stmt for delayed length computation 1144 prevent all strinfos through dsi from being 1145 invalidated. */ 1146 chainsi = unshare_strinfo (chainsi); 1147 chainsi->stmt = stmt; 1148 chainsi->length = NULL_TREE; 1149 chainsi->endptr = NULL_TREE; 1150 chainsi->dont_invalidate = true; 1151 } 1152 } 1153 dsi->stmt = stmt; 1154 return; 1155 } 1156 1157 if (olddsi != NULL) 1158 { 1159 tree adj = NULL_TREE; 1160 if (oldlen == NULL_TREE) 1161 ; 1162 else if (integer_zerop (oldlen)) 1163 adj = srclen; 1164 else if (TREE_CODE (oldlen) == INTEGER_CST 1165 || TREE_CODE (srclen) == INTEGER_CST) 1166 adj = fold_build2_loc (loc, MINUS_EXPR, 1167 TREE_TYPE (srclen), srclen, 1168 fold_convert_loc (loc, TREE_TYPE (srclen), 1169 oldlen)); 1170 if (adj != NULL_TREE) 1171 adjust_related_strinfos (loc, dsi, adj); 1172 else 1173 dsi->prev = 0; 1174 } 1175 /* strcpy src may not overlap dst, so src doesn't need to be 1176 invalidated either. */ 1177 if (si != NULL) 1178 si->dont_invalidate = true; 1179 1180 fn = NULL_TREE; 1181 zsi = NULL; 1182 switch (bcode) 1183 { 1184 case BUILT_IN_STRCPY: 1185 fn = builtin_decl_implicit (BUILT_IN_MEMCPY); 1186 if (lhs) 1187 ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; 1188 break; 1189 case BUILT_IN_STRCPY_CHK: 1190 fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); 1191 if (lhs) 1192 ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; 1193 break; 1194 case BUILT_IN_STPCPY: 1195 /* This would need adjustment of the lhs (subtract one), 1196 or detection that the trailing '\0' doesn't need to be 1197 written, if it will be immediately overwritten. 1198 fn = builtin_decl_explicit (BUILT_IN_MEMPCPY); */ 1199 if (lhs) 1200 { 1201 dsi->endptr = lhs; 1202 zsi = zero_length_string (lhs, dsi); 1203 } 1204 break; 1205 case BUILT_IN_STPCPY_CHK: 1206 /* This would need adjustment of the lhs (subtract one), 1207 or detection that the trailing '\0' doesn't need to be 1208 written, if it will be immediately overwritten. 1209 fn = builtin_decl_explicit (BUILT_IN_MEMPCPY_CHK); */ 1210 if (lhs) 1211 { 1212 dsi->endptr = lhs; 1213 zsi = zero_length_string (lhs, dsi); 1214 } 1215 break; 1216 default: 1217 gcc_unreachable (); 1218 } 1219 if (zsi != NULL) 1220 zsi->dont_invalidate = true; 1221 1222 if (fn == NULL_TREE) 1223 return; 1224 1225 args = TYPE_ARG_TYPES (TREE_TYPE (fn)); 1226 type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); 1227 1228 len = fold_convert_loc (loc, type, unshare_expr (srclen)); 1229 len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1)); 1230 len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true, 1231 GSI_SAME_STMT); 1232 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1233 { 1234 fprintf (dump_file, "Optimizing: "); 1235 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 1236 } 1237 if (gimple_call_num_args (stmt) == 2) 1238 success = update_gimple_call (gsi, fn, 3, dst, src, len); 1239 else 1240 success = update_gimple_call (gsi, fn, 4, dst, src, len, 1241 gimple_call_arg (stmt, 2)); 1242 if (success) 1243 { 1244 stmt = gsi_stmt (*gsi); 1245 update_stmt (stmt); 1246 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1247 { 1248 fprintf (dump_file, "into: "); 1249 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 1250 } 1251 /* Allow adjust_last_stmt to decrease this memcpy's size. */ 1252 laststmt.stmt = stmt; 1253 laststmt.len = srclen; 1254 laststmt.stridx = dsi->idx; 1255 } 1256 else if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1257 fprintf (dump_file, "not possible.\n"); 1258 } 1259 1260 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call. 1261 If strlen of the second argument is known and length of the third argument 1262 is that plus one, strlen of the first argument is the same after this 1263 call. */ 1264 1265 static void 1266 handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) 1267 { 1268 int idx, didx; 1269 tree src, dst, len, lhs, oldlen, newlen; 1270 gimple stmt = gsi_stmt (*gsi); 1271 strinfo si, dsi, olddsi; 1272 1273 len = gimple_call_arg (stmt, 2); 1274 src = gimple_call_arg (stmt, 1); 1275 dst = gimple_call_arg (stmt, 0); 1276 idx = get_stridx (src); 1277 if (idx == 0) 1278 return; 1279 1280 didx = get_stridx (dst); 1281 olddsi = NULL; 1282 if (didx > 0) 1283 olddsi = get_strinfo (didx); 1284 else if (didx < 0) 1285 return; 1286 1287 if (olddsi != NULL 1288 && host_integerp (len, 1) 1289 && !integer_zerop (len)) 1290 adjust_last_stmt (olddsi, stmt, false); 1291 1292 if (idx > 0) 1293 { 1294 gimple def_stmt; 1295 1296 /* Handle memcpy (x, y, l) where l is strlen (y) + 1. */ 1297 si = get_strinfo (idx); 1298 if (si == NULL || si->length == NULL_TREE) 1299 return; 1300 if (TREE_CODE (len) != SSA_NAME) 1301 return; 1302 def_stmt = SSA_NAME_DEF_STMT (len); 1303 if (!is_gimple_assign (def_stmt) 1304 || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR 1305 || gimple_assign_rhs1 (def_stmt) != si->length 1306 || !integer_onep (gimple_assign_rhs2 (def_stmt))) 1307 return; 1308 } 1309 else 1310 { 1311 si = NULL; 1312 /* Handle memcpy (x, "abcd", 5) or 1313 memcpy (x, "abc\0uvw", 7). */ 1314 if (!host_integerp (len, 1) 1315 || (unsigned HOST_WIDE_INT) tree_low_cst (len, 1) 1316 <= (unsigned HOST_WIDE_INT) ~idx) 1317 return; 1318 } 1319 1320 if (olddsi != NULL && TREE_CODE (len) == SSA_NAME) 1321 adjust_last_stmt (olddsi, stmt, false); 1322 1323 if (didx == 0) 1324 { 1325 didx = new_stridx (dst); 1326 if (didx == 0) 1327 return; 1328 } 1329 if (si != NULL) 1330 newlen = si->length; 1331 else 1332 newlen = build_int_cst (size_type_node, ~idx); 1333 oldlen = NULL_TREE; 1334 if (olddsi != NULL) 1335 { 1336 dsi = unshare_strinfo (olddsi); 1337 oldlen = olddsi->length; 1338 dsi->length = newlen; 1339 /* Break the chain, so adjust_related_strinfo on later pointers in 1340 the chain won't adjust this one anymore. */ 1341 dsi->next = 0; 1342 dsi->stmt = NULL; 1343 dsi->endptr = NULL_TREE; 1344 } 1345 else 1346 { 1347 dsi = new_strinfo (dst, didx, newlen); 1348 set_strinfo (didx, dsi); 1349 find_equal_ptrs (dst, didx); 1350 } 1351 dsi->writable = true; 1352 dsi->dont_invalidate = true; 1353 if (olddsi != NULL) 1354 { 1355 tree adj = NULL_TREE; 1356 location_t loc = gimple_location (stmt); 1357 if (oldlen == NULL_TREE) 1358 ; 1359 else if (integer_zerop (oldlen)) 1360 adj = dsi->length; 1361 else if (TREE_CODE (oldlen) == INTEGER_CST 1362 || TREE_CODE (dsi->length) == INTEGER_CST) 1363 adj = fold_build2_loc (loc, MINUS_EXPR, 1364 TREE_TYPE (dsi->length), dsi->length, 1365 fold_convert_loc (loc, TREE_TYPE (dsi->length), 1366 oldlen)); 1367 if (adj != NULL_TREE) 1368 adjust_related_strinfos (loc, dsi, adj); 1369 else 1370 dsi->prev = 0; 1371 } 1372 /* memcpy src may not overlap dst, so src doesn't need to be 1373 invalidated either. */ 1374 if (si != NULL) 1375 si->dont_invalidate = true; 1376 1377 lhs = gimple_call_lhs (stmt); 1378 switch (bcode) 1379 { 1380 case BUILT_IN_MEMCPY: 1381 case BUILT_IN_MEMCPY_CHK: 1382 /* Allow adjust_last_stmt to decrease this memcpy's size. */ 1383 laststmt.stmt = stmt; 1384 laststmt.len = dsi->length; 1385 laststmt.stridx = dsi->idx; 1386 if (lhs) 1387 ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; 1388 break; 1389 case BUILT_IN_MEMPCPY: 1390 case BUILT_IN_MEMPCPY_CHK: 1391 break; 1392 default: 1393 gcc_unreachable (); 1394 } 1395 } 1396 1397 /* Handle a strcat-like ({strcat,__strcat_chk}) call. 1398 If strlen of the second argument is known, strlen of the first argument 1399 is increased by the length of the second argument. Furthermore, attempt 1400 to convert it to memcpy/strcpy if the length of the first argument 1401 is known. */ 1402 1403 static void 1404 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) 1405 { 1406 int idx, didx; 1407 tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr; 1408 bool success; 1409 gimple stmt = gsi_stmt (*gsi); 1410 strinfo si, dsi; 1411 location_t loc; 1412 1413 src = gimple_call_arg (stmt, 1); 1414 dst = gimple_call_arg (stmt, 0); 1415 lhs = gimple_call_lhs (stmt); 1416 1417 didx = get_stridx (dst); 1418 if (didx < 0) 1419 return; 1420 1421 dsi = NULL; 1422 if (didx > 0) 1423 dsi = get_strinfo (didx); 1424 if (dsi == NULL || get_string_length (dsi) == NULL_TREE) 1425 { 1426 /* strcat (p, q) can be transformed into 1427 tmp = p + strlen (p); endptr = strpcpy (tmp, q); 1428 with length endptr - p if we need to compute the length 1429 later on. Don't do this transformation if we don't need 1430 it. */ 1431 if (builtin_decl_implicit_p (BUILT_IN_STPCPY) && lhs == NULL_TREE) 1432 { 1433 if (didx == 0) 1434 { 1435 didx = new_stridx (dst); 1436 if (didx == 0) 1437 return; 1438 } 1439 if (dsi == NULL) 1440 { 1441 dsi = new_strinfo (dst, didx, NULL_TREE); 1442 set_strinfo (didx, dsi); 1443 find_equal_ptrs (dst, didx); 1444 } 1445 else 1446 { 1447 dsi = unshare_strinfo (dsi); 1448 dsi->length = NULL_TREE; 1449 dsi->next = 0; 1450 dsi->endptr = NULL_TREE; 1451 } 1452 dsi->writable = true; 1453 dsi->stmt = stmt; 1454 dsi->dont_invalidate = true; 1455 } 1456 return; 1457 } 1458 1459 srclen = NULL_TREE; 1460 si = NULL; 1461 idx = get_stridx (src); 1462 if (idx < 0) 1463 srclen = build_int_cst (size_type_node, ~idx); 1464 else if (idx > 0) 1465 { 1466 si = get_strinfo (idx); 1467 if (si != NULL) 1468 srclen = get_string_length (si); 1469 } 1470 1471 loc = gimple_location (stmt); 1472 dstlen = dsi->length; 1473 endptr = dsi->endptr; 1474 1475 dsi = unshare_strinfo (dsi); 1476 dsi->endptr = NULL_TREE; 1477 dsi->stmt = NULL; 1478 dsi->writable = true; 1479 1480 if (srclen != NULL_TREE) 1481 { 1482 dsi->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (dsi->length), 1483 dsi->length, srclen); 1484 adjust_related_strinfos (loc, dsi, srclen); 1485 dsi->dont_invalidate = true; 1486 } 1487 else 1488 { 1489 dsi->length = NULL; 1490 if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY)) 1491 dsi->dont_invalidate = true; 1492 } 1493 1494 if (si != NULL) 1495 /* strcat src may not overlap dst, so src doesn't need to be 1496 invalidated either. */ 1497 si->dont_invalidate = true; 1498 1499 /* For now. Could remove the lhs from the call and add 1500 lhs = dst; afterwards. */ 1501 if (lhs) 1502 return; 1503 1504 fn = NULL_TREE; 1505 objsz = NULL_TREE; 1506 switch (bcode) 1507 { 1508 case BUILT_IN_STRCAT: 1509 if (srclen != NULL_TREE) 1510 fn = builtin_decl_implicit (BUILT_IN_MEMCPY); 1511 else 1512 fn = builtin_decl_implicit (BUILT_IN_STRCPY); 1513 break; 1514 case BUILT_IN_STRCAT_CHK: 1515 if (srclen != NULL_TREE) 1516 fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); 1517 else 1518 fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK); 1519 objsz = gimple_call_arg (stmt, 2); 1520 break; 1521 default: 1522 gcc_unreachable (); 1523 } 1524 1525 if (fn == NULL_TREE) 1526 return; 1527 1528 len = NULL_TREE; 1529 if (srclen != NULL_TREE) 1530 { 1531 args = TYPE_ARG_TYPES (TREE_TYPE (fn)); 1532 type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); 1533 1534 len = fold_convert_loc (loc, type, unshare_expr (srclen)); 1535 len = fold_build2_loc (loc, PLUS_EXPR, type, len, 1536 build_int_cst (type, 1)); 1537 len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true, 1538 GSI_SAME_STMT); 1539 } 1540 if (endptr) 1541 dst = fold_convert_loc (loc, TREE_TYPE (dst), unshare_expr (endptr)); 1542 else 1543 dst = fold_build2_loc (loc, POINTER_PLUS_EXPR, 1544 TREE_TYPE (dst), unshare_expr (dst), 1545 fold_convert_loc (loc, sizetype, 1546 unshare_expr (dstlen))); 1547 dst = force_gimple_operand_gsi (gsi, dst, true, NULL_TREE, true, 1548 GSI_SAME_STMT); 1549 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1550 { 1551 fprintf (dump_file, "Optimizing: "); 1552 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 1553 } 1554 if (srclen != NULL_TREE) 1555 success = update_gimple_call (gsi, fn, 3 + (objsz != NULL_TREE), 1556 dst, src, len, objsz); 1557 else 1558 success = update_gimple_call (gsi, fn, 2 + (objsz != NULL_TREE), 1559 dst, src, objsz); 1560 if (success) 1561 { 1562 stmt = gsi_stmt (*gsi); 1563 update_stmt (stmt); 1564 if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1565 { 1566 fprintf (dump_file, "into: "); 1567 print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); 1568 } 1569 /* If srclen == NULL, note that current string length can be 1570 computed by transforming this strcpy into stpcpy. */ 1571 if (srclen == NULL_TREE && dsi->dont_invalidate) 1572 dsi->stmt = stmt; 1573 adjust_last_stmt (dsi, stmt, true); 1574 if (srclen != NULL_TREE) 1575 { 1576 laststmt.stmt = stmt; 1577 laststmt.len = srclen; 1578 laststmt.stridx = dsi->idx; 1579 } 1580 } 1581 else if (dump_file && (dump_flags & TDF_DETAILS) != 0) 1582 fprintf (dump_file, "not possible.\n"); 1583 } 1584 1585 /* Handle a POINTER_PLUS_EXPR statement. 1586 For p = "abcd" + 2; compute associated length, or if 1587 p = q + off is pointing to a '\0' character of a string, call 1588 zero_length_string on it. */ 1589 1590 static void 1591 handle_pointer_plus (gimple_stmt_iterator *gsi) 1592 { 1593 gimple stmt = gsi_stmt (*gsi); 1594 tree lhs = gimple_assign_lhs (stmt), off; 1595 int idx = get_stridx (gimple_assign_rhs1 (stmt)); 1596 strinfo si, zsi; 1597 1598 if (idx == 0) 1599 return; 1600 1601 if (idx < 0) 1602 { 1603 tree off = gimple_assign_rhs2 (stmt); 1604 if (host_integerp (off, 1) 1605 && (unsigned HOST_WIDE_INT) tree_low_cst (off, 1) 1606 <= (unsigned HOST_WIDE_INT) ~idx) 1607 ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] 1608 = ~(~idx - (int) tree_low_cst (off, 1)); 1609 return; 1610 } 1611 1612 si = get_strinfo (idx); 1613 if (si == NULL || si->length == NULL_TREE) 1614 return; 1615 1616 off = gimple_assign_rhs2 (stmt); 1617 zsi = NULL; 1618 if (operand_equal_p (si->length, off, 0)) 1619 zsi = zero_length_string (lhs, si); 1620 else if (TREE_CODE (off) == SSA_NAME) 1621 { 1622 gimple def_stmt = SSA_NAME_DEF_STMT (off); 1623 if (gimple_assign_single_p (def_stmt) 1624 && operand_equal_p (si->length, gimple_assign_rhs1 (def_stmt), 0)) 1625 zsi = zero_length_string (lhs, si); 1626 } 1627 if (zsi != NULL 1628 && si->endptr != NULL_TREE 1629 && si->endptr != lhs 1630 && TREE_CODE (si->endptr) == SSA_NAME) 1631 { 1632 enum tree_code rhs_code 1633 = useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (si->endptr)) 1634 ? SSA_NAME : NOP_EXPR; 1635 gimple_assign_set_rhs_with_ops (gsi, rhs_code, si->endptr, NULL_TREE); 1636 gcc_assert (gsi_stmt (*gsi) == stmt); 1637 update_stmt (stmt); 1638 } 1639 } 1640 1641 /* Handle a single character store. */ 1642 1643 static bool 1644 handle_char_store (gimple_stmt_iterator *gsi) 1645 { 1646 int idx = -1; 1647 strinfo si = NULL; 1648 gimple stmt = gsi_stmt (*gsi); 1649 tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt); 1650 1651 if (TREE_CODE (lhs) == MEM_REF 1652 && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME) 1653 { 1654 if (integer_zerop (TREE_OPERAND (lhs, 1))) 1655 { 1656 ssaname = TREE_OPERAND (lhs, 0); 1657 idx = get_stridx (ssaname); 1658 } 1659 } 1660 else 1661 idx = get_addr_stridx (lhs); 1662 1663 if (idx > 0) 1664 { 1665 si = get_strinfo (idx); 1666 if (si != NULL && si->length != NULL_TREE && integer_zerop (si->length)) 1667 { 1668 if (initializer_zerop (gimple_assign_rhs1 (stmt))) 1669 { 1670 /* When storing '\0', the store can be removed 1671 if we know it has been stored in the current function. */ 1672 if (!stmt_could_throw_p (stmt) && si->writable) 1673 { 1674 unlink_stmt_vdef (stmt); 1675 release_defs (stmt); 1676 gsi_remove (gsi, true); 1677 return false; 1678 } 1679 else 1680 { 1681 si->writable = true; 1682 si->dont_invalidate = true; 1683 } 1684 } 1685 else 1686 /* Otherwise this statement overwrites the '\0' with 1687 something, if the previous stmt was a memcpy, 1688 its length may be decreased. */ 1689 adjust_last_stmt (si, stmt, false); 1690 } 1691 else if (si != NULL && integer_zerop (gimple_assign_rhs1 (stmt))) 1692 { 1693 si = unshare_strinfo (si); 1694 si->length = build_int_cst (size_type_node, 0); 1695 si->endptr = NULL; 1696 si->prev = 0; 1697 si->next = 0; 1698 si->stmt = NULL; 1699 si->first = 0; 1700 si->writable = true; 1701 if (ssaname && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname)) 1702 si->endptr = ssaname; 1703 si->dont_invalidate = true; 1704 } 1705 } 1706 else if (idx == 0 && initializer_zerop (gimple_assign_rhs1 (stmt))) 1707 { 1708 if (ssaname) 1709 { 1710 si = zero_length_string (ssaname, NULL); 1711 if (si != NULL) 1712 si->dont_invalidate = true; 1713 } 1714 else 1715 { 1716 int idx = new_addr_stridx (lhs); 1717 if (idx != 0) 1718 { 1719 si = new_strinfo (build_fold_addr_expr (lhs), idx, 1720 build_int_cst (size_type_node, 0)); 1721 set_strinfo (idx, si); 1722 si->dont_invalidate = true; 1723 } 1724 } 1725 if (si != NULL) 1726 si->writable = true; 1727 } 1728 1729 if (si != NULL && initializer_zerop (gimple_assign_rhs1 (stmt))) 1730 { 1731 /* Allow adjust_last_stmt to remove it if the stored '\0' 1732 is immediately overwritten. */ 1733 laststmt.stmt = stmt; 1734 laststmt.len = build_int_cst (size_type_node, 1); 1735 laststmt.stridx = si->idx; 1736 } 1737 return true; 1738 } 1739 1740 /* Attempt to optimize a single statement at *GSI using string length 1741 knowledge. */ 1742 1743 static bool 1744 strlen_optimize_stmt (gimple_stmt_iterator *gsi) 1745 { 1746 gimple stmt = gsi_stmt (*gsi); 1747 1748 if (is_gimple_call (stmt)) 1749 { 1750 tree callee = gimple_call_fndecl (stmt); 1751 if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) 1752 switch (DECL_FUNCTION_CODE (callee)) 1753 { 1754 case BUILT_IN_STRLEN: 1755 handle_builtin_strlen (gsi); 1756 break; 1757 case BUILT_IN_STRCHR: 1758 handle_builtin_strchr (gsi); 1759 break; 1760 case BUILT_IN_STRCPY: 1761 case BUILT_IN_STRCPY_CHK: 1762 case BUILT_IN_STPCPY: 1763 case BUILT_IN_STPCPY_CHK: 1764 handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi); 1765 break; 1766 case BUILT_IN_MEMCPY: 1767 case BUILT_IN_MEMCPY_CHK: 1768 case BUILT_IN_MEMPCPY: 1769 case BUILT_IN_MEMPCPY_CHK: 1770 handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi); 1771 break; 1772 case BUILT_IN_STRCAT: 1773 case BUILT_IN_STRCAT_CHK: 1774 handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi); 1775 break; 1776 default: 1777 break; 1778 } 1779 } 1780 else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt)) 1781 { 1782 tree lhs = gimple_assign_lhs (stmt); 1783 1784 if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (lhs))) 1785 { 1786 if (gimple_assign_single_p (stmt) 1787 || (gimple_assign_cast_p (stmt) 1788 && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))) 1789 { 1790 int idx = get_stridx (gimple_assign_rhs1 (stmt)); 1791 ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = idx; 1792 } 1793 else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) 1794 handle_pointer_plus (gsi); 1795 } 1796 else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs)) 1797 { 1798 tree type = TREE_TYPE (lhs); 1799 if (TREE_CODE (type) == ARRAY_TYPE) 1800 type = TREE_TYPE (type); 1801 if (TREE_CODE (type) == INTEGER_TYPE 1802 && TYPE_MODE (type) == TYPE_MODE (char_type_node) 1803 && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node)) 1804 { 1805 if (! handle_char_store (gsi)) 1806 return false; 1807 } 1808 } 1809 } 1810 1811 if (gimple_vdef (stmt)) 1812 maybe_invalidate (stmt); 1813 return true; 1814 } 1815 1816 /* Recursively call maybe_invalidate on stmts that might be executed 1817 in between dombb and current bb and that contain a vdef. Stop when 1818 *count stmts are inspected, or if the whole strinfo vector has 1819 been invalidated. */ 1820 1821 static void 1822 do_invalidate (basic_block dombb, gimple phi, bitmap visited, int *count) 1823 { 1824 unsigned int i, n = gimple_phi_num_args (phi); 1825 1826 for (i = 0; i < n; i++) 1827 { 1828 tree vuse = gimple_phi_arg_def (phi, i); 1829 gimple stmt = SSA_NAME_DEF_STMT (vuse); 1830 basic_block bb = gimple_bb (stmt); 1831 if (bb == NULL 1832 || bb == dombb 1833 || !bitmap_set_bit (visited, bb->index) 1834 || !dominated_by_p (CDI_DOMINATORS, bb, dombb)) 1835 continue; 1836 while (1) 1837 { 1838 if (gimple_code (stmt) == GIMPLE_PHI) 1839 { 1840 do_invalidate (dombb, stmt, visited, count); 1841 if (*count == 0) 1842 return; 1843 break; 1844 } 1845 if (--*count == 0) 1846 return; 1847 if (!maybe_invalidate (stmt)) 1848 { 1849 *count = 0; 1850 return; 1851 } 1852 vuse = gimple_vuse (stmt); 1853 stmt = SSA_NAME_DEF_STMT (vuse); 1854 if (gimple_bb (stmt) != bb) 1855 { 1856 bb = gimple_bb (stmt); 1857 if (bb == NULL 1858 || bb == dombb 1859 || !bitmap_set_bit (visited, bb->index) 1860 || !dominated_by_p (CDI_DOMINATORS, bb, dombb)) 1861 break; 1862 } 1863 } 1864 } 1865 } 1866 1867 /* Callback for walk_dominator_tree. Attempt to optimize various 1868 string ops by remembering string lenths pointed by pointer SSA_NAMEs. */ 1869 1870 static void 1871 strlen_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED, 1872 basic_block bb) 1873 { 1874 gimple_stmt_iterator gsi; 1875 basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb); 1876 1877 if (dombb == NULL) 1878 stridx_to_strinfo = NULL; 1879 else 1880 { 1881 stridx_to_strinfo = ((vec<strinfo, va_heap, vl_embed> *) dombb->aux); 1882 if (stridx_to_strinfo) 1883 { 1884 for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) 1885 { 1886 gimple phi = gsi_stmt (gsi); 1887 if (virtual_operand_p (gimple_phi_result (phi))) 1888 { 1889 bitmap visited = BITMAP_ALLOC (NULL); 1890 int count_vdef = 100; 1891 do_invalidate (dombb, phi, visited, &count_vdef); 1892 BITMAP_FREE (visited); 1893 if (count_vdef == 0) 1894 { 1895 /* If there were too many vdefs in between immediate 1896 dominator and current bb, invalidate everything. 1897 If stridx_to_strinfo has been unshared, we need 1898 to free it, otherwise just set it to NULL. */ 1899 if (!strinfo_shared ()) 1900 { 1901 unsigned int i; 1902 strinfo si; 1903 1904 for (i = 1; 1905 vec_safe_iterate (stridx_to_strinfo, i, &si); 1906 ++i) 1907 { 1908 free_strinfo (si); 1909 (*stridx_to_strinfo)[i] = NULL; 1910 } 1911 } 1912 else 1913 stridx_to_strinfo = NULL; 1914 } 1915 break; 1916 } 1917 } 1918 } 1919 } 1920 1921 /* If all PHI arguments have the same string index, the PHI result 1922 has it as well. */ 1923 for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) 1924 { 1925 gimple phi = gsi_stmt (gsi); 1926 tree result = gimple_phi_result (phi); 1927 if (!virtual_operand_p (result) && POINTER_TYPE_P (TREE_TYPE (result))) 1928 { 1929 int idx = get_stridx (gimple_phi_arg_def (phi, 0)); 1930 if (idx != 0) 1931 { 1932 unsigned int i, n = gimple_phi_num_args (phi); 1933 for (i = 1; i < n; i++) 1934 if (idx != get_stridx (gimple_phi_arg_def (phi, i))) 1935 break; 1936 if (i == n) 1937 ssa_ver_to_stridx[SSA_NAME_VERSION (result)] = idx; 1938 } 1939 } 1940 } 1941 1942 /* Attempt to optimize individual statements. */ 1943 for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ) 1944 if (strlen_optimize_stmt (&gsi)) 1945 gsi_next (&gsi); 1946 1947 bb->aux = stridx_to_strinfo; 1948 if (vec_safe_length (stridx_to_strinfo) && !strinfo_shared ()) 1949 (*stridx_to_strinfo)[0] = (strinfo) bb; 1950 } 1951 1952 /* Callback for walk_dominator_tree. Free strinfo vector if it is 1953 owned by the current bb, clear bb->aux. */ 1954 1955 static void 1956 strlen_leave_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED, 1957 basic_block bb) 1958 { 1959 if (bb->aux) 1960 { 1961 stridx_to_strinfo = ((vec<strinfo, va_heap, vl_embed> *) bb->aux); 1962 if (vec_safe_length (stridx_to_strinfo) 1963 && (*stridx_to_strinfo)[0] == (strinfo) bb) 1964 { 1965 unsigned int i; 1966 strinfo si; 1967 1968 for (i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i) 1969 free_strinfo (si); 1970 vec_free (stridx_to_strinfo); 1971 } 1972 bb->aux = NULL; 1973 } 1974 } 1975 1976 /* Main entry point. */ 1977 1978 static unsigned int 1979 tree_ssa_strlen (void) 1980 { 1981 struct dom_walk_data walk_data; 1982 1983 ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names); 1984 max_stridx = 1; 1985 strinfo_pool = create_alloc_pool ("strinfo_struct pool", 1986 sizeof (struct strinfo_struct), 64); 1987 1988 calculate_dominance_info (CDI_DOMINATORS); 1989 1990 /* String length optimization is implemented as a walk of the dominator 1991 tree and a forward walk of statements within each block. */ 1992 walk_data.dom_direction = CDI_DOMINATORS; 1993 walk_data.initialize_block_local_data = NULL; 1994 walk_data.before_dom_children = strlen_enter_block; 1995 walk_data.after_dom_children = strlen_leave_block; 1996 walk_data.block_local_data_size = 0; 1997 walk_data.global_data = NULL; 1998 1999 /* Initialize the dominator walker. */ 2000 init_walk_dominator_tree (&walk_data); 2001 2002 /* Recursively walk the dominator tree. */ 2003 walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR); 2004 2005 /* Finalize the dominator walker. */ 2006 fini_walk_dominator_tree (&walk_data); 2007 2008 ssa_ver_to_stridx.release (); 2009 free_alloc_pool (strinfo_pool); 2010 if (decl_to_stridxlist_htab) 2011 { 2012 obstack_free (&stridx_obstack, NULL); 2013 htab_delete (decl_to_stridxlist_htab); 2014 decl_to_stridxlist_htab = NULL; 2015 } 2016 laststmt.stmt = NULL; 2017 laststmt.len = NULL_TREE; 2018 laststmt.stridx = 0; 2019 2020 return 0; 2021 } 2022 2023 static bool 2024 gate_strlen (void) 2025 { 2026 return flag_optimize_strlen != 0; 2027 } 2028 2029 struct gimple_opt_pass pass_strlen = 2030 { 2031 { 2032 GIMPLE_PASS, 2033 "strlen", /* name */ 2034 OPTGROUP_NONE, /* optinfo_flags */ 2035 gate_strlen, /* gate */ 2036 tree_ssa_strlen, /* execute */ 2037 NULL, /* sub */ 2038 NULL, /* next */ 2039 0, /* static_pass_number */ 2040 TV_TREE_STRLEN, /* tv_id */ 2041 PROP_cfg | PROP_ssa, /* properties_required */ 2042 0, /* properties_provided */ 2043 0, /* properties_destroyed */ 2044 0, /* todo_flags_start */ 2045 TODO_ggc_collect 2046 | TODO_verify_ssa /* todo_flags_finish */ 2047 } 2048 }; 2049