tree-dump.c revision 117395
1/* Tree-dumping functionality for intermediate representation.
2   Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
3   Written by Mark Mitchell <mark@codesourcery.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING.  If not, write to the Free
19Software Foundation, 59 Temple Place - Suite 330, Boston, MA
2002111-1307, USA.  */
21
22#include "config.h"
23#include "system.h"
24#include "tree.h"
25#include "splay-tree.h"
26#include "diagnostic.h"
27#include "toplev.h"
28#include "tree-dump.h"
29#include "langhooks.h"
30
31static unsigned int queue PARAMS ((dump_info_p, tree, int));
32static void dump_index PARAMS ((dump_info_p, unsigned int));
33static void dequeue_and_dump PARAMS ((dump_info_p));
34static void dump_new_line PARAMS ((dump_info_p));
35static void dump_maybe_newline PARAMS ((dump_info_p));
36static void dump_string_field PARAMS ((dump_info_p, const char *, const char *));
37
38/* Add T to the end of the queue of nodes to dump.  Returns the index
39   assigned to T.  */
40
41static unsigned int
42queue (di, t, flags)
43     dump_info_p di;
44     tree t;
45     int flags;
46{
47  dump_queue_p dq;
48  dump_node_info_p dni;
49  unsigned int index;
50
51  /* Assign the next available index to T.  */
52  index = ++di->index;
53
54  /* Obtain a new queue node.  */
55  if (di->free_list)
56    {
57      dq = di->free_list;
58      di->free_list = dq->next;
59    }
60  else
61    dq = (dump_queue_p) xmalloc (sizeof (struct dump_queue));
62
63  /* Create a new entry in the splay-tree.  */
64  dni = (dump_node_info_p) xmalloc (sizeof (struct dump_node_info));
65  dni->index = index;
66  dni->binfo_p = ((flags & DUMP_BINFO) != 0);
67  dq->node = splay_tree_insert (di->nodes, (splay_tree_key) t,
68				(splay_tree_value) dni);
69
70  /* Add it to the end of the queue.  */
71  dq->next = 0;
72  if (!di->queue_end)
73    di->queue = dq;
74  else
75    di->queue_end->next = dq;
76  di->queue_end = dq;
77
78  /* Return the index.  */
79  return index;
80}
81
82static void
83dump_index (di, index)
84     dump_info_p di;
85     unsigned int index;
86{
87  fprintf (di->stream, "@%-6u ", index);
88  di->column += 8;
89}
90
91/* If T has not already been output, queue it for subsequent output.
92   FIELD is a string to print before printing the index.  Then, the
93   index of T is printed.  */
94
95void
96queue_and_dump_index (di, field, t, flags)
97     dump_info_p di;
98     const char *field;
99     tree t;
100     int flags;
101{
102  unsigned int index;
103  splay_tree_node n;
104
105  /* If there's no node, just return.  This makes for fewer checks in
106     our callers.  */
107  if (!t)
108    return;
109
110  /* See if we've already queued or dumped this node.  */
111  n = splay_tree_lookup (di->nodes, (splay_tree_key) t);
112  if (n)
113    index = ((dump_node_info_p) n->value)->index;
114  else
115    /* If we haven't, add it to the queue.  */
116    index = queue (di, t, flags);
117
118  /* Print the index of the node.  */
119  dump_maybe_newline (di);
120  fprintf (di->stream, "%-4s: ", field);
121  di->column += 6;
122  dump_index (di, index);
123}
124
125/* Dump the type of T.  */
126
127void
128queue_and_dump_type (di, t)
129     dump_info_p di;
130     tree t;
131{
132  queue_and_dump_index (di, "type", TREE_TYPE (t), DUMP_NONE);
133}
134
135/* Dump column control */
136#define SOL_COLUMN 25		/* Start of line column.  */
137#define EOL_COLUMN 55		/* End of line column.  */
138#define COLUMN_ALIGNMENT 15	/* Alignment.  */
139
140/* Insert a new line in the dump output, and indent to an appropriate
141   place to start printing more fields.  */
142
143static void
144dump_new_line (di)
145     dump_info_p di;
146{
147  fprintf (di->stream, "\n%*s", SOL_COLUMN, "");
148  di->column = SOL_COLUMN;
149}
150
151/* If necessary, insert a new line.  */
152
153static void
154dump_maybe_newline (di)
155     dump_info_p di;
156{
157  int extra;
158
159  /* See if we need a new line.  */
160  if (di->column > EOL_COLUMN)
161    dump_new_line (di);
162  /* See if we need any padding.  */
163  else if ((extra = (di->column - SOL_COLUMN) % COLUMN_ALIGNMENT) != 0)
164    {
165      fprintf (di->stream, "%*s", COLUMN_ALIGNMENT - extra, "");
166      di->column += COLUMN_ALIGNMENT - extra;
167    }
168}
169
170/* Dump pointer PTR using FIELD to identify it.  */
171
172void
173dump_pointer (di, field, ptr)
174     dump_info_p di;
175     const char *field;
176     void *ptr;
177{
178  dump_maybe_newline (di);
179  fprintf (di->stream, "%-4s: %-8lx ", field, (long) ptr);
180  di->column += 15;
181}
182
183/* Dump integer I using FIELD to identify it.  */
184
185void
186dump_int (di, field, i)
187     dump_info_p di;
188     const char *field;
189     int i;
190{
191  dump_maybe_newline (di);
192  fprintf (di->stream, "%-4s: %-7d ", field, i);
193  di->column += 14;
194}
195
196/* Dump the string S.  */
197
198void
199dump_string (di, string)
200     dump_info_p di;
201     const char *string;
202{
203  dump_maybe_newline (di);
204  fprintf (di->stream, "%-13s ", string);
205  if (strlen (string) > 13)
206    di->column += strlen (string) + 1;
207  else
208    di->column += 14;
209}
210
211/* Dump the string field S.  */
212
213static void
214dump_string_field (di, field, string)
215     dump_info_p di;
216     const char *field;
217     const char *string;
218{
219  dump_maybe_newline (di);
220  fprintf (di->stream, "%-4s: %-7s ", field, string);
221  if (strlen (string) > 7)
222    di->column += 6 + strlen (string) + 1;
223  else
224    di->column += 14;
225}
226
227/* Dump the next node in the queue.  */
228
229static void
230dequeue_and_dump (di)
231     dump_info_p di;
232{
233  dump_queue_p dq;
234  splay_tree_node stn;
235  dump_node_info_p dni;
236  tree t;
237  unsigned int index;
238  enum tree_code code;
239  char code_class;
240  const char* code_name;
241
242  /* Get the next node from the queue.  */
243  dq = di->queue;
244  stn = dq->node;
245  t = (tree) stn->key;
246  dni = (dump_node_info_p) stn->value;
247  index = dni->index;
248
249  /* Remove the node from the queue, and put it on the free list.  */
250  di->queue = dq->next;
251  if (!di->queue)
252    di->queue_end = 0;
253  dq->next = di->free_list;
254  di->free_list = dq;
255
256  /* Print the node index.  */
257  dump_index (di, index);
258  /* And the type of node this is.  */
259  if (dni->binfo_p)
260    code_name = "binfo";
261  else
262    code_name = tree_code_name[(int) TREE_CODE (t)];
263  fprintf (di->stream, "%-16s ", code_name);
264  di->column = 25;
265
266  /* Figure out what kind of node this is.  */
267  code = TREE_CODE (t);
268  code_class = TREE_CODE_CLASS (code);
269
270  /* Although BINFOs are TREE_VECs, we dump them specially so as to be
271     more informative.  */
272  if (dni->binfo_p)
273    {
274      if (TREE_VIA_PUBLIC (t))
275	dump_string (di, "pub");
276      else if (TREE_VIA_PROTECTED (t))
277	dump_string (di, "prot");
278      else if (TREE_VIA_PRIVATE (t))
279	dump_string (di, "priv");
280      if (TREE_VIA_VIRTUAL (t))
281	dump_string (di, "virt");
282
283      dump_child ("type", BINFO_TYPE (t));
284      dump_child ("base", BINFO_BASETYPES (t));
285
286      goto done;
287    }
288
289  /* We can knock off a bunch of expression nodes in exactly the same
290     way.  */
291  if (IS_EXPR_CODE_CLASS (code_class))
292    {
293      /* If we're dumping children, dump them now.  */
294      queue_and_dump_type (di, t);
295
296      switch (code_class)
297	{
298	case '1':
299	  dump_child ("op 0", TREE_OPERAND (t, 0));
300	  break;
301
302	case '2':
303	case '<':
304	  dump_child ("op 0", TREE_OPERAND (t, 0));
305	  dump_child ("op 1", TREE_OPERAND (t, 1));
306	  break;
307
308	case 'e':
309	  /* These nodes are handled explicitly below.  */
310	  break;
311
312	default:
313	  abort ();
314	}
315    }
316  else if (DECL_P (t))
317    {
318      /* All declarations have names.  */
319      if (DECL_NAME (t))
320	dump_child ("name", DECL_NAME (t));
321      if (DECL_ASSEMBLER_NAME_SET_P (t)
322	  && DECL_ASSEMBLER_NAME (t) != DECL_NAME (t))
323	dump_child ("mngl", DECL_ASSEMBLER_NAME (t));
324      /* And types.  */
325      queue_and_dump_type (di, t);
326      dump_child ("scpe", DECL_CONTEXT (t));
327      /* And a source position.  */
328      if (DECL_SOURCE_FILE (t))
329	{
330	  const char *filename = strrchr (DECL_SOURCE_FILE (t), '/');
331	  if (!filename)
332	    filename = DECL_SOURCE_FILE (t);
333	  else
334	    /* Skip the slash.  */
335	    ++filename;
336
337	  dump_maybe_newline (di);
338	  fprintf (di->stream, "srcp: %s:%-6d ", filename,
339		   DECL_SOURCE_LINE (t));
340	  di->column += 6 + strlen (filename) + 8;
341	}
342      /* And any declaration can be compiler-generated.  */
343      if (DECL_ARTIFICIAL (t))
344	dump_string (di, "artificial");
345      if (TREE_CHAIN (t) && !dump_flag (di, TDF_SLIM, NULL))
346	dump_child ("chan", TREE_CHAIN (t));
347    }
348  else if (code_class == 't')
349    {
350      /* All types have qualifiers.  */
351      int quals = (*lang_hooks.tree_dump.type_quals) (t);
352
353      if (quals != TYPE_UNQUALIFIED)
354	{
355	  fprintf (di->stream, "qual: %c%c%c     ",
356		   (quals & TYPE_QUAL_CONST) ? 'c' : ' ',
357		   (quals & TYPE_QUAL_VOLATILE) ? 'v' : ' ',
358		   (quals & TYPE_QUAL_RESTRICT) ? 'r' : ' ');
359	  di->column += 14;
360	}
361
362      /* All types have associated declarations.  */
363      dump_child ("name", TYPE_NAME (t));
364
365      /* All types have a main variant.  */
366      if (TYPE_MAIN_VARIANT (t) != t)
367	dump_child ("unql", TYPE_MAIN_VARIANT (t));
368
369      /* And sizes.  */
370      dump_child ("size", TYPE_SIZE (t));
371
372      /* All types have alignments.  */
373      dump_int (di, "algn", TYPE_ALIGN (t));
374    }
375  else if (code_class == 'c')
376    /* All constants can have types.  */
377    queue_and_dump_type (di, t);
378
379  /* Give the language-specific code a chance to print something.  If
380     it's completely taken care of things, don't bother printing
381     anything more ourselves.  */
382  if ((*lang_hooks.tree_dump.dump_tree) (di, t))
383    goto done;
384
385  /* Now handle the various kinds of nodes.  */
386  switch (code)
387    {
388      int i;
389
390    case IDENTIFIER_NODE:
391      dump_string_field (di, "strg", IDENTIFIER_POINTER (t));
392      dump_int (di, "lngt", IDENTIFIER_LENGTH (t));
393      break;
394
395    case TREE_LIST:
396      dump_child ("purp", TREE_PURPOSE (t));
397      dump_child ("valu", TREE_VALUE (t));
398      dump_child ("chan", TREE_CHAIN (t));
399      break;
400
401    case TREE_VEC:
402      dump_int (di, "lngt", TREE_VEC_LENGTH (t));
403      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
404	{
405	  char buffer[32];
406	  sprintf (buffer, "%u", i);
407	  dump_child (buffer, TREE_VEC_ELT (t, i));
408	}
409      break;
410
411    case INTEGER_TYPE:
412    case ENUMERAL_TYPE:
413      dump_int (di, "prec", TYPE_PRECISION (t));
414      if (TREE_UNSIGNED (t))
415	dump_string (di, "unsigned");
416      dump_child ("min", TYPE_MIN_VALUE (t));
417      dump_child ("max", TYPE_MAX_VALUE (t));
418
419      if (code == ENUMERAL_TYPE)
420	dump_child ("csts", TYPE_VALUES (t));
421      break;
422
423    case REAL_TYPE:
424      dump_int (di, "prec", TYPE_PRECISION (t));
425      break;
426
427    case POINTER_TYPE:
428      dump_child ("ptd", TREE_TYPE (t));
429      break;
430
431    case REFERENCE_TYPE:
432      dump_child ("refd", TREE_TYPE (t));
433      break;
434
435    case METHOD_TYPE:
436      dump_child ("clas", TYPE_METHOD_BASETYPE (t));
437      /* Fall through.  */
438
439    case FUNCTION_TYPE:
440      dump_child ("retn", TREE_TYPE (t));
441      dump_child ("prms", TYPE_ARG_TYPES (t));
442      break;
443
444    case ARRAY_TYPE:
445      dump_child ("elts", TREE_TYPE (t));
446      dump_child ("domn", TYPE_DOMAIN (t));
447      break;
448
449    case RECORD_TYPE:
450    case UNION_TYPE:
451      if (TREE_CODE (t) == RECORD_TYPE)
452	dump_string (di, "struct");
453      else
454	dump_string (di, "union");
455
456      dump_child ("flds", TYPE_FIELDS (t));
457      dump_child ("fncs", TYPE_METHODS (t));
458      queue_and_dump_index (di, "binf", TYPE_BINFO (t),
459			    DUMP_BINFO);
460      break;
461
462    case CONST_DECL:
463      dump_child ("cnst", DECL_INITIAL (t));
464      break;
465
466    case VAR_DECL:
467    case PARM_DECL:
468    case FIELD_DECL:
469    case RESULT_DECL:
470      if (TREE_CODE (t) == PARM_DECL)
471	dump_child ("argt", DECL_ARG_TYPE (t));
472      else
473	dump_child ("init", DECL_INITIAL (t));
474      dump_child ("size", DECL_SIZE (t));
475      dump_int (di, "algn", DECL_ALIGN (t));
476
477      if (TREE_CODE (t) == FIELD_DECL)
478	{
479	  if (DECL_FIELD_OFFSET (t))
480	    dump_child ("bpos", bit_position (t));
481	}
482      else if (TREE_CODE (t) == VAR_DECL
483	       || TREE_CODE (t) == PARM_DECL)
484	{
485	  dump_int (di, "used", TREE_USED (t));
486	  if (DECL_REGISTER (t))
487	    dump_string (di, "register");
488	}
489      break;
490
491    case FUNCTION_DECL:
492      dump_child ("args", DECL_ARGUMENTS (t));
493      if (DECL_EXTERNAL (t))
494	dump_string (di, "undefined");
495      if (TREE_PUBLIC (t))
496	dump_string (di, "extern");
497      else
498	dump_string (di, "static");
499      if (DECL_LANG_SPECIFIC (t) && !dump_flag (di, TDF_SLIM, t))
500	dump_child ("body", DECL_SAVED_TREE (t));
501      break;
502
503    case INTEGER_CST:
504      if (TREE_INT_CST_HIGH (t))
505	dump_int (di, "high", TREE_INT_CST_HIGH (t));
506      dump_int (di, "low", TREE_INT_CST_LOW (t));
507      break;
508
509    case STRING_CST:
510      fprintf (di->stream, "strg: %-7s ", TREE_STRING_POINTER (t));
511      dump_int (di, "lngt", TREE_STRING_LENGTH (t));
512      break;
513
514    case TRUTH_NOT_EXPR:
515    case ADDR_EXPR:
516    case INDIRECT_REF:
517    case CLEANUP_POINT_EXPR:
518    case SAVE_EXPR:
519      /* These nodes are unary, but do not have code class `1'.  */
520      dump_child ("op 0", TREE_OPERAND (t, 0));
521      break;
522
523    case TRUTH_ANDIF_EXPR:
524    case TRUTH_ORIF_EXPR:
525    case INIT_EXPR:
526    case MODIFY_EXPR:
527    case COMPONENT_REF:
528    case COMPOUND_EXPR:
529    case ARRAY_REF:
530    case PREDECREMENT_EXPR:
531    case PREINCREMENT_EXPR:
532    case POSTDECREMENT_EXPR:
533    case POSTINCREMENT_EXPR:
534      /* These nodes are binary, but do not have code class `2'.  */
535      dump_child ("op 0", TREE_OPERAND (t, 0));
536      dump_child ("op 1", TREE_OPERAND (t, 1));
537      break;
538
539    case COND_EXPR:
540      dump_child ("op 0", TREE_OPERAND (t, 0));
541      dump_child ("op 1", TREE_OPERAND (t, 1));
542      dump_child ("op 2", TREE_OPERAND (t, 2));
543      break;
544
545    case CALL_EXPR:
546      dump_child ("fn", TREE_OPERAND (t, 0));
547      dump_child ("args", TREE_OPERAND (t, 1));
548      break;
549
550    case CONSTRUCTOR:
551      dump_child ("elts", TREE_OPERAND (t, 1));
552      break;
553
554    case BIND_EXPR:
555      dump_child ("vars", TREE_OPERAND (t, 0));
556      dump_child ("body", TREE_OPERAND (t, 1));
557      break;
558
559    case LOOP_EXPR:
560      dump_child ("body", TREE_OPERAND (t, 0));
561      break;
562
563    case EXIT_EXPR:
564      dump_child ("cond", TREE_OPERAND (t, 0));
565      break;
566
567    case TARGET_EXPR:
568      dump_child ("decl", TREE_OPERAND (t, 0));
569      dump_child ("init", TREE_OPERAND (t, 1));
570      dump_child ("clnp", TREE_OPERAND (t, 2));
571      /* There really are two possible places the initializer can be.
572	 After RTL expansion, the second operand is moved to the
573	 position of the fourth operand, and the second operand
574	 becomes NULL.  */
575      dump_child ("init", TREE_OPERAND (t, 3));
576      break;
577
578    case EXPR_WITH_FILE_LOCATION:
579      dump_child ("expr", EXPR_WFL_NODE (t));
580      break;
581
582    default:
583      /* There are no additional fields to print.  */
584      break;
585    }
586
587 done:
588  if (dump_flag (di, TDF_ADDRESS, NULL))
589    dump_pointer (di, "addr", (void *)t);
590
591  /* Terminate the line.  */
592  fprintf (di->stream, "\n");
593}
594
595/* Return nonzero if FLAG has been specified for the dump, and NODE
596   is not the root node of the dump.  */
597
598int dump_flag (di, flag, node)
599     dump_info_p di;
600     int flag;
601     tree node;
602{
603  return (di->flags & flag) && (node != di->node);
604}
605
606/* Dump T, and all its children, on STREAM.  */
607
608void
609dump_node (t, flags, stream)
610     tree t;
611     int flags;
612     FILE *stream;
613{
614  struct dump_info di;
615  dump_queue_p dq;
616  dump_queue_p next_dq;
617
618  /* Initialize the dump-information structure.  */
619  di.stream = stream;
620  di.index = 0;
621  di.column = 0;
622  di.queue = 0;
623  di.queue_end = 0;
624  di.free_list = 0;
625  di.flags = flags;
626  di.node = t;
627  di.nodes = splay_tree_new (splay_tree_compare_pointers, 0,
628			     (splay_tree_delete_value_fn) &free);
629
630  /* Queue up the first node.  */
631  queue (&di, t, DUMP_NONE);
632
633  /* Until the queue is empty, keep dumping nodes.  */
634  while (di.queue)
635    dequeue_and_dump (&di);
636
637  /* Now, clean up.  */
638  for (dq = di.free_list; dq; dq = next_dq)
639    {
640      next_dq = dq->next;
641      free (dq);
642    }
643  splay_tree_delete (di.nodes);
644}
645
646/* Define a tree dump switch.  */
647struct dump_file_info
648{
649  const char *const suffix;	/* suffix to give output file.  */
650  const char *const swtch;	/* command line switch */
651  int flags;			/* user flags */
652  int state;			/* state of play */
653};
654
655/* Table of tree dump switches. This must be consistent with the
656   TREE_DUMP_INDEX enumeration in tree.h */
657static struct dump_file_info dump_files[TDI_end] =
658{
659  {".tu", "dump-translation-unit", 0, 0},
660  {".class", "dump-class-hierarchy", 0, 0},
661  {".original", "dump-tree-original", 0, 0},
662  {".optimized", "dump-tree-optimized", 0, 0},
663  {".inlined", "dump-tree-inlined", 0, 0},
664};
665
666/* Define a name->number mapping for a dump flag value.  */
667struct dump_option_value_info
668{
669  const char *const name;	/* the name of the value */
670  const int value;		/* the value of the name */
671};
672
673/* Table of dump options. This must be consistent with the TDF_* flags
674   in tree.h */
675static const struct dump_option_value_info dump_options[] =
676{
677  {"address", TDF_ADDRESS},
678  {"slim", TDF_SLIM},
679  {"all", ~0},
680  {NULL, 0}
681};
682
683/* Begin a tree dump for PHASE. Stores any user supplied flag in
684   *FLAG_PTR and returns a stream to write to. If the dump is not
685   enabled, returns NULL.
686   Multiple calls will reopen and append to the dump file.  */
687
688FILE *
689dump_begin (phase, flag_ptr)
690     enum tree_dump_index phase;
691     int *flag_ptr;
692{
693  FILE *stream;
694  char *name;
695
696  if (!dump_files[phase].state)
697    return NULL;
698
699  name = concat (dump_base_name, dump_files[phase].suffix, NULL);
700  stream = fopen (name, dump_files[phase].state < 0 ? "w" : "a");
701  if (!stream)
702    error ("could not open dump file `%s'", name);
703  else
704    dump_files[phase].state = 1;
705  free (name);
706  if (flag_ptr)
707    *flag_ptr = dump_files[phase].flags;
708
709  return stream;
710}
711
712/* Returns nonzero if tree dump PHASE is enabled.  */
713
714int
715dump_enabled_p (phase)
716     enum tree_dump_index phase;
717{
718  return dump_files[phase].state;
719}
720
721/* Returns the switch name of PHASE.  */
722
723const char *
724dump_flag_name (phase)
725     enum tree_dump_index phase;
726{
727  return dump_files[phase].swtch;
728}
729
730/* Finish a tree dump for PHASE. STREAM is the stream created by
731   dump_begin.  */
732
733void
734dump_end (phase, stream)
735     enum tree_dump_index phase ATTRIBUTE_UNUSED;
736     FILE *stream;
737{
738  fclose (stream);
739}
740
741/* Parse ARG as a dump switch. Return nonzero if it is, and store the
742   relevant details in the dump_files array.  */
743
744int
745dump_switch_p (arg)
746     const char *arg;
747{
748  unsigned ix;
749  const char *option_value;
750
751  for (ix = 0; ix != TDI_end; ix++)
752    if ((option_value = skip_leading_substring (arg, dump_files[ix].swtch)))
753      {
754	const char *ptr = option_value;
755	int flags = 0;
756
757	while (*ptr)
758	  {
759	    const struct dump_option_value_info *option_ptr;
760	    const char *end_ptr;
761	    unsigned length;
762
763	    while (*ptr == '-')
764	      ptr++;
765	    end_ptr = strchr (ptr, '-');
766	    if (!end_ptr)
767	      end_ptr = ptr + strlen (ptr);
768	    length = end_ptr - ptr;
769
770	    for (option_ptr = dump_options; option_ptr->name;
771		 option_ptr++)
772	      if (strlen (option_ptr->name) == length
773		  && !memcmp (option_ptr->name, ptr, length))
774		{
775		  flags |= option_ptr->value;
776		  goto found;
777		}
778	    warning ("ignoring unknown option `%.*s' in `-f%s'",
779		     length, ptr, dump_files[ix].swtch);
780	  found:;
781	    ptr = end_ptr;
782	  }
783
784	dump_files[ix].state = -1;
785	dump_files[ix].flags = flags;
786
787	return 1;
788      }
789  return 0;
790}
791