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