Add diclaimer of copyright to _osname() manual page.
[unix-history] / gnu / usr.bin / gcc1 / cc1 / genoutput.c
CommitLineData
15637ed4
RG
1/* Generate code from to output assembler insns as recognized from rtl.
2 Copyright (C) 1987, 1988 Free Software Foundation, Inc.
3
4This file is part of GNU CC.
5
6GNU CC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU CC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU CC; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21/* This program reads the machine description for the compiler target machine
22 and produces a file containing three things:
23
24 1, An array of strings `insn_template' which is indexed by insn code number
25 and contains the template for output of that insn,
26
27 2. An array of ints `insn_n_operands' which is indexed by insn code number
28 and contains the number of distinct operands in the pattern for that insn,
29
30 3. An array of ints `insn_n_dups' which is indexed by insn code number
31 and contains the number of match_dup's that appear in the insn's pattern.
32 This says how many elements of `recog_dup_loc' are significant
33 after an insn has been recognized.
34
35 4. An array of arrays of operand constraint strings,
36 `insn_operand_constraint',
37 indexed first by insn code number and second by operand number,
38 containing the constraint for that operand.
39
40 This array is generated only if register constraints appear in
41 match_operand rtx's.
42
43 5. An array of arrays of chars which indicate which operands of
44 which insn patterns appear within ADDRESS rtx's. This array is
45 called `insn_operand_address_p' and is generated only if there
46 are *no* register constraints in the match_operand rtx's.
47
48 6. An array of arrays of machine modes, `insn_operand_mode',
49 indexed first by insn code number and second by operand number,
50 containing the machine mode that that operand is supposed to have.
51 Also `insn_operand_strict_low', which is nonzero for operands
52 contained in a STRICT_LOW_PART.
53
54 7. An array of arrays of int-valued functions, `insn_operand_predicate',
55 indexed first by insn code number and second by operand number,
56 containing the match_operand predicate for this operand.
57
58 8. An array of functions `insn_gen_function' which, indexed
59 by insn code number, gives the function to generate a body
60 for that patter, given operands as arguments.
61
62 9. A function `output_insn_hairy' which is called with two arguments
63 (an insn code number and a vector of operand value rtx's)
64 and returns a template to use for output of that insn.
65 This is used only in the cases where the template is not constant.
66 These cases are specified by a * at the beginning of the template string
67 in the machine description. They are identified for the sake of
68 other parts of the compiler by a zero element in `insn_template'.
69
70 10. An array of structures, `insn_machine_info', that gives machine-specific
71 information about the insn.
72
73 11. An array of ints, `insn_n_alternatives', that gives the number
74 of alternatives in the constraints of each pattern.
75
76The code number of an insn is simply its position in the machine description;
77code numbers are assigned sequentially to entries in the description,
78starting with code number 0.
79
80Thus, the following entry in the machine description
81
82 (define_insn "clrdf"
83 [(set (match_operand:DF 0 "general_operand" "")
84 (const_int 0))]
85 ""
86 "clrd %0")
87
88assuming it is the 25th entry present, would cause
89insn_template[24] to be "clrd %0", and insn_n_operands[24] to be 1.
90It would not make an case in output_insn_hairy because the template
91given in the entry is a constant (it does not start with `*'). */
92\f
93#include <stdio.h>
94#include "config.h"
95#include "rtl.h"
96#include "obstack.h"
97
98/* No instruction can have more operands than this.
99 Sorry for this arbitrary limit, but what machine will
100 have an instruction with this many operands? */
101
102#define MAX_MAX_OPERANDS 40
103
104struct obstack obstack;
105struct obstack *rtl_obstack = &obstack;
106
107#define obstack_chunk_alloc xmalloc
108#define obstack_chunk_free free
109extern int xmalloc ();
110extern void free ();
111
112void fatal ();
113void fancy_abort ();
114void error ();
115void mybcopy ();
116void mybzero ();
117
118/* insns in the machine description are assigned sequential code numbers
119 that are used by insn-recog.c (produced by genrecog) to communicate
120 to insn-output.c (produced by this program). */
121
122int next_code_number;
123
124/* Record in this chain all information that we will output,
125 associated with the code number of the insn. */
126
127struct data
128{
129 int code_number;
130 char *name;
131 char *template; /* string such as "movl %1,%0" */
132 int n_operands; /* Number of operands this insn recognizes */
133 int n_dups; /* Number times match_dup appears in pattern */
134 int n_alternatives; /* Number of alternatives in each constraint */
135 struct data *next;
136 char *constraints[MAX_MAX_OPERANDS];
137 /* Number of alternatives in constraints of operand N. */
138 int op_n_alternatives[MAX_MAX_OPERANDS];
139 char *predicates[MAX_MAX_OPERANDS];
140 char address_p[MAX_MAX_OPERANDS];
141 enum machine_mode modes[MAX_MAX_OPERANDS];
142 char strict_low[MAX_MAX_OPERANDS];
143 char outfun; /* Nonzero means this has an output function */
144 char *machine_info; /* machine-specific info string. */
145};
146
147/* This variable points to the first link in the chain. */
148
149struct data *insn_data;
150
151/* Pointer to the last link in the chain, so new elements
152 can be added at the end. */
153
154struct data *end_of_insn_data;
155
156/* Nonzero if any match_operand has a constraint string;
157 implies that REGISTER_CONSTRAINTS will be defined
158 for this machine description. */
159
160int have_constraints;
161\f
162void
163output_prologue ()
164{
165
166 printf ("/* Generated automatically by the program `genoutput'\n\
167from the machine description file `md'. */\n\n");
168
169 printf ("#include \"config.h\"\n");
170 printf ("#include \"rtl.h\"\n");
171 printf ("#include \"regs.h\"\n");
172 printf ("#include \"hard-reg-set.h\"\n");
173 printf ("#include \"real.h\"\n");
174 printf ("#include \"conditions.h\"\n");
175 printf ("#include \"insn-flags.h\"\n");
176 printf ("#include \"insn-config.h\"\n\n");
177
178 printf ("#ifndef __STDC__\n");
179 printf ("#define const\n");
180 printf ("#endif\n\n");
181
182 printf ("#include \"output.h\"\n");
183 printf ("#include \"aux-output.c\"\n\n");
184
185 /* Make sure there is at least a dummy definition of INSN_MACHINE_INFO. */
186 printf ("#ifndef INSN_MACHINE_INFO\n");
187 printf ("#define INSN_MACHINE_INFO struct dummy1 {int i;}\n");
188 printf ("#endif\n\n");
189}
190
191void
192output_epilogue ()
193{
194 register struct data *d;
195
196 printf ("\nchar * const insn_template[] =\n {\n");
197 for (d = insn_data; d; d = d->next)
198 {
199 if (d->template)
200 printf (" \"%s\",\n", d->template);
201 else
202 printf (" 0,\n");
203 }
204 printf (" };\n");
205
206 printf ("\nchar *(*const insn_outfun[])() =\n {\n");
207 for (d = insn_data; d; d = d->next)
208 {
209 if (d->outfun)
210 printf (" output_%d,\n", d->code_number);
211 else
212 printf (" 0,\n");
213 }
214 printf (" };\n");
215
216 printf ("\nrtx (*const insn_gen_function[]) () =\n {\n");
217 for (d = insn_data; d; d = d->next)
218 {
219 if (d->name)
220 printf (" gen_%s,\n", d->name);
221 else
222 printf (" 0,\n");
223 }
224 printf (" };\n");
225
226 printf ("\nconst int insn_n_operands[] =\n {\n");
227 for (d = insn_data; d; d = d->next)
228 {
229 printf (" %d,\n", d->n_operands);
230 }
231 printf (" };\n");
232
233 printf ("\nconst int insn_n_dups[] =\n {\n");
234 for (d = insn_data; d; d = d->next)
235 {
236 printf (" %d,\n", d->n_dups);
237 }
238 printf (" };\n");
239
240 if (have_constraints)
241 {
242 printf ("\nchar *const insn_operand_constraint[][MAX_RECOG_OPERANDS] =\n {\n");
243 for (d = insn_data; d; d = d->next)
244 {
245 register int i, n = 0, start;
246 printf (" {");
247 /* Make sure all the operands have the same number of
248 alternatives in their constraints.
249 Let N be that number. */
250 for (start = 0; start < d->n_operands; start++)
251 if (d->op_n_alternatives[start] > 0)
252 {
253 if (n == 0)
254 n = d->op_n_alternatives[start];
255 else if (n != d->op_n_alternatives[start])
256 error ("wrong number of alternatives in operand %d of insn number %d",
257 start, d->code_number);
258 }
259 /* Record the insn's overall number of alternatives. */
260 d->n_alternatives = n;
261
262 for (i = 0; i < d->n_operands; i++)
263 {
264 if (d->constraints[i] == 0)
265 printf (" \"\",");
266 else
267 printf (" \"%s\",", d->constraints[i]);
268 }
269 if (d->n_operands == 0)
270 printf (" 0");
271 printf (" },\n");
272 }
273 printf (" };\n");
274 }
275 else
276 {
277 printf ("\nconst char insn_operand_address_p[][MAX_RECOG_OPERANDS] =\n {\n");
278 for (d = insn_data; d; d = d->next)
279 {
280 register int i;
281 printf (" {");
282 for (i = 0; i < d->n_operands; i++)
283 printf (" %d,", d->address_p[i]);
284 if (d->n_operands == 0)
285 printf (" 0");
286 printf (" },\n");
287 }
288 printf (" };\n");
289 }
290
291 printf ("\nconst enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS] =\n {\n");
292 for (d = insn_data; d; d = d->next)
293 {
294 register int i;
295 printf (" {");
296 for (i = 0; i < d->n_operands; i++)
297 printf (" %smode,", GET_MODE_NAME (d->modes[i]));
298 if (d->n_operands == 0)
299 printf (" VOIDmode");
300 printf (" },\n");
301 }
302 printf (" };\n");
303
304 printf ("\nconst char insn_operand_strict_low[][MAX_RECOG_OPERANDS] =\n {\n");
305 for (d = insn_data; d; d = d->next)
306 {
307 register int i;
308 printf (" {");
309 for (i = 0; i < d->n_operands; i++)
310 printf (" %d,", d->strict_low[i]);
311 if (d->n_operands == 0)
312 printf (" 0");
313 printf (" },\n");
314 }
315 printf (" };\n");
316
317 printf ("\nint (*const insn_operand_predicate[][MAX_RECOG_OPERANDS])() =\n {\n");
318 for (d = insn_data; d; d = d->next)
319 {
320 register int i;
321 printf (" {");
322 for (i = 0; i < d->n_operands; i++)
323 printf (" %s,", ((d->predicates[i] && d->predicates[i][0])
324 ? d->predicates[i] : "0"));
325 if (d->n_operands == 0)
326 printf (" 0");
327 printf (" },\n");
328 }
329 printf (" };\n");
330
331 printf ("\n#ifndef DEFAULT_MACHINE_INFO\n#define DEFAULT_MACHINE_INFO 0\n");
332 printf ("#endif\n\nconst INSN_MACHINE_INFO insn_machine_info[] =\n {\n");
333 for (d = insn_data; d; d = d->next)
334 {
335 if (d->machine_info)
336 printf (" {%s},\n", d->machine_info);
337 else
338 printf(" { DEFAULT_MACHINE_INFO },\n");
339 }
340 printf(" };\n");
341
342 printf ("\nconst int insn_n_alternatives[] =\n {\n");
343 for (d = insn_data; d; d = d->next)
344 {
345 if (d->n_alternatives)
346 printf (" %d,\n", d->n_alternatives);
347 else
348 printf(" 0,\n");
349 }
350 printf(" };\n");
351}
352\f
353/* scan_operands (X) stores in max_opno the largest operand
354 number present in X, if that is larger than the previous
355 value of max_opno. It stores all the constraints in `constraints'
356 and all the machine modes in `modes'.
357
358 THIS_ADDRESS_P is nonzero if the containing rtx was an ADDRESS.
359 THIS_STRICT_LOW is nonzero if the containing rtx was a STRICT_LOW_PART. */
360
361int max_opno;
362int num_dups;
363char *constraints[MAX_MAX_OPERANDS];
364int op_n_alternatives[MAX_MAX_OPERANDS];
365char *predicates[MAX_MAX_OPERANDS];
366char address_p[MAX_MAX_OPERANDS];
367enum machine_mode modes[MAX_MAX_OPERANDS];
368char strict_low[MAX_MAX_OPERANDS];
369
370void
371scan_operands (part, this_address_p, this_strict_low)
372 rtx part;
373 int this_address_p;
374 int this_strict_low;
375{
376 register int i, j;
377 register RTX_CODE code;
378 register char *format_ptr;
379
380 if (part == 0)
381 return;
382
383 code = GET_CODE (part);
384
385 if (code == MATCH_OPERAND)
386 {
387 int opno = XINT (part, 0);
388 if (opno > max_opno)
389 max_opno = opno;
390 if (max_opno >= MAX_MAX_OPERANDS)
391 error ("Too many operands (%d) in one instruction pattern.\n",
392 max_opno + 1);
393 modes[opno] = GET_MODE (part);
394 strict_low[opno] = this_strict_low;
395 predicates[opno] = XSTR (part, 1);
396 constraints[opno] = XSTR (part, 2);
397 if (XSTR (part, 2) != 0 && *XSTR (part, 2) != 0)
398 {
399 op_n_alternatives[opno] = n_occurrences (',', XSTR (part, 2)) + 1;
400 have_constraints = 1;
401 }
402 address_p[opno] = this_address_p;
403 return;
404 }
405
406 if (code == MATCH_OPERATOR)
407 {
408 int opno = XINT (part, 0);
409 if (opno > max_opno)
410 max_opno = opno;
411 if (max_opno >= MAX_MAX_OPERANDS)
412 error ("Too many operands (%d) in one instruction pattern.\n",
413 max_opno + 1);
414 modes[opno] = GET_MODE (part);
415 strict_low[opno] = 0;
416 predicates[opno] = XSTR (part, 1);
417 constraints[opno] = 0;
418 address_p[opno] = 0;
419 for (i = 0; i < XVECLEN (part, 2); i++)
420 scan_operands (XVECEXP (part, 2, i), 0, 0);
421 return;
422 }
423
424 if (code == MATCH_DUP)
425 {
426 ++num_dups;
427 return;
428 }
429
430 if (code == ADDRESS)
431 {
432 scan_operands (XEXP (part, 0), 1, 0);
433 return;
434 }
435
436 if (code == STRICT_LOW_PART)
437 {
438 scan_operands (XEXP (part, 0), 0, 1);
439 return;
440 }
441
442 format_ptr = GET_RTX_FORMAT (GET_CODE (part));
443
444 for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++)
445 switch (*format_ptr++)
446 {
447 case 'e':
448 scan_operands (XEXP (part, i), 0, 0);
449 break;
450 case 'E':
451 if (XVEC (part, i) != NULL)
452 for (j = 0; j < XVECLEN (part, i); j++)
453 scan_operands (XVECEXP (part, i, j), 0, 0);
454 break;
455 }
456}
457\f
458/* Look at a define_insn just read. Assign its code number.
459 Record on insn_data the template and the number of arguments.
460 If the insn has a hairy output action, output a function for now. */
461
462void
463gen_insn (insn)
464 rtx insn;
465{
466 register struct data *d = (struct data *) xmalloc (sizeof (struct data));
467 register int i;
468
469 d->code_number = next_code_number++;
470 if (XSTR (insn, 0)[0])
471 d->name = XSTR (insn, 0);
472 else
473 d->name = 0;
474
475 /* Build up the list in the same order as the insns are seen
476 in the machine description. */
477 d->next = 0;
478 if (end_of_insn_data)
479 end_of_insn_data->next = d;
480 else
481 insn_data = d;
482
483 end_of_insn_data = d;
484
485 max_opno = -1;
486 num_dups = 0;
487
488 mybzero (constraints, sizeof constraints);
489 mybzero (op_n_alternatives, sizeof op_n_alternatives);
490 mybzero (predicates, sizeof predicates);
491 mybzero (address_p, sizeof address_p);
492 mybzero (modes, sizeof modes);
493 mybzero (strict_low, sizeof strict_low);
494 for (i = 0; i < XVECLEN (insn, 1); i++)
495 scan_operands (XVECEXP (insn, 1, i), 0, 0);
496 d->n_operands = max_opno + 1;
497 d->n_dups = num_dups;
498 mybcopy (constraints, d->constraints, sizeof constraints);
499 mybcopy (op_n_alternatives, d->op_n_alternatives, sizeof op_n_alternatives);
500 mybcopy (predicates, d->predicates, sizeof predicates);
501 mybcopy (address_p, d->address_p, sizeof address_p);
502 mybcopy (modes, d->modes, sizeof modes);
503 mybcopy (strict_low, d->strict_low, sizeof strict_low);
504 d->machine_info = XSTR (insn, 4);
505
506 /* We need to consider only the instructions whose assembler code template
507 starts with a *. These are the ones where the template is really
508 C code to run to decide on a template to use.
509 So for all others just return now. */
510
511 if (XSTR (insn, 3)[0] != '*')
512 {
513 d->template = XSTR (insn, 3);
514 d->outfun = 0;
515 return;
516 }
517
518 d->template = 0;
519 d->outfun = 1;
520
521 printf ("\nstatic char *\n");
522 printf ("output_%d (operands, insn)\n", d->code_number);
523 printf (" rtx *operands;\n");
524 printf (" rtx insn;\n");
525 printf ("{\n");
526 /* The following is done in a funny way to get around problems in
527 VAX-11 "C" on VMS. It is the equivalent of:
528 printf ("%s\n", &(XSTR (insn, 3)[1])); */
529 {
530 register char *cp = &(XSTR (insn, 3)[1]);
531 while (*cp) putchar (*cp++);
532 putchar ('\n');
533 }
534 printf ("}\n");
535}
536\f
537/* Look at a define_peephole just read. Assign its code number.
538 Record on insn_data the template and the number of arguments.
539 If the insn has a hairy output action, output it now. */
540
541void
542gen_peephole (peep)
543 rtx peep;
544{
545 register struct data *d = (struct data *) xmalloc (sizeof (struct data));
546 register int i;
547
548 d->code_number = next_code_number++;
549 d->name = 0;
550
551 /* Build up the list in the same order as the insns are seen
552 in the machine description. */
553 d->next = 0;
554 if (end_of_insn_data)
555 end_of_insn_data->next = d;
556 else
557 insn_data = d;
558
559 end_of_insn_data = d;
560
561 max_opno = -1;
562 mybzero (constraints, sizeof constraints);
563 mybzero (op_n_alternatives, sizeof op_n_alternatives);
564
565 /* Get the number of operands by scanning all the
566 patterns of the peephole optimizer.
567 But ignore all the rest of the information thus obtained. */
568 for (i = 0; i < XVECLEN (peep, 0); i++)
569 scan_operands (XVECEXP (peep, 0, i), 0, 0);
570
571 d->n_operands = max_opno + 1;
572 d->n_dups = 0;
573 mybcopy (constraints, d->constraints, sizeof constraints);
574 mybcopy (op_n_alternatives, d->op_n_alternatives, sizeof op_n_alternatives);
575 mybzero (d->predicates, sizeof predicates);
576 mybzero (d->address_p, sizeof address_p);
577 mybzero (d->modes, sizeof modes);
578 mybzero (d->strict_low, sizeof strict_low);
579 d->machine_info = XSTR (peep, 3);
580
581 /* We need to consider only the instructions whose assembler code template
582 starts with a *. These are the ones where the template is really
583 C code to run to decide on a template to use.
584 So for all others just return now. */
585
586 if (XSTR (peep, 2)[0] != '*')
587 {
588 d->template = XSTR (peep, 2);
589 d->outfun = 0;
590 return;
591 }
592
593 d->template = 0;
594 d->outfun = 1;
595
596 printf ("\nstatic char *\n");
597 printf ("output_%d (operands, insn)\n", d->code_number);
598 printf (" rtx *operands;\n");
599 printf (" rtx insn;\n");
600 printf ("{\n");
601 printf ("%s\n", &(XSTR (peep, 2)[1]));
602 printf ("}\n");
603}
604\f
605/* Process a define_expand just read. Assign its code number,
606 only for the purposes of `insn_gen_function'. */
607
608void
609gen_expand (insn)
610 rtx insn;
611{
612 register struct data *d = (struct data *) xmalloc (sizeof (struct data));
613 register int i;
614
615 d->code_number = next_code_number++;
616 if (XSTR (insn, 0)[0])
617 d->name = XSTR (insn, 0);
618 else
619 d->name = 0;
620
621 /* Build up the list in the same order as the insns are seen
622 in the machine description. */
623 d->next = 0;
624 if (end_of_insn_data)
625 end_of_insn_data->next = d;
626 else
627 insn_data = d;
628
629 end_of_insn_data = d;
630
631 max_opno = -1;
632 num_dups = 0;
633
634 /* Scan the operands to get the specified predicates and modes,
635 since expand_binop needs to know them. */
636
637 mybzero (predicates, sizeof predicates);
638 mybzero (modes, sizeof modes);
639 if (XVEC (insn, 1))
640 for (i = 0; i < XVECLEN (insn, 1); i++)
641 scan_operands (XVECEXP (insn, 1, i), 0, 0);
642 d->n_operands = max_opno + 1;
643 mybcopy (predicates, d->predicates, sizeof predicates);
644 mybcopy (modes, d->modes, sizeof modes);
645
646 mybzero (d->constraints, sizeof constraints);
647 mybzero (d->op_n_alternatives, sizeof op_n_alternatives);
648 mybzero (d->address_p, sizeof address_p);
649 mybzero (d->strict_low, sizeof strict_low);
650
651 d->n_dups = 0;
652 d->template = 0;
653 d->outfun = 0;
654 d->machine_info = 0;
655}
656\f
657int
658xmalloc (size)
659{
660 register int val = malloc (size);
661
662 if (val == 0)
663 fatal ("virtual memory exhausted");
664 return val;
665}
666
667int
668xrealloc (ptr, size)
669 char *ptr;
670 int size;
671{
672 int result = realloc (ptr, size);
673 if (!result)
674 fatal ("virtual memory exhausted");
675 return result;
676}
677
678void
679mybzero (b, length)
680 register char *b;
681 register int length;
682{
683 while (length-- > 0)
684 *b++ = 0;
685}
686
687void
688mybcopy (b1, b2, length)
689 register char *b1;
690 register char *b2;
691 register int length;
692{
693 while (length-- > 0)
694 *b2++ = *b1++;
695}
696
697void
698fatal (s, a1, a2)
699 char *s;
700{
701 fprintf (stderr, "genoutput: ");
702 fprintf (stderr, s, a1, a2);
703 fprintf (stderr, "\n");
704 exit (FATAL_EXIT_CODE);
705}
706
707/* More 'friendly' abort that prints the line and file.
708 config.h can #define abort fancy_abort if you like that sort of thing. */
709
710void
711fancy_abort ()
712{
713 fatal ("Internal gcc abort.");
714}
715
716void
717error (s, a1, a2)
718 char *s;
719{
720 fprintf (stderr, "genoutput: ");
721 fprintf (stderr, s, a1, a2);
722 fprintf (stderr, "\n");
723}
724\f
725int
726main (argc, argv)
727 int argc;
728 char **argv;
729{
730 rtx desc;
731 FILE *infile;
732 extern rtx read_rtx ();
733 register int c;
734
735 obstack_init (rtl_obstack);
736
737 if (argc <= 1)
738 fatal ("No input file name.");
739
740 infile = fopen (argv[1], "r");
741 if (infile == 0)
742 {
743 perror (argv[1]);
744 exit (FATAL_EXIT_CODE);
745 }
746
747 init_rtl ();
748
749 output_prologue ();
750 next_code_number = 0;
751 have_constraints = 0;
752
753 /* Read the machine description. */
754
755 while (1)
756 {
757 c = read_skip_spaces (infile);
758 if (c == EOF)
759 break;
760 ungetc (c, infile);
761
762 desc = read_rtx (infile);
763 if (GET_CODE (desc) == DEFINE_INSN)
764 gen_insn (desc);
765 if (GET_CODE (desc) == DEFINE_PEEPHOLE)
766 gen_peephole (desc);
767 if (GET_CODE (desc) == DEFINE_EXPAND)
768 gen_expand (desc);
769 }
770
771 output_epilogue ();
772
773 fflush (stdout);
774 exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
775}
776
777int
778n_occurrences (c, s)
779 char c;
780 char *s;
781{
782 int n = 0;
783 while (*s)
784 n += (*s++ == c);
785 return n;
786}