xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/compare-elim.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /* Post-reload compare elimination.
2    Copyright (C) 2010-2013 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10 
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19 
20 /* There is a set of targets whose general-purpose move or addition
21    instructions clobber the flags.  These targets cannot split their
22    CBRANCH/CSTORE etc patterns before reload is complete, lest reload
23    itself insert these instructions in between the flags setter and user.
24    Because these targets cannot split the compare from the use, they
25    cannot make use of the comparison elimination offered by the combine pass.
26 
27    This is a small pass intended to provide comparison elimination similar to
28    what is available via NOTICE_UPDATE_CC for cc0 targets.  This should help
29    encourage cc0 targets to convert to an explicit post-reload representation
30    of the flags.
31 
32    This pass assumes:
33 
34    (0) CBRANCH/CSTORE etc have been split in pass_split_after_reload.
35 
36    (1) All comparison patterns are represented as
37 
38 	[(set (reg:CC) (compare:CC (reg) (immediate)))]
39 
40    (2) All insn patterns that modify the flags are represented as
41 
42 	[(set (reg) (operation)
43 	 (clobber (reg:CC))]
44 
45    (3) If an insn of form (2) can usefully set the flags, there is
46        another pattern of the form
47 
48 	[(set (reg) (operation)
49 	 (set (reg:CCM) (compare:CCM (operation) (immediate)))]
50 
51        The mode CCM will be chosen as if by SELECT_CC_MODE.
52 
53    Note that unlike NOTICE_UPDATE_CC, we do not handle memory operands.
54    This could be handled as a future enhancement.
55 */
56 
57 #include "config.h"
58 #include "system.h"
59 #include "coretypes.h"
60 #include "tm.h"
61 #include "rtl.h"
62 #include "tm_p.h"
63 #include "insn-config.h"
64 #include "recog.h"
65 #include "flags.h"
66 #include "basic-block.h"
67 #include "tree-pass.h"
68 #include "target.h"
69 #include "df.h"
70 #include "domwalk.h"
71 
72 
73 /* These structures describe a comparison and how it is used.  */
74 
75 /* The choice of maximum 3 uses comes from wanting to eliminate the two
76    duplicate compares from a three-way branch on the sign of a value.
77    This is also sufficient to eliminate the duplicate compare against the
78    high-part of a double-word comparison.  */
79 #define MAX_CMP_USE 3
80 
81 struct comparison_use
82 {
83   /* The instruction in which the result of the compare is used.  */
84   rtx insn;
85   /* The location of the flags register within the use.  */
86   rtx *loc;
87   /* The comparison code applied against the flags register.  */
88   enum rtx_code code;
89 };
90 
91 struct comparison
92 {
93   /* The comparison instruction.  */
94   rtx insn;
95 
96   /* The insn prior to the comparison insn that clobbers the flags.  */
97   rtx prev_clobber;
98 
99   /* The two values being compared.  These will be either REGs or
100      constants.  */
101   rtx in_a, in_b;
102 
103   /* Information about how this comparison is used.  */
104   struct comparison_use uses[MAX_CMP_USE];
105 
106   /* The original CC_MODE for this comparison.  */
107   enum machine_mode orig_mode;
108 
109   /* The number of uses identified for this comparison.  */
110   unsigned short n_uses;
111 
112   /* True if not all uses of this comparison have been identified.
113      This can happen either for overflowing the array above, or if
114      the flags register is used in some unusual context.  */
115   bool missing_uses;
116 
117   /* True if its inputs are still valid at the end of the block.  */
118   bool inputs_valid;
119 };
120 
121 typedef struct comparison *comparison_struct_p;
122 
123 static vec<comparison_struct_p> all_compares;
124 
125 /* Look for a "conforming" comparison, as defined above.  If valid, return
126    the rtx for the COMPARE itself.  */
127 
128 static rtx
129 conforming_compare (rtx insn)
130 {
131   rtx set, src, dest;
132 
133   set = single_set (insn);
134   if (set == NULL)
135     return NULL;
136 
137   src = SET_SRC (set);
138   if (GET_CODE (src) != COMPARE)
139     return NULL;
140 
141   dest = SET_DEST (set);
142   if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
143     return NULL;
144 
145   if (REG_P (XEXP (src, 0))
146       && REG_P (XEXP (src, 0))
147       && (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1))))
148     return src;
149 
150   return NULL;
151 }
152 
153 /* Look for a pattern of the "correct" form for an insn with a flags clobber
154    for which we may be able to eliminate a compare later.  We're not looking
155    to validate any inputs at this time, merely see that the basic shape is
156    correct.  The term "arithmetic" may be somewhat misleading...  */
157 
158 static bool
159 arithmetic_flags_clobber_p (rtx insn)
160 {
161   rtx pat, x;
162 
163   if (!NONJUMP_INSN_P (insn))
164     return false;
165   pat = PATTERN (insn);
166   if (extract_asm_operands (pat))
167     return false;
168 
169   if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) == 2)
170     {
171       x = XVECEXP (pat, 0, 0);
172       if (GET_CODE (x) != SET)
173 	return false;
174       x = SET_DEST (x);
175       if (!REG_P (x))
176 	return false;
177 
178       x = XVECEXP (pat, 0, 1);
179       if (GET_CODE (x) == CLOBBER)
180 	{
181 	  x = XEXP (x, 0);
182 	  if (REG_P (x) && REGNO (x) == targetm.flags_regnum)
183 	    return true;
184 	}
185     }
186 
187   return false;
188 }
189 
190 /* Look for uses of FLAGS in INSN.  If we find one we can analyze, record
191    it in CMP; otherwise indicate that we've missed a use.  */
192 
193 static void
194 find_flags_uses_in_insn (struct comparison *cmp, rtx insn)
195 {
196   df_ref *use_rec, use;
197 
198   /* If we've already lost track of uses, don't bother collecting more.  */
199   if (cmp->missing_uses)
200     return;
201 
202   /* Find a USE of the flags register.  */
203   for (use_rec = DF_INSN_USES (insn); (use = *use_rec) != NULL; use_rec++)
204     if (DF_REF_REGNO (use) == targetm.flags_regnum)
205       {
206 	rtx x, *loc;
207 
208 	/* If this is an unusual use, quit.  */
209 	if (DF_REF_TYPE (use) != DF_REF_REG_USE)
210 	  goto fail;
211 
212 	/* If we've run out of slots to record uses, quit.  */
213 	if (cmp->n_uses == MAX_CMP_USE)
214 	  goto fail;
215 
216 	/* Unfortunately the location of the flags register, while present
217 	   in the reference structure, doesn't help.  We need to find the
218 	   comparison code that is outer to the actual flags use.  */
219 	loc = DF_REF_LOC (use);
220 	x = PATTERN (insn);
221 	if (GET_CODE (x) == PARALLEL)
222 	  x = XVECEXP (x, 0, 0);
223 	x = SET_SRC (x);
224 	if (GET_CODE (x) == IF_THEN_ELSE)
225 	  x = XEXP (x, 0);
226 	if (COMPARISON_P (x)
227 	    && loc == &XEXP (x, 0)
228 	    && XEXP (x, 1) == const0_rtx)
229 	  {
230 	    /* We've found a use of the flags that we understand.  */
231 	    struct comparison_use *cuse = &cmp->uses[cmp->n_uses++];
232 	    cuse->insn = insn;
233 	    cuse->loc = loc;
234 	    cuse->code = GET_CODE (x);
235 	  }
236 	else
237 	  goto fail;
238       }
239   return;
240 
241  fail:
242   /* We failed to recognize this use of the flags register.  */
243   cmp->missing_uses = true;
244 }
245 
246 /* Identify comparison instructions within BB.  If the flags from the last
247    compare in the BB is live at the end of the block, install the compare
248    in BB->AUX.  Called via walk_dominators_tree.  */
249 
250 static void
251 find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
252 			basic_block bb)
253 {
254   struct comparison *last_cmp;
255   rtx insn, next, last_clobber;
256   bool last_cmp_valid;
257   bitmap killed;
258 
259   killed = BITMAP_ALLOC (NULL);
260 
261   /* The last comparison that was made.  Will be reset to NULL
262      once the flags are clobbered.  */
263   last_cmp = NULL;
264 
265   /* True iff the last comparison has not been clobbered, nor
266      have its inputs.  Used to eliminate duplicate compares.  */
267   last_cmp_valid = false;
268 
269   /* The last insn that clobbered the flags, if that insn is of
270      a form that may be valid for eliminating a following compare.
271      To be reset to NULL once the flags are set otherwise.  */
272   last_clobber = NULL;
273 
274   /* Propagate the last live comparison throughout the extended basic block. */
275   if (single_pred_p (bb))
276     {
277       last_cmp = (struct comparison *) single_pred (bb)->aux;
278       if (last_cmp)
279 	last_cmp_valid = last_cmp->inputs_valid;
280     }
281 
282   for (insn = BB_HEAD (bb); insn; insn = next)
283     {
284       rtx src;
285 
286       next = (insn == BB_END (bb) ? NULL_RTX : NEXT_INSN (insn));
287       if (!NONDEBUG_INSN_P (insn))
288 	continue;
289 
290       /* Compute the set of registers modified by this instruction.  */
291       bitmap_clear (killed);
292       df_simulate_find_defs (insn, killed);
293 
294       src = conforming_compare (insn);
295       if (src)
296 	{
297 	  enum machine_mode src_mode = GET_MODE (src);
298 
299 	  /* Eliminate a compare that's redundant with the previous.  */
300 	  if (last_cmp_valid
301 	      && rtx_equal_p (last_cmp->in_a, XEXP (src, 0))
302 	      && rtx_equal_p (last_cmp->in_b, XEXP (src, 1)))
303 	    {
304 	      rtx flags, x;
305 	      enum machine_mode new_mode
306 		= targetm.cc_modes_compatible (last_cmp->orig_mode, src_mode);
307 
308 	      /* New mode is incompatible with the previous compare mode.  */
309 	      if (new_mode == VOIDmode)
310 		continue;
311 
312 	      if (new_mode != last_cmp->orig_mode)
313 		{
314 		  flags = gen_rtx_REG (src_mode, targetm.flags_regnum);
315 
316 		  /* Generate new comparison for substitution.  */
317 		  x = gen_rtx_COMPARE (new_mode, XEXP (src, 0), XEXP (src, 1));
318 		  x = gen_rtx_SET (VOIDmode, flags, x);
319 
320 		  if (!validate_change (last_cmp->insn,
321 					&PATTERN (last_cmp->insn), x, false))
322 		    continue;
323 
324 		  last_cmp->orig_mode = new_mode;
325 		}
326 
327 	      delete_insn (insn);
328 	      continue;
329 	    }
330 
331 	  last_cmp = XCNEW (struct comparison);
332 	  last_cmp->insn = insn;
333 	  last_cmp->prev_clobber = last_clobber;
334 	  last_cmp->in_a = XEXP (src, 0);
335 	  last_cmp->in_b = XEXP (src, 1);
336 	  last_cmp->orig_mode = src_mode;
337 	  all_compares.safe_push (last_cmp);
338 
339 	  /* It's unusual, but be prepared for comparison patterns that
340 	     also clobber an input, or perhaps a scratch.  */
341 	  last_clobber = NULL;
342 	  last_cmp_valid = true;
343 	}
344 
345       /* Notice if this instruction kills the flags register.  */
346       else if (bitmap_bit_p (killed, targetm.flags_regnum))
347 	{
348 	  /* See if this insn could be the "clobber" that eliminates
349 	     a future comparison.   */
350 	  last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL);
351 
352 	  /* In either case, the previous compare is no longer valid.  */
353 	  last_cmp = NULL;
354 	  last_cmp_valid = false;
355 	  continue;
356 	}
357 
358       /* Notice if this instruction uses the flags register.  */
359       else if (last_cmp)
360 	find_flags_uses_in_insn (last_cmp, insn);
361 
362       /* Notice if any of the inputs to the comparison have changed.  */
363       if (last_cmp_valid
364 	  && (bitmap_bit_p (killed, REGNO (last_cmp->in_a))
365 	      || (REG_P (last_cmp->in_b)
366 		  && bitmap_bit_p (killed, REGNO (last_cmp->in_b)))))
367 	last_cmp_valid = false;
368     }
369 
370   BITMAP_FREE (killed);
371 
372   /* Remember the live comparison for subsequent members of
373      the extended basic block.  */
374   if (last_cmp)
375     {
376       bb->aux = last_cmp;
377       last_cmp->inputs_valid = last_cmp_valid;
378 
379       /* Look to see if the flags register is live outgoing here, and
380 	 incoming to any successor not part of the extended basic block.  */
381       if (bitmap_bit_p (df_get_live_out (bb), targetm.flags_regnum))
382 	{
383 	  edge e;
384 	  edge_iterator ei;
385 
386 	  FOR_EACH_EDGE (e, ei, bb->succs)
387 	    {
388 	      basic_block dest = e->dest;
389 	      if (bitmap_bit_p (df_get_live_in (bb),
390 				targetm.flags_regnum)
391 		  && !single_pred_p (dest))
392 		{
393 		  last_cmp->missing_uses = true;
394 		  break;
395 		}
396 	    }
397 	}
398     }
399 }
400 
401 /* Find all comparisons in the function.  */
402 
403 static void
404 find_comparisons (void)
405 {
406   struct dom_walk_data data;
407 
408   memset (&data, 0, sizeof(data));
409   data.dom_direction = CDI_DOMINATORS;
410   data.before_dom_children = find_comparisons_in_bb;
411 
412   calculate_dominance_info (CDI_DOMINATORS);
413 
414   init_walk_dominator_tree (&data);
415   walk_dominator_tree (&data, ENTRY_BLOCK_PTR);
416   fini_walk_dominator_tree (&data);
417 
418   clear_aux_for_blocks ();
419   free_dominance_info (CDI_DOMINATORS);
420 }
421 
422 /* Select an alternate CC_MODE for a comparison insn comparing A and B.
423    Note that inputs are almost certainly different than the IN_A and IN_B
424    stored in CMP -- we're called while attempting to eliminate the compare
425    after all.  Return the new FLAGS rtx if successful, else return NULL.
426    Note that this function may start a change group.  */
427 
428 static rtx
429 maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
430 		      rtx b ATTRIBUTE_UNUSED)
431 {
432   enum machine_mode sel_mode;
433   const int n = cmp->n_uses;
434   rtx flags = NULL;
435 
436 #ifndef SELECT_CC_MODE
437   /* Minimize code differences when this target macro is undefined.  */
438   return NULL;
439 #define SELECT_CC_MODE(A,B,C) (gcc_unreachable (), VOIDmode)
440 #endif
441 
442   /* If we don't have access to all of the uses, we can't validate.  */
443   if (cmp->missing_uses || n == 0)
444     return NULL;
445 
446   /* Find a new mode that works for all of the uses.  Special case the
447      common case of exactly one use.  */
448   if (n == 1)
449     {
450       sel_mode = SELECT_CC_MODE (cmp->uses[0].code, a, b);
451       if (sel_mode != cmp->orig_mode)
452 	{
453 	  flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
454 	  validate_change (cmp->uses[0].insn, cmp->uses[0].loc, flags, true);
455 	}
456     }
457   else
458     {
459       int i;
460 
461       sel_mode = SELECT_CC_MODE (cmp->uses[0].code, a, b);
462       for (i = 1; i < n; ++i)
463 	{
464 	  enum machine_mode new_mode;
465 	  new_mode = SELECT_CC_MODE (cmp->uses[i].code, a, b);
466 	  if (new_mode != sel_mode)
467 	    {
468 	      sel_mode = targetm.cc_modes_compatible (sel_mode, new_mode);
469 	      if (sel_mode == VOIDmode)
470 		return NULL;
471 	    }
472 	}
473 
474       if (sel_mode != cmp->orig_mode)
475 	{
476 	  flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
477 	  for (i = 0; i < n; ++i)
478 	    validate_change (cmp->uses[i].insn, cmp->uses[i].loc, flags, true);
479 	}
480     }
481 
482   return flags;
483 }
484 
485 /* Attempt to replace a comparison with a prior arithmetic insn that can
486    compute the same flags value as the comparison itself.  Return true if
487    successful, having made all rtl modifications necessary.  */
488 
489 static bool
490 try_eliminate_compare (struct comparison *cmp)
491 {
492   rtx x, insn, bb_head, flags, in_a, cmp_src;
493 
494   /* We must have found an interesting "clobber" preceding the compare.  */
495   if (cmp->prev_clobber == NULL)
496     return false;
497 
498   /* ??? For the moment we don't handle comparisons for which IN_B
499      is a register.  We accepted these during initial comparison
500      recognition in order to eliminate duplicate compares.
501      An improvement here would be to handle x = a - b; if (a cmp b).  */
502   if (!CONSTANT_P (cmp->in_b))
503     return false;
504 
505   /* Verify that IN_A is not clobbered in between CMP and PREV_CLOBBER.
506      Given that this target requires this pass, we can assume that most
507      insns do clobber the flags, and so the distance between the compare
508      and the clobber is likely to be small.  */
509   /* ??? This is one point at which one could argue that DF_REF_CHAIN would
510      be useful, but it is thought to be too heavy-weight a solution here.  */
511 
512   in_a = cmp->in_a;
513   insn = cmp->insn;
514   bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
515   for (insn = PREV_INSN (insn);
516        insn != cmp->prev_clobber;
517        insn = PREV_INSN (insn))
518     {
519       const int abnormal_flags
520 	= (DF_REF_CONDITIONAL | DF_REF_PARTIAL | DF_REF_MAY_CLOBBER
521 	   | DF_REF_MUST_CLOBBER | DF_REF_SIGN_EXTRACT
522 	   | DF_REF_ZERO_EXTRACT | DF_REF_STRICT_LOW_PART
523 	   | DF_REF_PRE_POST_MODIFY);
524       df_ref *def_rec, def;
525 
526       /* Note that the BB_HEAD is always either a note or a label, but in
527 	 any case it means that IN_A is defined outside the block.  */
528       if (insn == bb_head)
529 	return false;
530       if (NOTE_P (insn) || DEBUG_INSN_P (insn))
531 	continue;
532 
533       /* Find a possible def of IN_A in INSN.  */
534       for (def_rec = DF_INSN_DEFS (insn); (def = *def_rec) != NULL; def_rec++)
535 	if (DF_REF_REGNO (def) == REGNO (in_a))
536 	  break;
537 
538       /* No definitions of IN_A; continue searching.  */
539       if (def == NULL)
540 	continue;
541 
542       /* Bail if this is not a totally normal set of IN_A.  */
543       if (DF_REF_IS_ARTIFICIAL (def))
544 	return false;
545       if (DF_REF_FLAGS (def) & abnormal_flags)
546 	return false;
547 
548       /* We've found an insn between the compare and the clobber that sets
549 	 IN_A.  Given that pass_cprop_hardreg has not yet run, we still find
550 	 situations in which we can usefully look through a copy insn.  */
551       x = single_set (insn);
552       if (x == NULL)
553 	return false;
554       in_a = SET_SRC (x);
555       if (!REG_P (in_a))
556 	return false;
557     }
558 
559   /* We've reached PREV_CLOBBER without finding a modification of IN_A.
560      Validate that PREV_CLOBBER itself does in fact refer to IN_A.  Do
561      recall that we've already validated the shape of PREV_CLOBBER.  */
562   x = XVECEXP (PATTERN (insn), 0, 0);
563   if (rtx_equal_p (SET_DEST (x), in_a))
564     cmp_src = SET_SRC (x);
565 
566   /* Also check operations with implicit extensions, e.g.:
567      [(set (reg:DI)
568 	   (zero_extend:DI (plus:SI (reg:SI)(reg:SI))))
569       (set (reg:CCZ flags)
570 	   (compare:CCZ
571 	     (plus:SI (reg:SI)(reg:SI))
572 	     (const_int 0)))]				*/
573   else if (REG_P (SET_DEST (x))
574 	   && REG_P (in_a)
575 	   && REGNO (SET_DEST (x)) == REGNO (in_a)
576 	   && (GET_CODE (SET_SRC (x)) == ZERO_EXTEND
577 	       || GET_CODE (SET_SRC (x)) == SIGN_EXTEND)
578 	   && GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a))
579     cmp_src = XEXP (SET_SRC (x), 0);
580   else
581     return false;
582 
583   /* Determine if we ought to use a different CC_MODE here.  */
584   flags = maybe_select_cc_mode (cmp, cmp_src, cmp->in_b);
585   if (flags == NULL)
586     flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum);
587 
588   /* Generate a new comparison for installation in the setter.  */
589   x = copy_rtx (cmp_src);
590   x = gen_rtx_COMPARE (GET_MODE (flags), x, cmp->in_b);
591   x = gen_rtx_SET (VOIDmode, flags, x);
592 
593   /* Succeed if the new instruction is valid.  Note that we may have started
594      a change group within maybe_select_cc_mode, therefore we must continue. */
595   validate_change (insn, &XVECEXP (PATTERN (insn), 0, 1), x, true);
596   if (!apply_change_group ())
597     return false;
598 
599   /* Success.  Delete the compare insn...  */
600   delete_insn (cmp->insn);
601 
602   /* ... and any notes that are now invalid due to multiple sets.  */
603   x = find_regno_note (insn, REG_UNUSED, targetm.flags_regnum);
604   if (x)
605     remove_note (insn, x);
606   x = find_reg_note (insn, REG_EQUAL, NULL);
607   if (x)
608     remove_note (insn, x);
609   x = find_reg_note (insn, REG_EQUIV, NULL);
610   if (x)
611     remove_note (insn, x);
612 
613   return true;
614 }
615 
616 /* Main entry point to the pass.  */
617 
618 static unsigned int
619 execute_compare_elim_after_reload (void)
620 {
621   df_analyze ();
622 
623   gcc_checking_assert (!all_compares.exists ());
624 
625   /* Locate all comparisons and their uses, and eliminate duplicates.  */
626   find_comparisons ();
627   if (all_compares.exists ())
628     {
629       struct comparison *cmp;
630       size_t i;
631 
632       /* Eliminate comparisons that are redundant with flags computation.  */
633       FOR_EACH_VEC_ELT (all_compares, i, cmp)
634 	{
635 	  try_eliminate_compare (cmp);
636 	  XDELETE (cmp);
637 	}
638 
639       all_compares.release ();
640     }
641 
642   return 0;
643 }
644 
645 static bool
646 gate_compare_elim_after_reload (void)
647 {
648   /* Setting this target hook value is how a backend indicates the need.  */
649   if (targetm.flags_regnum == INVALID_REGNUM)
650     return false;
651   return flag_compare_elim_after_reload;
652 }
653 
654 struct rtl_opt_pass pass_compare_elim_after_reload =
655 {
656  {
657   RTL_PASS,
658   "cmpelim",				/* name */
659   OPTGROUP_NONE,                        /* optinfo_flags */
660   gate_compare_elim_after_reload,	/* gate */
661   execute_compare_elim_after_reload,	/* execute */
662   NULL,					/* sub */
663   NULL,					/* next */
664   0,					/* static_pass_number */
665   TV_NONE,				/* tv_id */
666   0,					/* properties_required */
667   0,					/* properties_provided */
668   0,					/* properties_destroyed */
669   0,					/* todo_flags_start */
670   TODO_df_finish
671   | TODO_df_verify
672   | TODO_verify_rtl_sharing
673   | TODO_ggc_collect			/* todo_flags_finish */
674  }
675 };
676