1 /* Callgraph based analysis of static variables.
2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3 Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING. If not, write to the Free
19 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA. */
21
22 /* This file mark functions as being either const (TREE_READONLY) or
23 pure (DECL_IS_PURE).
24
25 This must be run after inlining decisions have been made since
26 otherwise, the local sets will not contain information that is
27 consistent with post inlined state. The global sets are not prone
28 to this problem since they are by definition transitive. */
29
30 /* The code in this module is called by the ipa pass manager. It
31 should be one of the later passes since it's information is used by
32 the rest of the compilation. */
33
34 #include "config.h"
35 #include "system.h"
36 #include "coretypes.h"
37 #include "tm.h"
38 #include "tree.h"
39 #include "tree-flow.h"
40 #include "tree-inline.h"
41 #include "tree-pass.h"
42 #include "langhooks.h"
43 #include "pointer-set.h"
44 #include "ggc.h"
45 #include "ipa-utils.h"
46 #include "c-common.h"
47 #include "tree-gimple.h"
48 #include "cgraph.h"
49 #include "output.h"
50 #include "flags.h"
51 #include "timevar.h"
52 #include "diagnostic.h"
53 #include "langhooks.h"
54 #include "target.h"
55
56 static struct pointer_set_t *visited_nodes;
57
58 /* Lattice values for const and pure functions. Everything starts out
59 being const, then may drop to pure and then neither depending on
60 what is found. */
61 enum pure_const_state_e
62 {
63 IPA_CONST,
64 IPA_PURE,
65 IPA_NEITHER
66 };
67
68 /* Holder inserted into the ipa_dfs_info aux field to hold the
69 const_state. */
70 struct funct_state_d
71 {
72 enum pure_const_state_e pure_const_state;
73 bool state_set_in_source;
74 };
75
76 typedef struct funct_state_d * funct_state;
77
78 /* Return the function state from NODE. */
79
80 static inline funct_state
get_function_state(struct cgraph_node * node)81 get_function_state (struct cgraph_node *node)
82 {
83 struct ipa_dfs_info * info = node->aux;
84 return info->aux;
85 }
86
87 /* Check to see if the use (or definition when CHECHING_WRITE is true)
88 variable T is legal in a function that is either pure or const. */
89
90 static inline void
check_decl(funct_state local,tree t,bool checking_write)91 check_decl (funct_state local,
92 tree t, bool checking_write)
93 {
94 /* If the variable has the "used" attribute, treat it as if it had a
95 been touched by the devil. */
96 if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
97 {
98 local->pure_const_state = IPA_NEITHER;
99 return;
100 }
101
102 /* Do not want to do anything with volatile except mark any
103 function that uses one to be not const or pure. */
104 if (TREE_THIS_VOLATILE (t))
105 {
106 local->pure_const_state = IPA_NEITHER;
107 return;
108 }
109
110 /* Do not care about a local automatic that is not static. */
111 if (!TREE_STATIC (t) && !DECL_EXTERNAL (t))
112 return;
113
114 /* Since we have dealt with the locals and params cases above, if we
115 are CHECKING_WRITE, this cannot be a pure or constant
116 function. */
117 if (checking_write)
118 local->pure_const_state = IPA_NEITHER;
119
120 if (DECL_EXTERNAL (t) || TREE_PUBLIC (t))
121 {
122 /* If the front end set the variable to be READONLY and
123 constant, we can allow this variable in pure or const
124 functions but the scope is too large for our analysis to set
125 these bits ourselves. */
126
127 if (TREE_READONLY (t)
128 && DECL_INITIAL (t)
129 && is_gimple_min_invariant (DECL_INITIAL (t)))
130 ; /* Read of a constant, do not change the function state. */
131 else
132 {
133 /* Just a regular read. */
134 if (local->pure_const_state == IPA_CONST)
135 local->pure_const_state = IPA_PURE;
136 }
137 }
138
139 /* Compilation level statics can be read if they are readonly
140 variables. */
141 if (TREE_READONLY (t))
142 return;
143
144 /* Just a regular read. */
145 if (local->pure_const_state == IPA_CONST)
146 local->pure_const_state = IPA_PURE;
147 }
148
149 /* If T is a VAR_DECL check to see if it is an allowed reference. */
150
151 static void
check_operand(funct_state local,tree t,bool checking_write)152 check_operand (funct_state local,
153 tree t, bool checking_write)
154 {
155 if (!t) return;
156
157 if (TREE_CODE (t) == VAR_DECL)
158 check_decl (local, t, checking_write);
159 }
160
161 /* Examine tree T for references. */
162
163 static void
check_tree(funct_state local,tree t,bool checking_write)164 check_tree (funct_state local, tree t, bool checking_write)
165 {
166 if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
167 return;
168
169 /* Any tree which is volatile disqualifies thie function from being
170 const or pure. */
171 if (TREE_THIS_VOLATILE (t))
172 {
173 local->pure_const_state = IPA_NEITHER;
174 return;
175 }
176
177 while (TREE_CODE (t) == REALPART_EXPR
178 || TREE_CODE (t) == IMAGPART_EXPR
179 || handled_component_p (t))
180 {
181 if (TREE_CODE (t) == ARRAY_REF)
182 check_operand (local, TREE_OPERAND (t, 1), false);
183 t = TREE_OPERAND (t, 0);
184 }
185
186 /* The bottom of an indirect reference can only be read, not
187 written. */
188 if (INDIRECT_REF_P (t))
189 {
190 check_tree (local, TREE_OPERAND (t, 0), false);
191
192 /* Any indirect reference that occurs on the lhs
193 disqualifies the function from being pure or const. Any
194 indirect reference that occurs on the rhs disqualifies the
195 function from being const. */
196 if (checking_write)
197 {
198 local->pure_const_state = IPA_NEITHER;
199 return;
200 }
201 else if (local->pure_const_state == IPA_CONST)
202 local->pure_const_state = IPA_PURE;
203 }
204
205 if (SSA_VAR_P (t))
206 check_operand (local, t, checking_write);
207 }
208
209 /* Scan tree T to see if there are any addresses taken in within T. */
210
211 static void
look_for_address_of(funct_state local,tree t)212 look_for_address_of (funct_state local, tree t)
213 {
214 if (TREE_CODE (t) == ADDR_EXPR)
215 {
216 tree x = get_base_var (t);
217 if (TREE_CODE (x) == VAR_DECL)
218 {
219 check_decl (local, x, false);
220
221 /* Taking the address of something appears to be reasonable
222 in PURE code. Not allowed in const. */
223 if (local->pure_const_state == IPA_CONST)
224 local->pure_const_state = IPA_PURE;
225 }
226 }
227 }
228
229 /* Check to see if T is a read or address of operation on a var we are
230 interested in analyzing. LOCAL is passed in to get access to its
231 bit vectors. */
232
233 static void
check_rhs_var(funct_state local,tree t)234 check_rhs_var (funct_state local, tree t)
235 {
236 look_for_address_of (local, t);
237
238 /* Memcmp and strlen can both trap and they are declared pure. */
239 if (tree_could_trap_p (t)
240 && local->pure_const_state == IPA_CONST)
241 local->pure_const_state = IPA_PURE;
242
243 check_tree(local, t, false);
244 }
245
246 /* Check to see if T is an assignment to a var we are interested in
247 analyzing. LOCAL is passed in to get access to its bit vectors. */
248
249 static void
check_lhs_var(funct_state local,tree t)250 check_lhs_var (funct_state local, tree t)
251 {
252 /* Memcmp and strlen can both trap and they are declared pure.
253 Which seems to imply that we can apply the same rule here. */
254 if (tree_could_trap_p (t)
255 && local->pure_const_state == IPA_CONST)
256 local->pure_const_state = IPA_PURE;
257
258 check_tree(local, t, true);
259 }
260
261 /* This is a scaled down version of get_asm_expr_operands from
262 tree_ssa_operands.c. The version there runs much later and assumes
263 that aliasing information is already available. Here we are just
264 trying to find if the set of inputs and outputs contain references
265 or address of operations to local static variables. STMT is the
266 actual asm statement. */
267
268 static void
get_asm_expr_operands(funct_state local,tree stmt)269 get_asm_expr_operands (funct_state local, tree stmt)
270 {
271 int noutputs = list_length (ASM_OUTPUTS (stmt));
272 const char **oconstraints
273 = (const char **) alloca ((noutputs) * sizeof (const char *));
274 int i;
275 tree link;
276 const char *constraint;
277 bool allows_mem, allows_reg, is_inout;
278
279 for (i=0, link = ASM_OUTPUTS (stmt); link; ++i, link = TREE_CHAIN (link))
280 {
281 oconstraints[i] = constraint
282 = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
283 parse_output_constraint (&constraint, i, 0, 0,
284 &allows_mem, &allows_reg, &is_inout);
285
286 check_lhs_var (local, TREE_VALUE (link));
287 }
288
289 for (link = ASM_INPUTS (stmt); link; link = TREE_CHAIN (link))
290 {
291 constraint
292 = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
293 parse_input_constraint (&constraint, 0, 0, noutputs, 0,
294 oconstraints, &allows_mem, &allows_reg);
295
296 check_rhs_var (local, TREE_VALUE (link));
297 }
298
299 for (link = ASM_CLOBBERS (stmt); link; link = TREE_CHAIN (link))
300 if (simple_cst_equal(TREE_VALUE (link), memory_identifier_string) == 1)
301 /* Abandon all hope, ye who enter here. */
302 local->pure_const_state = IPA_NEITHER;
303
304 if (ASM_VOLATILE_P (stmt))
305 local->pure_const_state = IPA_NEITHER;
306 }
307
308 /* Check the parameters of a function call to CALL_EXPR to see if
309 there are any references in the parameters that are not allowed for
310 pure or const functions. Also check to see if this is either an
311 indirect call, a call outside the compilation unit, or has special
312 attributes that may also effect the purity. The CALL_EXPR node for
313 the entire call expression. */
314
315 static void
check_call(funct_state local,tree call_expr)316 check_call (funct_state local, tree call_expr)
317 {
318 int flags = call_expr_flags(call_expr);
319 tree operand_list = TREE_OPERAND (call_expr, 1);
320 tree operand;
321 tree callee_t = get_callee_fndecl (call_expr);
322 struct cgraph_node* callee;
323 enum availability avail = AVAIL_NOT_AVAILABLE;
324
325 for (operand = operand_list;
326 operand != NULL_TREE;
327 operand = TREE_CHAIN (operand))
328 {
329 tree argument = TREE_VALUE (operand);
330 check_rhs_var (local, argument);
331 }
332
333 /* The const and pure flags are set by a variety of places in the
334 compiler (including here). If someone has already set the flags
335 for the callee, (such as for some of the builtins) we will use
336 them, otherwise we will compute our own information.
337
338 Const and pure functions have less clobber effects than other
339 functions so we process these first. Otherwise if it is a call
340 outside the compilation unit or an indirect call we punt. This
341 leaves local calls which will be processed by following the call
342 graph. */
343 if (callee_t)
344 {
345 callee = cgraph_node(callee_t);
346 avail = cgraph_function_body_availability (callee);
347
348 /* When bad things happen to bad functions, they cannot be const
349 or pure. */
350 if (setjmp_call_p (callee_t))
351 local->pure_const_state = IPA_NEITHER;
352
353 if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
354 switch (DECL_FUNCTION_CODE (callee_t))
355 {
356 case BUILT_IN_LONGJMP:
357 case BUILT_IN_NONLOCAL_GOTO:
358 local->pure_const_state = IPA_NEITHER;
359 break;
360 default:
361 break;
362 }
363 }
364
365 /* The callee is either unknown (indirect call) or there is just no
366 scannable code for it (external call) . We look to see if there
367 are any bits available for the callee (such as by declaration or
368 because it is builtin) and process solely on the basis of those
369 bits. */
370 if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
371 {
372 if (flags & ECF_PURE)
373 {
374 if (local->pure_const_state == IPA_CONST)
375 local->pure_const_state = IPA_PURE;
376 }
377 else
378 local->pure_const_state = IPA_NEITHER;
379 }
380 else
381 {
382 /* We have the code and we will scan it for the effects. */
383 if (flags & ECF_PURE)
384 {
385 if (local->pure_const_state == IPA_CONST)
386 local->pure_const_state = IPA_PURE;
387 }
388 }
389 }
390
391 /* TP is the part of the tree currently under the microscope.
392 WALK_SUBTREES is part of the walk_tree api but is unused here.
393 DATA is cgraph_node of the function being walked. */
394
395 /* FIXME: When this is converted to run over SSA form, this code
396 should be converted to use the operand scanner. */
397
398 static tree
scan_function(tree * tp,int * walk_subtrees,void * data)399 scan_function (tree *tp,
400 int *walk_subtrees,
401 void *data)
402 {
403 struct cgraph_node *fn = data;
404 tree t = *tp;
405 funct_state local = get_function_state (fn);
406
407 switch (TREE_CODE (t))
408 {
409 case VAR_DECL:
410 if (DECL_INITIAL (t))
411 walk_tree (&DECL_INITIAL (t), scan_function, fn, visited_nodes);
412 *walk_subtrees = 0;
413 break;
414
415 case MODIFY_EXPR:
416 {
417 /* First look on the lhs and see what variable is stored to */
418 tree lhs = TREE_OPERAND (t, 0);
419 tree rhs = TREE_OPERAND (t, 1);
420 check_lhs_var (local, lhs);
421
422 /* For the purposes of figuring out what the cast affects */
423
424 /* Next check the operands on the rhs to see if they are ok. */
425 switch (TREE_CODE_CLASS (TREE_CODE (rhs)))
426 {
427 case tcc_binary:
428 {
429 tree op0 = TREE_OPERAND (rhs, 0);
430 tree op1 = TREE_OPERAND (rhs, 1);
431 check_rhs_var (local, op0);
432 check_rhs_var (local, op1);
433 }
434 break;
435 case tcc_unary:
436 {
437 tree op0 = TREE_OPERAND (rhs, 0);
438 check_rhs_var (local, op0);
439 }
440
441 break;
442 case tcc_reference:
443 check_rhs_var (local, rhs);
444 break;
445 case tcc_declaration:
446 check_rhs_var (local, rhs);
447 break;
448 case tcc_expression:
449 switch (TREE_CODE (rhs))
450 {
451 case ADDR_EXPR:
452 check_rhs_var (local, rhs);
453 break;
454 case CALL_EXPR:
455 check_call (local, rhs);
456 break;
457 default:
458 break;
459 }
460 break;
461 default:
462 break;
463 }
464 *walk_subtrees = 0;
465 }
466 break;
467
468 case ADDR_EXPR:
469 /* This case is here to find addresses on rhs of constructors in
470 decl_initial of static variables. */
471 check_rhs_var (local, t);
472 *walk_subtrees = 0;
473 break;
474
475 case LABEL_EXPR:
476 if (DECL_NONLOCAL (TREE_OPERAND (t, 0)))
477 /* Target of long jump. */
478 local->pure_const_state = IPA_NEITHER;
479 break;
480
481 case CALL_EXPR:
482 check_call (local, t);
483 *walk_subtrees = 0;
484 break;
485
486 case ASM_EXPR:
487 get_asm_expr_operands (local, t);
488 *walk_subtrees = 0;
489 break;
490
491 default:
492 break;
493 }
494 return NULL;
495 }
496
497 /* This is the main routine for finding the reference patterns for
498 global variables within a function FN. */
499
500 static void
analyze_function(struct cgraph_node * fn)501 analyze_function (struct cgraph_node *fn)
502 {
503 funct_state l = XCNEW (struct funct_state_d);
504 tree decl = fn->decl;
505 struct ipa_dfs_info * w_info = fn->aux;
506
507 w_info->aux = l;
508
509 l->pure_const_state = IPA_CONST;
510 l->state_set_in_source = false;
511
512 /* If this function does not return normally or does not bind local,
513 do not touch this unless it has been marked as const or pure by the
514 front end. */
515 if (TREE_THIS_VOLATILE (decl)
516 || !targetm.binds_local_p (decl))
517 {
518 l->pure_const_state = IPA_NEITHER;
519 return;
520 }
521
522 if (TREE_READONLY (decl))
523 {
524 l->pure_const_state = IPA_CONST;
525 l->state_set_in_source = true;
526 }
527 if (DECL_IS_PURE (decl))
528 {
529 l->pure_const_state = IPA_PURE;
530 l->state_set_in_source = true;
531 }
532
533 if (dump_file)
534 {
535 fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ",
536 cgraph_node_name (fn),
537 l->pure_const_state);
538 }
539
540 if (!l->state_set_in_source)
541 {
542 struct function *this_cfun = DECL_STRUCT_FUNCTION (decl);
543 basic_block this_block;
544
545 FOR_EACH_BB_FN (this_block, this_cfun)
546 {
547 block_stmt_iterator bsi;
548 for (bsi = bsi_start (this_block); !bsi_end_p (bsi); bsi_next (&bsi))
549 {
550 walk_tree (bsi_stmt_ptr (bsi), scan_function,
551 fn, visited_nodes);
552 if (l->pure_const_state == IPA_NEITHER)
553 goto end;
554 }
555 }
556
557 if (l->pure_const_state != IPA_NEITHER)
558 {
559 tree old_decl = current_function_decl;
560 /* Const functions cannot have back edges (an
561 indication of possible infinite loop side
562 effect. */
563
564 current_function_decl = fn->decl;
565
566 /* The C++ front end, has a tendency to some times jerk away
567 a function after it has created it. This should have
568 been fixed. */
569 gcc_assert (DECL_STRUCT_FUNCTION (fn->decl));
570
571 push_cfun (DECL_STRUCT_FUNCTION (fn->decl));
572
573 if (mark_dfs_back_edges ())
574 l->pure_const_state = IPA_NEITHER;
575
576 current_function_decl = old_decl;
577 pop_cfun ();
578 }
579 }
580
581 end:
582 if (dump_file)
583 {
584 fprintf (dump_file, "after local analysis of %s with initial value = %d\n ",
585 cgraph_node_name (fn),
586 l->pure_const_state);
587 }
588 }
589
590
591 /* Produce the global information by preforming a transitive closure
592 on the local information that was produced by ipa_analyze_function
593 and ipa_analyze_variable. */
594
595 static unsigned int
static_execute(void)596 static_execute (void)
597 {
598 struct cgraph_node *node;
599 struct cgraph_node *w;
600 struct cgraph_node **order =
601 XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
602 int order_pos = order_pos = ipa_utils_reduced_inorder (order, true, false);
603 int i;
604 struct ipa_dfs_info * w_info;
605
606 if (!memory_identifier_string)
607 memory_identifier_string = build_string(7, "memory");
608
609 /* There are some shared nodes, in particular the initializers on
610 static declarations. We do not need to scan them more than once
611 since all we would be interested in are the addressof
612 operations. */
613 visited_nodes = pointer_set_create ();
614
615 /* Process all of the functions.
616
617 We do not want to process any of the clones so we check that this
618 is a master clone. However, we do NOT process any
619 AVAIL_OVERWRITABLE functions (these are never clones) we cannot
620 guarantee that what we learn about the one we see will be true
621 for the one that overriders it.
622 */
623 for (node = cgraph_nodes; node; node = node->next)
624 if (node->analyzed && cgraph_is_master_clone (node))
625 analyze_function (node);
626
627 pointer_set_destroy (visited_nodes);
628 visited_nodes = NULL;
629 if (dump_file)
630 {
631 dump_cgraph (dump_file);
632 ipa_utils_print_order(dump_file, "reduced", order, order_pos);
633 }
634
635 /* Propagate the local information thru the call graph to produce
636 the global information. All the nodes within a cycle will have
637 the same info so we collapse cycles first. Then we can do the
638 propagation in one pass from the leaves to the roots. */
639 for (i = 0; i < order_pos; i++ )
640 {
641 enum pure_const_state_e pure_const_state = IPA_CONST;
642 int count = 0;
643 node = order[i];
644
645 /* Find the worst state for any node in the cycle. */
646 w = node;
647 while (w)
648 {
649 funct_state w_l = get_function_state (w);
650 if (pure_const_state < w_l->pure_const_state)
651 pure_const_state = w_l->pure_const_state;
652
653 if (pure_const_state == IPA_NEITHER)
654 break;
655
656 if (!w_l->state_set_in_source)
657 {
658 struct cgraph_edge *e;
659 count++;
660
661 /* FIXME!!! Because of pr33826, we cannot have either
662 immediate or transitive recursive functions marked as
663 pure or const because dce can delete a function that
664 is in reality an infinite loop. A better solution
665 than just outlawing them is to add another bit the
666 functions to distinguish recursive from non recursive
667 pure and const function. This would allow the
668 recursive ones to be cse'd but not dce'd. In this
669 same vein, we could allow functions with loops to
670 also be cse'd but not dce'd.
671
672 Unfortunately we are late in stage 3, and the fix
673 described above is is not appropriate. */
674 if (count > 1)
675 {
676 pure_const_state = IPA_NEITHER;
677 break;
678 }
679
680 for (e = w->callees; e; e = e->next_callee)
681 {
682 struct cgraph_node *y = e->callee;
683 /* Only look at the master nodes and skip external nodes. */
684 y = cgraph_master_clone (y);
685
686 /* Check for immediate recursive functions. See the
687 FIXME above. */
688 if (w == y)
689 {
690 pure_const_state = IPA_NEITHER;
691 break;
692 }
693 if (y)
694 {
695 funct_state y_l = get_function_state (y);
696 if (pure_const_state < y_l->pure_const_state)
697 pure_const_state = y_l->pure_const_state;
698 if (pure_const_state == IPA_NEITHER)
699 break;
700 }
701 }
702 }
703 w_info = w->aux;
704 w = w_info->next_cycle;
705 }
706
707 /* Copy back the region's pure_const_state which is shared by
708 all nodes in the region. */
709 w = node;
710 while (w)
711 {
712 funct_state w_l = get_function_state (w);
713
714 /* All nodes within a cycle share the same info. */
715 if (!w_l->state_set_in_source)
716 {
717 w_l->pure_const_state = pure_const_state;
718 switch (pure_const_state)
719 {
720 case IPA_CONST:
721 TREE_READONLY (w->decl) = 1;
722 if (dump_file)
723 fprintf (dump_file, "Function found to be const: %s\n",
724 lang_hooks.decl_printable_name(w->decl, 2));
725 break;
726
727 case IPA_PURE:
728 DECL_IS_PURE (w->decl) = 1;
729 if (dump_file)
730 fprintf (dump_file, "Function found to be pure: %s\n",
731 lang_hooks.decl_printable_name(w->decl, 2));
732 break;
733
734 default:
735 break;
736 }
737 }
738 w_info = w->aux;
739 w = w_info->next_cycle;
740 }
741 }
742
743 /* Cleanup. */
744 for (node = cgraph_nodes; node; node = node->next)
745 /* Get rid of the aux information. */
746 if (node->aux)
747 {
748 w_info = node->aux;
749 if (w_info->aux)
750 free (w_info->aux);
751 free (node->aux);
752 node->aux = NULL;
753 }
754
755 free (order);
756 return 0;
757 }
758
759 static bool
gate_pure_const(void)760 gate_pure_const (void)
761 {
762 return (flag_unit_at_a_time != 0 && flag_ipa_pure_const
763 /* Don't bother doing anything if the program has errors. */
764 && !(errorcount || sorrycount));
765 }
766
767 struct tree_opt_pass pass_ipa_pure_const =
768 {
769 "pure-const", /* name */
770 gate_pure_const, /* gate */
771 static_execute, /* execute */
772 NULL, /* sub */
773 NULL, /* next */
774 0, /* static_pass_number */
775 TV_IPA_PURE_CONST, /* tv_id */
776 0, /* properties_required */
777 0, /* properties_provided */
778 0, /* properties_destroyed */
779 0, /* todo_flags_start */
780 0, /* todo_flags_finish */
781 0 /* letter */
782 };
783
784
785