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