386BSD 0.0 development
[unix-history] / usr / src / usr.bin / gcc / cc1 / rtl.c
CommitLineData
a44fd975
WJ
1/* Allocate, read and print RTL for C-Compiler
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#include "config.h"
22#include <ctype.h>
23#include <stdio.h>
24#include "rtl.h"
25
26#include "obstack.h"
27#define obstack_chunk_alloc xmalloc
28#define obstack_chunk_free free
29extern int xmalloc ();
30extern void free ();
31
32/* Obstack used for allocating RTL objects.
33 Between functions, this is the permanent_obstack.
34 While parsing and expanding a function, this is maybepermanent_obstack
35 so we can save it if it is an inline function.
36 During optimization and output, this is temporary_obstack. */
37
38extern struct obstack *rtl_obstack;
39
40#define MIN(x,y) ((x < y) ? x : y)
41
42extern long ftell();
43\f
44/* Indexed by rtx code, gives number of operands for an rtx with that code.
45 Does NOT include rtx header data (code and links).
46 This array is initialized in init_rtl. */
47
48int rtx_length[NUM_RTX_CODE + 1];
49
50/* Indexed by rtx code, gives the name of that kind of rtx, as a C string. */
51
52#define DEF_RTL_EXPR(ENUM, NAME, FORMAT) NAME ,
53
54char *rtx_name[] = {
55#include "rtl.def" /* rtl expressions are documented here */
56};
57
58#undef DEF_RTL_EXPR
59
60/* Indexed by machine mode, gives the name of that machine mode.
61 This name does not include the letters "mode". */
62
63#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) NAME,
64
65char *mode_name[] = {
66#include "machmode.def"
67};
68
69#undef DEF_MACHMODE
70
71/* Indexed by machine mode, gives the length of the mode, in bytes.
72 GET_MODE_CLASS uses this. */
73
74#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) CLASS,
75
76enum mode_class mode_class[] = {
77#include "machmode.def"
78};
79
80#undef DEF_MACHMODE
81
82/* Indexed by machine mode, gives the length of the mode, in bytes.
83 GET_MODE_SIZE uses this. */
84
85#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \
86 (SIZE*UNITS_PER_WORD+3)/4,
87
88int mode_size[] = {
89#include "machmode.def"
90};
91
92#undef DEF_MACHMODE
93
94/* Indexed by machine mode, gives the length of the mode's subunit.
95 GET_MODE_UNIT_SIZE uses this. */
96
97#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \
98 (UNIT*UNITS_PER_WORD+3)/4,
99
100int mode_unit_size[] = {
101#include "machmode.def" /* machine modes are documented here */
102};
103
104#undef DEF_MACHMODE
105
106/* Indexed by machine mode, gives next wider natural mode
107 (QI -> HI -> SI -> DI, etc.) Widening multiply instructions
108 use this. */
109
110#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \
111 (enum machine_mode) WIDER,
112
113enum machine_mode mode_wider_mode[] = {
114#include "machmode.def" /* machine modes are documented here */
115};
116
117#undef DEF_MACHMODE
118
119/* Indexed by rtx code, gives a sequence of operand-types for
120 rtx's of that code. The sequence is a C string in which
121 each charcter describes one operand. */
122
123char *rtx_format[] = {
124 /* "*" undefined.
125 can cause a warning message
126 "0" field is unused (or used in a phase-dependent manner)
127 prints nothing
128 "i" an integer
129 prints the integer
130 "s" a pointer to a string
131 prints the string
132 "S" like "s", but optional:
133 the containing rtx may end before this operand
134 "e" a pointer to an rtl expression
135 prints the expression
136 "E" a pointer to a vector that points to a number of rtl expressions
137 prints a list of the rtl expressions
138 "u" a pointer to another insn
139 prints the uid of the insn. */
140
141#define DEF_RTL_EXPR(ENUM, NAME, FORMAT) FORMAT ,
142#include "rtl.def" /* rtl expressions are defined here */
143#undef DEF_RTL_EXPR
144};
145
146/* Names for kinds of NOTEs and REG_NOTEs. */
147
148char *note_insn_name[] = { "NOTE_INSN_FUNCTION_BEG", "NOTE_INSN_DELETED",
149 "NOTE_INSN_BLOCK_BEG", "NOTE_INSN_BLOCK_END",
150 "NOTE_INSN_LOOP_BEG", "NOTE_INSN_LOOP_END",
151 "NOTE_INSN_FUNCTION_END", "NOTE_INSN_SETJMP",
152 "NOTE_INSN_LOOP_CONT" };
153
154char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0",
155 "REG_EQUAL", "REG_RETVAL", "REG_LIBCALL",
156 "REG_NONNEG", "REG_ASM_LINE", "REG_ASM_FILE" };
157
158/* Allocate an rtx vector of N elements.
159 Store the length, and initialize all elements to zero. */
160
161rtvec
162rtvec_alloc (n)
163 int n;
164{
165 rtvec rt;
166 int i;
167
168 rt = (rtvec) obstack_alloc (rtl_obstack,
169 sizeof (struct rtvec_def)
170 + (( n - 1) * sizeof (rtunion)));
171
172 /* clear out the vector */
173 PUT_NUM_ELEM(rt, n);
174 for (i=0; i < n; i++)
175 rt->elem[i].rtvec = NULL; /* @@ not portable due to rtunion */
176
177 return rt;
178}
179
180/* Allocate an rtx of code CODE. The CODE is stored in the rtx;
181 all the rest is initialized to zero. */
182
183rtx
184rtx_alloc (code)
185 RTX_CODE code;
186{
187 rtx rt;
188 register int nelts = GET_RTX_LENGTH (code);
189 register int length = sizeof (struct rtx_def)
190 + (nelts - 1) * sizeof (rtunion);
191
192 rt = (rtx) obstack_alloc (rtl_obstack, length);
193
194 * (int *) rt = 0;
195 PUT_CODE (rt, code);
196
197 return rt;
198}
199\f
200/* Create a new copy of an rtx.
201 Recursively copies the operands of the rtx,
202 except for those few rtx codes that are sharable. */
203
204rtx
205copy_rtx (orig)
206 register rtx orig;
207{
208 register rtx copy;
209 register int i, j;
210 register RTX_CODE code;
211 register char *format_ptr;
212
213 code = GET_CODE (orig);
214
215 switch (code)
216 {
217 case REG:
218 case QUEUED:
219 case CONST_INT:
220 case CONST_DOUBLE:
221 case SYMBOL_REF:
222 case CODE_LABEL:
223 case PC:
224 case CC0:
225 return orig;
226 }
227
228 copy = rtx_alloc (code);
229 PUT_MODE (copy, GET_MODE (orig));
230 copy->in_struct = orig->in_struct;
231 copy->volatil = orig->volatil;
232 copy->unchanging = orig->unchanging;
233 copy->integrated = orig->integrated;
234
235 format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
236
237 for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
238 {
239 switch (*format_ptr++)
240 {
241 case 'e':
242 XEXP (copy, i) = XEXP (orig, i);
243 if (XEXP (orig, i) != NULL)
244 XEXP (copy, i) = copy_rtx (XEXP (orig, i));
245 break;
246
247 case 'E':
248 XVEC (copy, i) = XVEC (orig, i);
249 if (XVEC (orig, i) != NULL)
250 {
251 XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
252 for (j = 0; j < XVECLEN (copy, i); j++)
253 XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j));
254 }
255 break;
256
257 default:
258 XINT (copy, i) = XINT (orig, i);
259 break;
260 }
261 }
262 return copy;
263}
264\f
265/* Printing rtl for debugging dumps. */
266
267static FILE *outfile;
268
269char spaces[] = " ";
270
271static int sawclose = 0;
272
273/* Print IN_RTX onto OUTFILE. This is the recursive part of printing. */
274
275static void
276print_rtx (in_rtx)
277 register rtx in_rtx;
278{
279 static int indent;
280 register int i, j;
281 register char *format_ptr;
282
283 if (sawclose)
284 {
285 fprintf (outfile, "\n%s",
286 (spaces + (sizeof spaces - indent * 2)));
287 sawclose = 0;
288 }
289
290 if (in_rtx == 0)
291 {
292 fprintf (outfile, "(nil)");
293 sawclose = 1;
294 return;
295 }
296
297 /* print name of expression code */
298 fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx)));
299
300 if (in_rtx->in_struct)
301 fprintf (outfile, "/s");
302
303 if (in_rtx->volatil)
304 fprintf (outfile, "/v");
305
306 if (in_rtx->unchanging)
307 fprintf (outfile, "/u");
308
309 if (in_rtx->integrated)
310 fprintf (outfile, "/i");
311
312 if (GET_MODE (in_rtx) != VOIDmode)
313 {
314 /* Print REG_NOTE names for EXPR_LIST and INSN_LIST. */
315 if (GET_CODE (in_rtx) == EXPR_LIST || GET_CODE (in_rtx) == INSN_LIST)
316 fprintf (outfile, ":%s", GET_REG_NOTE_NAME (GET_MODE (in_rtx)));
317 else
318 fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx)));
319 }
320
321 format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx));
322
323 for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++)
324 switch (*format_ptr++)
325 {
326 case 'S':
327 case 's':
328 if (XSTR (in_rtx, i) == 0)
329 fprintf (outfile, " \"\"");
330 else
331 fprintf (outfile, " (\"%s\")", XSTR (in_rtx, i));
332 sawclose = 1;
333 break;
334
335 /* 0 indicates a field for internal use that should not be printed. */
336 case '0':
337 break;
338
339 case 'e':
340 indent += 2;
341 if (!sawclose)
342 fprintf (outfile, " ");
343 print_rtx (XEXP (in_rtx, i));
344 indent -= 2;
345 break;
346
347 case 'E':
348 indent += 2;
349 if (sawclose)
350 {
351 fprintf (outfile, "\n%s",
352 (spaces + (sizeof spaces - indent * 2)));
353 sawclose = 0;
354 }
355 fprintf (outfile, "[ ");
356 if (NULL != XVEC (in_rtx, i))
357 {
358 indent += 2;
359 if (XVECLEN (in_rtx, i))
360 sawclose = 1;
361
362 for (j = 0; j < XVECLEN (in_rtx, i); j++)
363 print_rtx (XVECEXP (in_rtx, i, j));
364
365 indent -= 2;
366 }
367 if (sawclose)
368 fprintf (outfile, "\n%s",
369 (spaces + (sizeof spaces - indent * 2)));
370
371 fprintf (outfile, "] ");
372 sawclose = 1;
373 indent -= 2;
374 break;
375
376 case 'i':
377 fprintf (outfile, " %d", XINT (in_rtx, i));
378 sawclose = 0;
379 break;
380
381 /* Print NOTE_INSN names rather than integer codes. */
382
383 case 'n':
384 if (XINT (in_rtx, i) <= 0)
385 fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i)));
386 else
387 fprintf (outfile, " %d", XINT (in_rtx, i));
388 sawclose = 0;
389 break;
390
391 case 'u':
392 if (XEXP (in_rtx, i) != NULL)
393 fprintf(outfile, " %d", INSN_UID (XEXP (in_rtx, i)));
394 else
395 fprintf(outfile, " 0");
396 sawclose = 0;
397 break;
398
399 default:
400 fprintf (stderr,
401 "switch format wrong in rtl.print_rtx(). format was: %c.\n",
402 format_ptr[-1]);
403 abort ();
404 }
405
406 fprintf (outfile, ")");
407 sawclose = 1;
408}
409
410/* Call this function from the debugger to see what X looks like. */
411
412void
413debug_rtx (x)
414 rtx x;
415{
416 outfile = stderr;
417 print_rtx (x);
418 fprintf (stderr, "\n");
419}
420
421/* External entry point for printing a chain of insns
422 starting with RTX_FIRST onto file OUTF.
423 A blank line separates insns.
424
425 If RTX_FIRST is not an insn, then it alone is printed, with no newline. */
426
427void
428print_rtl (outf, rtx_first)
429 FILE *outf;
430 rtx rtx_first;
431{
432 register rtx tmp_rtx;
433
434 outfile = outf;
435 sawclose = 0;
436
437 switch (GET_CODE (rtx_first))
438 {
439 case INSN:
440 case JUMP_INSN:
441 case CALL_INSN:
442 case NOTE:
443 case CODE_LABEL:
444 case BARRIER:
445 for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx))
446 {
447 print_rtx (tmp_rtx);
448 fprintf (outfile, "\n");
449 }
450 break;
451
452 default:
453 print_rtx (rtx_first);
454 }
455}
456\f
457/* Subroutines of read_rtx. */
458
459/* Dump code after printing a message. Used when read_rtx finds
460 invalid data. */
461
462static void
463dump_and_abort (expected_c, actual_c, infile)
464 int expected_c, actual_c;
465 FILE *infile;
466{
467 int c, i;
468
469 if (expected_c >= 0)
470 fprintf (stderr,
471 "Expected character %c. Found character %c.",
472 expected_c, actual_c);
473 fprintf (stderr, " At file position: %ld\n", ftell (infile));
474 fprintf (stderr, "Following characters are:\n\t");
475 for (i = 0; i < 200; i++)
476 {
477 c = getc (infile);
478 if (EOF == c) break;
479 putc (c, stderr);
480 }
481 fprintf (stderr, "Aborting.\n");
482 abort ();
483}
484
485/* Read chars from INFILE until a non-whitespace char
486 and return that. Comments, both Lisp style and C style,
487 are treated as whitespace.
488 Tools such as genflags use this function. */
489
490int
491read_skip_spaces (infile)
492 FILE *infile;
493{
494 register int c;
495 while (c = getc (infile))
496 {
497 if (c == ' ' || c == '\n' || c == '\t' || c == '\f')
498 ;
499 else if (c == ';')
500 {
501 while ((c = getc (infile)) && c != '\n') ;
502 }
503 else if (c == '/')
504 {
505 register int prevc;
506 c = getc (infile);
507 if (c != '*')
508 dump_and_abort ('*', c, infile);
509
510 prevc = 0;
511 while (c = getc (infile))
512 {
513 if (prevc == '*' && c == '/')
514 break;
515 prevc = c;
516 }
517 }
518 else break;
519 }
520 return c;
521}
522
523/* Read an rtx code name into the buffer STR[].
524 It is terminated by any of the punctuation chars of rtx printed syntax. */
525
526static void
527read_name (str, infile)
528 char *str;
529 FILE *infile;
530{
531 register char *p;
532 register int c;
533
534 c = read_skip_spaces(infile);
535
536 p = str;
537 while (1)
538 {
539 if (c == ' ' || c == '\n' || c == '\t' || c == '\f')
540 break;
541 if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/'
542 || c == '(' || c == '[')
543 {
544 ungetc (c, infile);
545 break;
546 }
547 *p++ = c;
548 c = getc (infile);
549 }
550 if (p == str)
551 {
552 fprintf (stderr, "missing name or number");
553 dump_and_abort (-1, -1, infile);
554 }
555
556 *p = 0;
557}
558\f
559/* Read an rtx in printed representation from INFILE
560 and return an actual rtx in core constructed accordingly.
561 read_rtx is not used in the compiler proper, but rather in
562 the utilities gen*.c that construct C code from machine descriptions. */
563
564rtx
565read_rtx (infile)
566 FILE *infile;
567{
568 register int i, j, list_counter;
569 RTX_CODE tmp_code;
570 register char *format_ptr;
571 /* tmp_char is a buffer used for reading decimal integers
572 and names of rtx types and machine modes.
573 Therefore, 256 must be enough. */
574 char tmp_char[256];
575 rtx return_rtx;
576 register int c;
577 int tmp_int;
578
579 /* Linked list structure for making RTXs: */
580 struct rtx_list
581 {
582 struct rtx_list *next;
583 rtx value; /* Value of this node... */
584 };
585
586 c = read_skip_spaces (infile); /* Should be open paren. */
587 if (c != '(')
588 dump_and_abort ('(', c, infile);
589
590 read_name (tmp_char, infile);
591
592 tmp_code = UNKNOWN;
593
594 for (i=0; i < NUM_RTX_CODE; i++) /* @@ might speed this search up */
595 {
596 if (!(strcmp (tmp_char, GET_RTX_NAME (i))))
597 {
598 tmp_code = (RTX_CODE) i; /* get value for name */
599 break;
600 }
601 }
602 if (tmp_code == UNKNOWN)
603 {
604 fprintf (stderr,
605 "Unknown rtx read in rtl.read_rtx(). Code name was %s .",
606 tmp_char);
607 }
608 /* (NIL) stands for an expression that isn't there. */
609 if (tmp_code == NIL)
610 {
611 /* Discard the closeparen. */
612 while ((c = getc (infile)) && c != ')');
613 return 0;
614 }
615
616 return_rtx = rtx_alloc (tmp_code); /* if we end up with an insn expression
617 then we free this space below. */
618 format_ptr = GET_RTX_FORMAT (GET_CODE (return_rtx));
619
620 /* If what follows is `: mode ', read it and
621 store the mode in the rtx. */
622
623 i = read_skip_spaces (infile);
624 if (i == ':')
625 {
626 register int k;
627 read_name (tmp_char, infile);
628 for (k = 0; k < NUM_MACHINE_MODES; k++)
629 if (!strcmp (GET_MODE_NAME (k), tmp_char))
630 break;
631
632 PUT_MODE (return_rtx, (enum machine_mode) k );
633 }
634 else
635 ungetc (i, infile);
636
637 for (i = 0; i < GET_RTX_LENGTH (GET_CODE (return_rtx)); i++)
638 switch (*format_ptr++)
639 {
640 /* 0 means a field for internal use only.
641 Don't expect it to be present in the input. */
642 case '0':
643 break;
644
645 case 'e':
646 case 'u':
647 XEXP (return_rtx, i) = read_rtx (infile);
648 break;
649
650 case 'E':
651 {
652 register struct rtx_list *next_rtx, *rtx_list_link;
653 struct rtx_list *list_rtx;
654
655 c = read_skip_spaces (infile);
656 if (c != '[')
657 dump_and_abort ('[', c, infile);
658
659 /* add expressions to a list, while keeping a count */
660 next_rtx = NULL;
661 list_counter = 0;
662 while ((c = read_skip_spaces (infile)) && c != ']')
663 {
664 ungetc (c, infile);
665 list_counter++;
666 rtx_list_link = (struct rtx_list *)
667 alloca (sizeof (struct rtx_list));
668 rtx_list_link->value = read_rtx (infile);
669 if (next_rtx == 0)
670 list_rtx = rtx_list_link;
671 else
672 next_rtx->next = rtx_list_link;
673 next_rtx = rtx_list_link;
674 rtx_list_link->next = 0;
675 }
676 /* get vector length and allocate it */
677 XVEC (return_rtx, i) = (list_counter
678 ? rtvec_alloc (list_counter)
679 : (struct rtvec_def *) NULL);
680 if (list_counter > 0)
681 {
682 next_rtx = list_rtx;
683 for (j = 0; j < list_counter; j++,
684 next_rtx = next_rtx->next)
685 XVECEXP (return_rtx, i, j) = next_rtx->value;
686 }
687 /* close bracket gotten */
688 }
689 break;
690
691 case 'S':
692 /* 'S' is an optional string: if a closeparen follows,
693 just store NULL for this element. */
694 c = read_skip_spaces (infile);
695 ungetc (c, infile);
696 if (c == ')')
697 {
698 XSTR (return_rtx, i) = 0;
699 break;
700 }
701
702 case 's':
703 {
704 int saw_paren = 0;
705 register char *stringbuf;
706 int stringbufsize;
707
708 c = read_skip_spaces (infile);
709 if (c == '(')
710 {
711 saw_paren = 1;
712 c = read_skip_spaces (infile);
713 }
714 if (c != '"')
715 dump_and_abort ('"', c, infile);
716 j = 0;
717 stringbufsize = 10;
718 stringbuf = (char *) xmalloc (stringbufsize + 1);
719
720 while (1)
721 {
722 if (j >= stringbufsize - 4)
723 {
724 stringbufsize *= 2;
725 stringbuf = (char *) xrealloc (stringbuf, stringbufsize + 1);
726 }
727 stringbuf[j] = getc (infile); /* Read the string */
728 if (stringbuf[j] == '\\')
729 {
730 stringbuf[j] = getc (infile); /* Read the string */
731 /* \; makes stuff for a C string constant containing
732 newline and tab. */
733 if (stringbuf[j] == ';')
734 {
735 strcpy (&stringbuf[j], "\\n\\t");
736 j += 3;
737 }
738 }
739 else if (stringbuf[j] == '"')
740 break;
741 j++;
742 }
743
744 stringbuf[j] = 0; /* NUL terminate the string */
745 stringbuf = (char *) xrealloc (stringbuf, j + 1);
746
747 if (saw_paren)
748 {
749 c = read_skip_spaces (infile);
750 if (c != ')')
751 dump_and_abort (')', c, infile);
752 }
753 XSTR (return_rtx, i) = stringbuf;
754 }
755 break;
756
757 case 'i':
758 case 'n':
759 read_name (tmp_char, infile);
760 tmp_int = atoi (tmp_char);
761 XINT (return_rtx, i) = tmp_int;
762 break;
763
764 default:
765 fprintf (stderr,
766 "switch format wrong in rtl.read_rtx(). format was: %c.\n",
767 format_ptr[-1]);
768 fprintf (stderr, "\tfile position: %ld\n", ftell (infile));
769 abort ();
770 }
771
772 c = read_skip_spaces (infile);
773 if (c != ')')
774 dump_and_abort (')', c, infile);
775
776 return return_rtx;
777}
778\f
779/* This is called once per compilation, before any rtx's are constructed.
780 It initializes the vector `rtx_length'. */
781
782void
783init_rtl ()
784{
785 int i;
786
787 for (i = 0; i < NUM_RTX_CODE; i++)
788 rtx_length[i] = strlen (rtx_format[i]);
789
790 /* Make CONST_DOUBLE bigger, if real values are bigger than
791 it normally expects to have room for.
792 Note that REAL_VALUE_TYPE is not defined by default,
793 since tree.h is not included. But the default dfn as `double'
794 would do no harm. */
795#ifdef REAL_VALUE_TYPE
796 i = sizeof (REAL_VALUE_TYPE) / sizeof (rtunion) + 2;
797 if (rtx_length[(int) CONST_DOUBLE] < i)
798 {
799 char *s = (char *) malloc (i + 1);
800 rtx_length[(int) CONST_DOUBLE] = i;
801 rtx_format[(int) CONST_DOUBLE] = s;
802 *s++ = 'e';
803 *s++ = '0';
804 /* Set the GET_RTX_FORMAT of CONST_DOUBLE to a string
805 of as many `i's as we now have elements. */
806 for (i = 0; i < rtx_length[(int) CONST_DOUBLE]; i++)
807 *s++ = 'i';
808 *s++ = 0;
809 }
810#endif
811}