OSDN Git Service

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