Category : UNIX Files
Archive   : GPLUS6.ZIP
Filename : G6

 
Output of file : G6 contained in archive : GPLUS6.ZIP
#!/bin/sh
# This is a shell archive.
# remove everything above the "#!/bin/sh" line
# and feed to /bin/sh
#
# Contents:
# crt0.c
# input.h
# tree.h
# cplus-decl.h
# crt1.c
# integrate.c
# varasm.c
# cplus-decl2.c
# dbxout.c
# lastfile.c
# version.c
# cplus-dem.c
# dummy.c
# ld.c
# packed: Wed Jan 01 23:09:05 EST 1992
# by: kds@mine
#
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo "Extracting crt0.c..."
sed 's/^X//' >crt0.c << '!EOF'
X/* C++ code startup routine.
X Copyright (C) 1985, 1986 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X
X NO WARRANTY
X
X BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X GENERAL PUBLIC LICENSE TO COPY
X
X 1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1986 Free Software Foundation, Inc."; and include following the
Xcopyright notice a verbatim copy of the above disclaimer of warranty
Xand of this License.
X
X 2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X a) cause the modified files to carry prominent notices stating
X that you changed the files and the date of any change; and
X
X b) cause the whole of any work that you distribute or publish,
X that in whole or in part contains or is a derivative of this
X program or any part thereof, to be licensed at no charge to all
X third parties on terms identical to those contained in this
X License Agreement (except that you may choose to grant more extensive
X warranty protection to some or all third parties, at your option).
X
X c) You may charge a distribution fee for the physical act of
X transferring a copy, and you may at your option offer warranty
X protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X 3. You may copy and distribute this program (or a portion or derivative
Xof it, under Paragraph 2) in object code or executable form under the terms
Xof Paragraphs 1 and 2 above provided that you also do one of the following:
X
X a) accompany it with the complete corresponding machine-readable
X source code, which must be distributed under the terms of
X Paragraphs 1 and 2 above; or,
X
X b) accompany it with a written offer, valid for at least three
X years, to give any third party free (except for a nominal
X shipping charge) a complete machine-readable copy of the
X corresponding source code, to be distributed under the terms of
X Paragraphs 1 and 2 above; or,
X
X c) accompany it with the information you received as to where the
X corresponding source code may be obtained. (This alternative is
X allowed only for noncommercial distribution and only if you
X received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X 4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement. Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated. However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X 5. If you wish to incorporate parts of this program into other free
Xprograms whose distribution conditions are different, write to the Free
XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet
Xworked out a simple rule that can be stated here, but we will often permit
Xthis. We will be guided by the two goals of preserving the free status of
Xall derivatives of our free software and of promoting the sharing and reuse of
Xsoftware.
X
X
XIn other words, you are welcome to use, share and improve this program.
XYou are forbidden to forbid anyone else to use, share and improve
Xwhat you give them. Help stamp out software-hoarding! */
X
X
X/* The standard Vax 4.2 Unix crt0.c cannot be used for Emacs
X because it makes `envron' an initialized variable.
X It is easiest to have a special crt0.c on all machines
X though I don't know whether other machines actually need it. */
X
X/* Also, the standard crt0.c cannot be used for C++, since C++
X guarantees to call global constructors before main () is
X called, and to call global destructors after control has left from
X main (). */
X
X/* On the vax and 68000, in BSD4.2 and USG5.2,
X this is the data format on startup:
X (vax) ap and fp are unpredictable as far as I know; don't use them.
X sp -> word containing argc
X word pointing to first arg string
X [word pointing to next arg string]... 0 or more times
X 0
XOptionally:
X [word pointing to environment variable]... 1 or more times
X ...
X 0
XAnd always:
X first arg string
X [next arg string]... 0 or more times
X*/
X
X/* On the 16000, at least in the one 4.2 system I know about,
X the initial data format is
X sp -> word containing argc
X word containing argp
X word pointing to first arg string, and so on as above
X*/
X
X/* On Suns, and possibly on other machines, a routine called
X `on_exit' is used to call termination handling routines.
X If your system provides this feature, define the macro
X ON_EXIT to perform this task. It is called after a program
X calls exit(3) or returns normally, and before its process
X terminates. The routine named is called as
X
X (*procp)(status, arg);
X
X where status is the argument with which exit was called, or
X zero if main returns. Typically, arg is the address of an
X argument vector to (*procp), but may be an integer value.
X Several calls may be made to ON_EXIT, specifying several
X termination handlers. The order in which they are called is
X the reverse of that in which they were given to ON_EXIT.
X
X Some systems may impose a limit on the number of termination
X handlers which can be specified. Users near that limit are
X warned that GNU C++ will take one of those slots.
X
X Note that proper GNU C++ code will not need to rely on this
X mechanism at the application level, because thoughtfully
X constructed classes will take care of this automatically.
X There is no limit to the number of classes which GNU C++
X can destroy. 🙂 */
X
X#include "config.h"
X
Xextern void exit ();
Xextern void __do_global_init ();
Xextern void __do_global_cleanup ();
X
X/* ******** WARNING ********
X Do not insert any data definitions before data_start!
X Since this is the first file linked, the address of the following
X variable should correspond to the start of initialized data space.
X On some systems this is a constant that is independent of the text
X size for shared executables. On others, it is a function of the
X text size. In short, this seems to be the most portable way to
X discover the start of initialized data space dynamically at runtime,
X for either shared or unshared executables, on either swapping or
X virtual systems. It only requires that the linker allocate objects
X in the order encountered, a reasonable model for most Unix systems.
X Similarly, note that the address of _start() should be the start
X of text space. Fred Fish, UniSoft Systems Inc. */
X
Xint data_start = 0;
X
X#ifdef NEED_ERRNO
Xint errno = 0;
X#endif
X
X#ifndef DONT_NEED_ENVIRON
Xchar **environ;
X#endif
X
X#if defined(sequent)
X asm(" .globl _387_flt");
X asm(" .set _387_flt,0");
X#endif
X
X#ifdef convex
Xint use_libc_sema = 0;
X#endif
X
X#if defined(sun)
X#define ON_EXIT(PROCP, ARG) \
X do { extern void PROCP (); on_exit (PROCP, ARG); } while (0)
X#endif
X
X#ifndef sun386 /* Not compatible with i386, sigh */
X#if defined(i386) || defined(ALTOS)
X_start(arg)
Xchar *arg;
X{
X int argc = (int)(&arg)[-1];
X environ = &arg + argc + 1;
X#ifndef sequent
X setchrclass((char *)0);
X#endif
X __do_global_init();
X exit(main(argc, &arg, environ));
X}
X#endif
X#endif /* sun386 */
X
X#if defined(orion) || defined(pyramid) || defined(celerity) || defined(ALLIANT)
X
X#ifdef ALLIANT
X/* _start must initialize _curbrk and _minbrk on the first startup;
X when starting up after dumping, it must initialize them to what they were
X before the dumping, since they are in the shared library and
X are not dumped. See ADJUST_EXEC_HEADER in m-alliant.h. */
Xextern unsigned char *_curbrk, *_minbrk;
Xextern unsigned char end;
Xunsigned char *_setbrk = &end;
X#endif
X
X_start (argc, argv, envp)
X int argc;
X char **argv, **envp;
X{
X#ifdef ALLIANT
X _curbrk = _setbrk;
X _minbrk = _setbrk;
X#endif
X
X environ = envp;
X
X __do_global_init ();
X exit (main (argc, argv, envp));
X}
X
X#endif /* orion or pyramid or celerity or alliant */
X
X#if defined (ns16000) && !defined (sequent) && !defined (UMAX)
X
X_start ()
X{
X/* On 16000, _start pushes fp onto stack */
X start1 ();
X}
X
X/* ignore takes care of skipping the fp value pushed in start. */
Xstatic
Xstart1 (ignore, argc, argv)
X int ignore;
X int argc;
X register char **argv;
X{
X environ = argv + argc + 1;
X
X if (environ == *argv)
X environ--;
X
X __do_global_init ();
X exit (main (argc, argv, environ));
X}
X#endif /* ns16000, not sequent and not UMAX */
X
X#ifdef UMAX
X_start()
X{
X asm(" exit [] # undo enter");
X asm(" .set exitsc,1");
X asm(" .set sigcatchall,0x400");
X
X asm(" .globl _exit");
X asm(" .globl start");
X asm(" .globl __start");
X asm(" .globl _main");
X asm(" .globl _environ");
X asm(" .globl _sigvec");
X asm(" .globl sigentry");
X
X asm("start:");
X asm(" br .xstart");
X asm(" .org 0x20");
X asm(" .double p_glbl,0,0xf00000,0");
X asm(" .org 0x30");
X asm(".xstart:");
X asm(" adjspb $8");
X asm(" movd 8(sp),0(sp) # argc");
X asm(" addr 12(sp),r0");
X asm(" movd r0,4(sp) # argv");
X asm("L1:");
X asm(" movd r0,r1");
X asm(" addqd $4,r0");
X asm(" cmpqd $0,0(r1) # null args term ?");
X asm(" bne L1");
X asm(" cmpd r0,0(4(sp)) # end of 'env' or 'argv' ?");
X asm(" blt L2");
X asm(" addqd $-4,r0 # envp's are in list");
X asm("L2:");
X asm(" movd r0,8(sp) # env");
X asm(" movd r0,@_environ # indir is 0 if no env ; not 0 if env");
X asm(" movqd $0,tos # setup intermediate signal handler");
X asm(" addr @sv,tos");
X asm(" movzwd $sigcatchall,tos");
X asm(" jsr @_sigvec");
X asm(" adjspb $-12");
X asm(" jsr @___do_global_init");
X asm(" jsr @_main");
X asm(" adjspb $-12");
X asm(" movd r0,tos");
X asm(" jsr @___do_global_cleanup");
X asm(" jsr @_exit");
X asm(" adjspb $-4");
X asm(" addr @exitsc,r0");
X asm(" svc");
X asm(" .align 4 # sigvec arg");
X asm("sv:");
X asm(" .double sigentry");
X asm(" .double 0");
X asm(" .double 0");
X
X asm(" .comm p_glbl,1");
X}
X#endif /* UMAX */
X
X#ifdef CRT0_DUMMIES
X
X/* Define symbol "start": here; some systems want that symbol. */
X#ifdef DOT_GLOBAL_START
Xasm(" .text ");
Xasm(" .globl start ");
Xasm(" start: ");
X#endif /* DOT_GLOBAL_START */
X
X#ifdef NODOT_GLOBAL_START
Xasm(" text ");
Xasm(" global start ");
Xasm(" start: ");
X#endif /* NODOT_GLOBAL_START */
X
X#ifdef hp300
Xasm(" .text ");
Xasm(" .long 0 "); /* conveniently both a 0 and a nop */
X#endif
X
X_start ()
X{
X/* On vax, nothing is pushed here */
X/* On sequent, bogus fp is pushed here */
X start1 ();
X}
X
Xstatic
Xstart1 (CRT0_DUMMIES argc, xargv)
X int argc;
X char *xargv;
X{
X register char **argv = &xargv;
X environ = argv + argc + 1;
X
X if ((char *)environ == xargv)
X environ--;
X
X __do_global_init ();
X exit (main (argc, argv, environ));
X}
X#else /* not CRT0_DUMMIES */
X
X/* "m68k" and "m68000" both stand for m68000 processors,
X but with different program-entry conventions.
X This is a kludge. Now that the CRT0_DUMMIES mechanism above exists,
X most of these machines could use the vax code above
X with some suitable definition of CRT0_DUMMIES.
X Then the symbol m68k could be flushed.
X But I don't want to risk breaking these machines
X in a version 17 patch release, so that change is being put off. */
X
X#ifdef m68k /* Can't do it all from C */
X asm (" global _start");
X asm (" text");
X asm ("_start:");
X#ifndef NU
X#ifdef STRIDE
X asm (" comm havefpu%,2");
X#else /* m68k, not STRIDE */
X asm (" data");
X asm (" even");
X asm (" global splimit%");
X asm ("splimit%:");
X asm (" space 4");
X#endif /* STRIDE */
X asm (" global exit");
X asm (" text");
X#ifdef STRIDE
X asm (" trap &3");
X asm (" mov.w %d0,havefpu%");
X#else /* m68k, not STRIDE */
X asm (" mov.l %d0,splimit%");
X#endif /* STRIDE */
X#endif /* not NU */
X asm (" jsr start1");
X asm (" mov.l %d0,(%sp)");
X asm (" jsr exit");
X asm (" mov.l &1,%d0"); /* d0 = 1 => exit */
X asm (" trap &0");
X#else /* m68000, not m68k */
X
X#if defined(m68000) || defined(mc68000) || defined(mc68020) || defined(sun386)
X
X#ifdef ISI68K
X/* Added by ESM Sun May 24 12:44:02 1987 to get new ISI library to work */
X asm (" .globl is68020");
X asm ("is68020:");
X asm (" .long 0x00000000");
X asm (" .long 0xffffffff");
X/* End of stuff added by ESM */
X asm (" .text");
X asm (" .globl __start");
X asm ("__start:");
X asm (" .word 0");
X asm (" link fp,#0");
X asm (" jbsr _start1");
X asm (" unlk fp");
X asm (" rts");
X#else /* not ISI68K */
X_start ()
X{
X static start1 ();
X /* On 68000, _start pushes a6 onto stack (as does the sun386i, sort of...) */
X start1 ();
X}
X#endif /* not ISI68k */
X#endif /* m68000 || mc68000 || mc68020 || sun386 */
X#endif /* m68k */
X
X#if defined(m68k) || defined(m68000) || defined(mc68000) || defined(mc68020) || defined(sun386)
X/* ignore takes care of skipping the a6 value pushed in start. */
Xstatic
X#if defined(m68k)
Xstart1 (argc, xargv)
X#else
Xstart1 (ignore, argc, xargv)
X#endif
X int argc;
X char *xargv;
X{
X register char **argv = &xargv;
X environ = argv + argc + 1;
X
X if ((char *)environ == xargv)
X environ--;
X
X#ifdef ON_EXIT
X
X#ifdef sun
X ON_EXIT (_cleanup, 0);
X#endif
X
X ON_EXIT (__do_global_cleanup, 0);
X
X#endif
X
X __do_global_init ();
X exit (main (argc, argv, environ));
X}
X
X#endif /* m68k or m68000 or mc68000 or mc68020 or sun386 */
X
X#endif /* not CRT0_DUMMIES */
X
X/* Should "hp9000" be completely removed? */
X#if defined(hp9000) || defined(hp9000s300)
Xint argc_value;
Xchar **argv_value;
X#ifdef OLD_HP_ASSEMBLER
X asm(" text");
X asm(" globl __start");
X asm(" globl _exit");
X asm(" globl _main");
X asm("__start");
X asm(" dc.l 0");
X asm(" subq.w #0x1,d0");
X asm(" move.w d0,float_soft");
X asm(" move.l 0x4(a7),d0");
X asm(" beq.s skip_1");
X asm(" move.l d0,a0");
X asm(" clr.l -0x4(a0)");
X asm("skip_1");
X asm(" move.l a7,a0");
X asm(" subq.l #0x8,a7");
X asm(" move.l (a0),(a7)");
X asm(" move.l (a0),_argc_value");
X asm(" addq.l #0x4,a0");
X asm(" move.l a0,0x4(a7)");
X asm(" move.l a0,_argv_value");
X asm("incr_loop");
X asm(" tst.l (a0)+");
X asm(" bne.s incr_loop");
X asm(" move.l 0x4(a7),a1");
X asm(" cmp.l (a1),a0");
X asm(" blt.s skip_2");
X asm(" subq.l #0x4,a0");
X asm("skip_2");
X asm(" move.l a0,0x8(a7)");
X asm(" move.l a0,_environ");
X asm(" jsr ___do_global_init");
X asm(" jsr _main");
X asm(" addq.l #0x8,a7");
X asm(" move.l d0,-(a7)");
X asm(" jsr _exit");
X asm(" move.w #0x1,d0");
X asm(" trap #0x0");
X asm(" comm float_soft,4");
X/* float_soft is allocated in this way because C would
X put an underscore character in its name otherwise. */
X
X#else /* new hp assembler */
X
X asm(" text");
X asm(" global __start");
X asm(" global _exit");
X asm(" global _main");
X asm(" global flag_fpa,flag_68010,fpa_loc,float_loc");
X asm("__start:");
X asm(" byte 0,0,0,0");
X asm(" sub.l %a6,%a6");
X asm(" subq.w &1,%d0");
X asm(" mov.w %d0,float_soft");
X asm(" mov.w %d1,flag_68881");
X asm(" beq.b skip_float");
X asm(" fmov.l &0x7400,%fpcr");
X asm("skip_float:");
X asm(" move.l %a0,%d0");
X asm(" add.l %d0,%d0");
X asm(" subx.w %d1,%d1");
X asm(" move.w %d1,flag_68010");
X asm(" add.l %d0,%d0");
X asm(" subx.w %d1,%d1");
X asm(" move.w %d1,flag_fpa");
X asm(" mov.l 4(%a7),%d0");
X asm(" beq.b skip_1");
X asm(" mov.l %d0,%a0");
X asm(" clr.l -4(%a0)");
X asm("skip_1:");
X asm(" mov.l %a7,%a0");
X asm(" subq.l &8,%a7");
X asm(" mov.l (%a0),(%a7)");
X asm(" mov.l (%a0),_argc_value");
X asm(" addq.l &4,%a0");
X asm(" mov.l %a0,4(%a7)");
X asm(" mov.l %a0,_argv_value");
X asm("incr_loop:");
X asm(" tst.l (%a0)+");
X asm(" bne.b incr_loop");
X asm(" mov.l 4(%a7),%a1");
X asm(" cmp.l %a0,(%a1)");
X asm(" blt.b skip_2");
X asm(" subq.l &4,%a0");
X asm("skip_2:");
X asm(" mov.l %a0,8(%a7)");
X asm(" mov.l %a0,_environ");
X asm(" jsr ___do_global_init");
X asm(" jsr _main");
X asm(" addq.l &8,%a7");
X asm(" mov.l %d0,-(%a7)");
X asm(" jsr _exit");
X asm(" mov.w &1,%d0");
X asm(" trap &0");
X asm(" comm float_soft, 4");
X asm(" comm flag_68881, 4");
X asm(" comm flag_68010, 4");
X asm(" comm flag_fpa, 4");
X asm(" set float_loc,0xffffb000");
X asm(" set fpa_loc,0xfff08000");
X
X#endif /* new hp assembler */
X#endif /* hp9000 */
X
X#ifdef GOULD
X
X/* startup code has to be in near text rather
X than fartext as allocated by the C compiler. */
X asm(" .text");
X asm(" .align 2");
X asm(" .globl __start");
X asm(" .text");
X asm("__start:");
X/* setup base register b1 (function base). */
X asm(" .using b1,.");
X asm(" tpcbr b1");
X/* setup base registers b3 through b7 (data references). */
X asm(" file basevals,b3");
X/* setup base register b2 (stack pointer); it should be
X aligned on a 8-word boundary; but because it is pointing
X to argc, its value should be remembered (in r5). */
X asm(" movw b2,r4");
X asm(" movw b2,r5");
X asm(" andw #~0x1f,r4");
X asm(" movw r4,b2");
X/* allocate stack frame to do some work. */
X asm(" subea 16w,b2");
X/* initialize signal catching for UTX/32 1.2; this is

X necessary to make restart from saved image work. */
X asm(" movea sigcatch,r1");
X asm(" movw r1,8w[b2]");
X asm(" svc #1,#150");
X/* setup address of argc for start1. */
X asm(" movw r5,8w[b2]");
X asm(" func #1,_start1");
X asm(" halt");
X/* space for ld to store base register initial values. */
X asm(" .align 5");
X asm("basevals:");
X asm(" .word __base3,__base4,__base5,__base6,__base7");
X
Xstatic
Xstart1 (xargc)
X int *xargc;
X{
X register int argc;
X register char **argv;
X
X argc = *xargc;
X argv = (char **)(xargc) + 1;
X environ = argv + argc + 1;
X
X if (environ == argv)
X environ--;
X
X __do_global_init ();
X exit (main (argc, argv, environ));
X}
X
X#endif /* GOULD */
X
X#ifdef elxsi
Xextern int errno;
Xextern char **environ;
X
X_start()
X{
X errno = 0;
X environ = *(&environ + 8);
X _stdinit();
X
X __do_global_init ();
X exit (main(*(&environ + 6), *(&environ + 7), environ));
X}
X#endif /* elxsi */
X
X#ifdef sparc
Xasm (".global start");
Xasm (".text");
Xasm ("start:");
X
X/* Set up `argc', `argv', and `envp' into local registers. */
Xasm (" mov 0, %fp");
Xasm (" ld [%sp + 64], %l0");
Xasm (" add %sp, 68, %l1");
Xasm (" sll %l0, 2, %l2");
Xasm (" add %l2, 4, %l2");
Xasm (" add %l1, %l2, %l2");
Xasm (" sethi %hi(_environ), %l3");
Xasm (" st %l2, [%l3+%lo(_environ)]");
X
X#ifdef ON_EXIT
X
X#ifdef sun
Xasm (" set __cleanup,%o0");
Xasm (" call _on_exit,0");
Xasm (" mov %g0,%o1");
X#endif
X
Xasm (" set ___do_global_cleanup,%o0");
Xasm (" call _on_exit,0");
Xasm (" mov %g0,%o1");
X
X#endif
X
X/* Finish diddling with stack and call `__do_global_init'. */
Xasm (" andn %sp, 7, %sp");
Xasm (" sub %sp, 24, %sp");
Xasm (" call ___do_global_init");
X
X/* Move `argc', `argv', and `envp' from locals to parameters for `main'. */
Xasm (" mov %l0,%o0");
Xasm (" mov %l1,%o1");
Xasm (" call _main");
Xasm (" mov %l2,%o2");
X
X#ifndef ON_EXIT
X/* Save return value from `main', and call `__do_global_cleanup',
X if necessary. In any event, get return value from `main' into
X a safe register. */
Xasm (" call ___do_global_cleanup");
X#endif
Xasm (" mov %o0,%l0");
X
X#ifdef ON_EXIT
X/* Call `exit' or `_exit' with return value from `main'. */
Xasm (" call _exit");
X#else
Xasm (" call __exit");
X#endif
Xasm (" mov %l0,%o0");
X#endif /* sparc */
X
X#ifndef sun
X/* For C++, calls to exit(3) must perform their cleanup duties.
X This means calling destructors on all of the items which
X need to have their destructors called. After calling these
X destructors, a call is made to _exit (2), which is serious
X business. */
Xvoid
Xexit (status)
X int status;
X{
X __do_global_cleanup ();
X#if defined(hp9000s300) || defined(i386) || defined(ALTOS) || defined(vax) || defined(hp300)
X _cleanup ();
X#endif
X _exit (status);
X}
X#endif
X
X#if 0
X
Xstatic int _end_crt0 ();
X
Xvoid
X__do_global_init ()
X{
X extern void (*__CTOR_LIST__)();
X register void (**ppf)() = &__CTOR_LIST__;
X register int *pi = (int *)ppf;
X
X#ifdef VIRTUAL_FUNCTION_MASK
X if ((int)_end_crt0 < VINDEX_MAX)
X {
X printf ("virtual function index too large--encroaches text space at address 0x%x\n", _end_crt0);
X abort ();
X }
X#else
X /* @@What to do for losing segmented architectures? */
X#endif
X
X while (*pi++)
X {
X /* Losing PCC cannot handle this statement as (*ppf++)(). Pity. */
X (*ppf)();
X ppf++;
X }
X}
X
Xtypedef struct dtor_table_entry
X{
X struct dtor_table_entry *next;
X void (*fp)();
X} dtor_table_entry;
X
Xextern dtor_table_entry *__DTOR_LIST__;
X
Xvoid
X__do_global_cleanup ()
X{
X register int i;
X
X while (__DTOR_LIST__)
X {
X /* Prevent problems if a destructor should mistakenly call
X exit() by always consuming one entry per round. */
X void (*fp)() = __DTOR_LIST__->fp;
X __DTOR_LIST__ = __DTOR_LIST__->next;
X (*fp)();
X }
X}
X
Xstatic int
X_end_crt0 ()
X{
X}

X#else
Xvoid
X__do_global_init () {}
X
Xvoid
X__do_global_cleanup () {}
X#endif
!EOF
echo "Extracting input.h..."
sed 's/^X//' >input.h << '!EOF'
X/* Source file current line is coming from. */
Xextern char *input_filename;
X
X/* Top-level source file. */
Xextern char *main_input_filename;
X
X/* Line number in current source file. */
Xextern int lineno;
X
Xstruct file_stack
X {
X char *name;
X struct file_stack *next;
X int line;
X };
X
X/* Stack of currently pending input files.
X The line member is not accurate for the innermost file on the stack. */
Xextern struct file_stack *input_file_stack;
X
X/* Incremented on each change to input_file_stack. */
Xextern int input_file_stack_tick;
!EOF
echo "Extracting tree.h..."
sed 's/^X//' >tree.h << '!EOF'
X/* Front-end tree definitions for GNU compiler.
X Copyright (C) 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* codes of tree nodes */
X
X#define DEFTREECODE(SYM, STRING, TYPE, NARGS) SYM,
X
Xenum tree_code {
X#include "tree.def"
X
X LAST_AND_UNUSED_TREE_CODE /* A convienent way to get a value for
X NUM_TREE_CODE. */
X};
X
X#undef DEFTREECODE
X
X/* Number of tree codes. */
X#define NUM_TREE_CODES ((int)LAST_AND_UNUSED_TREE_CODE)
X
X/* Indexed by enum tree_code, contains a character which is
X `e' for an expression, `r' for a reference, `c' for a constant,
X `d' for a decl, `t' for a type, `s' for a statement,
X and `x' for anything else (TREE_LIST, IDENTIFIER, etc). */
X
Xextern char **tree_code_type;
X
X/* Number of argument-words in each kind of tree-node. */
X
Xextern int *tree_code_length;
X
X/* Names of tree components. */
X
Xextern char **tree_code_name;
X
X/* Get the definition of `enum machine_mode' */
X
X#ifndef HAVE_MACHINE_MODES
X#define DEF_MACHMODE(SYM, NAME, TYPE, SIZE, UNIT, WIDER) SYM,
X
Xenum machine_mode {
X#include "machmode.def"
XMAX_MACHINE_MODE };
X
X#undef DEF_MACHMODE
X
X#define HAVE_MACHINE_MODES
X
X#endif /* not HAVE_MACHINE_MODES */
X
X#ifndef NUM_MACHINE_MODES
X#define NUM_MACHINE_MODES (int) MAX_MACHINE_MODE
X#endif
X
X/* Codes that identify the various built in functions
X so that expand_call can identify them quickly. */
X
Xenum built_in_function
X{
X NOT_BUILT_IN,
X BUILT_IN_ALLOCA,
X BUILT_IN_ABS,
X BUILT_IN_FABS,
X BUILT_IN_LABS,
X BUILT_IN_FFS,
X BUILT_IN_DIV,
X BUILT_IN_LDIV,
X BUILT_IN_FFLOOR,
X BUILT_IN_FCEIL,
X BUILT_IN_FMOD,
X BUILT_IN_FREM,
X BUILT_IN_MEMCPY,
X BUILT_IN_MEMCMP,
X BUILT_IN_MEMSET,
X BUILT_IN_FSQRT,
X BUILT_IN_GETEXP,
X BUILT_IN_GETMAN,
X BUILT_IN_SAVEREGS,
X BUILT_IN_CLASSIFY_TYPE,
X
X /* C++ extensions */
X BUILT_IN_NEW,
X BUILT_IN_VEC_NEW,
X BUILT_IN_DELETE,
X BUILT_IN_VEC_DELETE,
X};
X
X/* The definition of tree nodes fills the next several pages. */
X
X/* A tree node can represent a data type, a variable, an expression
X or a statement. Each node has a TREE_CODE which says what kind of
X thing it represents. Some common codes are:
X INTEGER_TYPE -- represents a type of integers.
X ARRAY_TYPE -- represents a type of pointer.
X VAR_DECL -- represents a declared variable.
X INTEGER_CST -- represents a constant integer value.
X PLUS_EXPR -- represents a sum (an expression).
X
X As for the contents of a tree node: there are some fields
X that all nodes share. Each TREE_CODE has various special-purpose
X fields as well. The fields of a node are never accessed directly,
X always through accessor macros. */
X
X/* This type is used everywhere to refer to a tree node. */
X
Xtypedef union tree_node *tree;
X
X#define NULL_TREE (tree) NULL
X
X/* Every kind of tree node starts with this structure,
X so all nodes have these fields.
X
X See the accessor macros, defined below, for documentation of the fields. */
X
Xstruct tree_common
X{
X int uid;
X union tree_node *chain;
X union tree_node *type;
X unsigned char code : 8;
X
X unsigned external_attr : 1;
X unsigned public_attr : 1;
X unsigned static_attr : 1;
X unsigned volatile_attr : 1;
X unsigned packed_attr : 1;
X unsigned readonly_attr : 1;
X unsigned literal_attr : 1;
X unsigned nonlocal_attr : 1;
X unsigned permanent_attr : 1;
X unsigned addressable_attr : 1;
X unsigned regdecl_attr : 1;
X unsigned this_vol_attr : 1;
X unsigned unsigned_attr : 1;
X unsigned asm_written_attr: 1;
X unsigned inline_attr : 1;
X unsigned used_attr : 1;
X unsigned lang_flag_1 : 1;
X unsigned lang_flag_2 : 1;
X unsigned lang_flag_3 : 1;
X unsigned lang_flag_4 : 1;
X unsigned raises_attr : 1;
X /* There is room for three more attributes. */
X};
X
X/* Define accessors for the fields that all tree nodes have
X (though some fields are not used for all kinds of nodes). */
X
X/* The unique id of a tree node distinguishes it from all other nodes
X in the same compiler run. */
X#define TREE_UID(NODE) ((NODE)->common.uid)
X
X/* The tree-code says what kind of node it is.
X Codes are defined in tree.def. */
X#define TREE_CODE(NODE) ((enum tree_code) (NODE)->common.code)
X#define TREE_SET_CODE(NODE, VALUE) ((NODE)->common.code = (int) (VALUE))
X
X/* In all nodes that are expressions, this is the data type of the expression.
X In POINTER_TYPE nodes, this is the type that the pointer points to.

X In ARRAY_TYPE nodes, this is the type of the elements. */
X#define TREE_TYPE(NODE) ((NODE)->common.type)
X
X/* Nodes are chained together for many purposes.
X Types are chained together to record them for being output to the debugger
X (see the function `chain_type').
X Decls in the same scope are chained together to record the contents
X of the scope.
X Statement nodes for successive statements used to be chained together.
X Often lists of things are represented by TREE_LIST nodes that
X are chained together. */
X
X#define TREE_CHAIN(NODE) ((NODE)->common.chain)
X
X/* Define many boolean fields that all tree nodes have. */
X
X/* In a VAR_DECL or FUNCTION_DECL,
X nonzero means external reference:
X do not allocate storage, and refer to a definition elsewhere. */
X#define TREE_EXTERNAL(NODE) ((NODE)->common.external_attr)
X
X/* In a VAR_DECL, nonzero means allocate static storage.
X In a FUNCTION_DECL, currently nonzero if function has been defined. */
X#define TREE_STATIC(NODE) ((NODE)->common.static_attr)
X
X/* In a VAR_DECL or FUNCTION_DECL,
X nonzero means name is to be accessible from outside this module. */
X#define TREE_PUBLIC(NODE) ((NODE)->common.public_attr)
X
X/* In VAR_DECL nodes, nonzero means address of this is needed.
X So it cannot be in a register.
X In a FUNCTION_DECL, nonzero means its address is needed.
X So it must be compiled even if it is an inline function.
X In CONSTRUCTOR nodes, it means the elements are all constants suitable
X for output as assembly-language initializers.
X In LABEL_DECL nodes, it means a goto for this label has been seen
X from a place outside all binding contours that restore stack levels,
X or that an error message about jumping into such a binding contour
X has been printed for this label.
X In ..._TYPE nodes, it means that objects of this type must
X be fully addressable. This means that pieces of this
X object cannot go into register parameters, for example. */
X#define TREE_ADDRESSABLE(NODE) ((NODE)->common.addressable_attr)
X
X/* In VAR_DECL nodes, nonzero means declared `register'. */
X#define TREE_REGDECL(NODE) ((NODE)->common.regdecl_attr)
X
X/* In any expression, nonzero means it has side effects or reevaluation
X of the whole expression could produce a different value.
X This is set if any subexpression is a function call, a side effect
X or a reference to a volatile variable.
X In a ..._DECL, this is set only if the declaration said `volatile'.
X In a ..._TYPE, nonzero means the type is volatile-qualified. */
X#define TREE_VOLATILE(NODE) ((NODE)->common.volatile_attr)
X
X/* Nonzero means this expression is volatile in the C sense:
X its address should be of type `volatile WHATEVER *'.
X If this bit is set, so is `volatile_attr'. */
X#define TREE_THIS_VOLATILE(NODE) ((NODE)->common.this_vol_attr)
X
X/* In a VAR_DECL, PARM_DECL or FIELD_DECL, or any kind of ..._REF node,
X nonzero means it may not be the lhs of an assignment.
X In a ..._TYPE node, means this type is const-qualified. */
X#define TREE_READONLY(NODE) ((NODE)->common.readonly_attr)
X
X/* Nonzero in a FIELD_DECL means it is a bit-field; it may occupy
X less than a storage unit, and its address may not be taken, etc.
X This controls layout of the containing record.
X In a LABEL_DECL, nonzero means label was defined inside a binding
X contour that restored a stack level and which is now exited. */
X#define TREE_PACKED(NODE) ((NODE)->common.packed_attr)
X
X/* Value of expression is constant.
X Always appears in all ..._CST nodes.
X May also appear in an arithmetic expression, an ADDR_EXPR or a CONSTRUCTOR
X if the value is constant. */
X#define TREE_LITERAL(NODE) ((NODE)->common.literal_attr)
X
X/* Nonzero in a ..._DECL means this variable is ref'd from a nested function.
X For VAR_DECL nodes, PARM_DECL nodes, and FUNCTION_DECL nodes.
X
X For LABEL_DECL nodes, nonzero if nonlocal gotos to the label are permitted.
X
X Also set in some languages for variables, etc., outside the normal
X lexical scope, such as class instance variables. */
X#define TREE_NONLOCAL(NODE) ((NODE)->common.nonlocal_attr)
X
X/* Nonzero means permanent node;
X node will continue to exist for the entire compiler run.
X Otherwise it will be recycled at the end of the function. */
X#define TREE_PERMANENT(NODE) ((NODE)->common.permanent_attr)
X
X/* In INTEGER_TYPE or ENUMERAL_TYPE nodes, means an unsigned type.
X In FIELD_DECL nodes, means an unsigned bit field. */
X#define TREE_UNSIGNED(NODE) ((NODE)->common.unsigned_attr)
X
X/* Nonzero in a VAR_DECL means assembler code has been written.
X Nonzero in a FUNCTION_DECL means that the function has been compiled.
X This is interesting in an inline function, since it might not need
X to be compiled separately. */
X#define TREE_ASM_WRITTEN(NODE) ((NODE)->common.asm_written_attr)
X
X/* Nonzero in a FUNCTION_DECL means this function can be substituted
X where it is called.
X Nonzero in a VAR_DECL or PARM_DECL means this decl was made by inlining;
X suppress any warnings about shadowing some other variable. */
X#define TREE_INLINE(NODE) ((NODE)->common.inline_attr)
X
X/* Nonzero in a _DECL if the name is used in its scope. */
X#define TREE_USED(NODE) ((NODE)->common.used_attr)
X
X#define TREE_LANG_FLAG_1(NODE) ((NODE)->common.lang_flag_1)
X#define TREE_LANG_FLAG_2(NODE) ((NODE)->common.lang_flag_2)
X#define TREE_LANG_FLAG_3(NODE) ((NODE)->common.lang_flag_3)
X#define TREE_LANG_FLAG_4(NODE) ((NODE)->common.lang_flag_4)
X
X/* Nonzero for an expression that might raise an exception. */
X#define TREE_RAISES(NODE) ((NODE)->common.raises_attr)
X
X/* Define additional fields and accessors for nodes representing constants. */
X
X/* In an INTEGER_CST node. These two together make a 64 bit integer.
X If the data type is signed, the value is sign-extended to 64 bits
X even though not all of them may really be in use.
X In an unsigned constant shorter than 64 bits, the extra bits are 0. */
X#define TREE_INT_CST_LOW(NODE) ((NODE)->int_cst.int_cst_low)
X#define TREE_INT_CST_HIGH(NODE) ((NODE)->int_cst.int_cst_high)
X
X#define INT_CST_LT(A, B) \
X(TREE_INT_CST_HIGH (A) < TREE_INT_CST_HIGH (B) \
X || (TREE_INT_CST_HIGH (A) == TREE_INT_CST_HIGH (B) \
X && ((unsigned) TREE_INT_CST_LOW (A) < (unsigned) TREE_INT_CST_LOW (B))))
X
X#define INT_CST_LT_UNSIGNED(A, B) \
X((unsigned) TREE_INT_CST_HIGH (A) < (unsigned) TREE_INT_CST_HIGH (B) \
X || ((unsigned) TREE_INT_CST_HIGH (A) == (unsigned) TREE_INT_CST_HIGH (B) \
X && ((unsigned) TREE_INT_CST_LOW (A) < (unsigned) TREE_INT_CST_LOW (B))))
X
Xstruct tree_int_cst
X{
X char common[sizeof (struct tree_common)];
X long int_cst_low;
X long int_cst_high;
X};
X
X/* In REAL_CST, STRING_CST, COMPLEX_CST nodes, and CONSTRUCTOR nodes,
X and generally in all kinds of constants that could
X be given labels (rather than being immediate). */
X
X#define TREE_CST_RTL(NODE) ((NODE)->real_cst.rtl)
X
X/* In a REAL_CST node. */
X/* We can represent a real value as either a `double' or a string.
X Strings don't allow for any optimization, but they do allow
X for cross-compilation. */
X
X#define TREE_REAL_CST(NODE) ((NODE)->real_cst.real_cst)
X
X#include "real.h"
X
Xstruct tree_real_cst
X{
X char common[sizeof (struct tree_common)];
X struct rtx_def *rtl; /* acts as link to register transfer language
X (rtl) info */
X REAL_VALUE_TYPE real_cst;
X};
X
X/* In a STRING_CST */
X#define TREE_STRING_LENGTH(NODE) ((NODE)->string.length)
X#define TREE_STRING_POINTER(NODE) ((NODE)->string.pointer)
X
Xstruct tree_string
X{
X char common[sizeof (struct tree_common)];
X struct rtx_def *rtl; /* acts as link to register transfer language
X (rtl) info */
X int length;
X char *pointer;
X};
X
X/* In a COMPLEX_CST node. */
X#define TREE_REALPART(NODE) ((NODE)->complex.real)
X#define TREE_IMAGPART(NODE) ((NODE)->complex.imag)
X
Xstruct tree_complex
X{
X char common[sizeof (struct tree_common)];
X struct rtx_def *rtl; /* acts as link to register transfer language
X (rtl) info */
X union tree_node *real;
X union tree_node *imag;
X};
X
X/* Define fields and accessors for some special-purpose tree nodes. */
X
X#define IDENTIFIER_LENGTH(NODE) ((NODE)->identifier.length)
X#define IDENTIFIER_POINTER(NODE) ((NODE)->identifier.pointer)
X
Xstruct tree_identifier
X{
X char common[sizeof (struct tree_common)];
X int length;
X char *pointer;
X};
X
X/* In a TREE_LIST node. */
X#define TREE_PURPOSE(NODE) ((NODE)->list.purpose)
X#define TREE_VALUE(NODE) ((NODE)->list.value)
X
Xstruct tree_list
X{
X char common[sizeof (struct tree_common)];
X union tree_node *purpose;
X union tree_node *value;
X};
X
X/* In a TREE_VEC node. */
X#define TREE_VEC_LENGTH(NODE) ((NODE)->vec.length)
X#define TREE_VEC_ELT(NODE,I) ((NODE)->vec.a[I])
X#define TREE_VEC_END(NODE) (&((NODE)->vec.a[(NODE)->vec.length]))
X
Xstruct tree_vec
X{
X char common[sizeof (struct tree_common)];
X int length;
X union tree_node *a[1];
X};
X
X/* Define fields and accessors for some nodes that represent expressions. */
X
X/* In a SAVE_EXPR node. */
X#define SAVE_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[1])
X
X/* In a RTL_EXPR node. */
X#define RTL_EXPR_SEQUENCE(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[0])
X#define RTL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[1])
X
X/* In a CALL_EXPR node. */
X#define CALL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[2])
X
X/* In a CONSTRUCTOR node. */
X#define CONSTRUCTOR_ELTS(NODE) TREE_OPERAND (NODE, 1)
X
X/* In expression and reference nodes. */
X#define TREE_OPERAND(NODE, I) ((NODE)->exp.operands[I])
X#define TREE_COMPLEXITY(NODE, I) ((NODE)->exp.complexity)
X
Xstruct tree_exp
X{
X char common[sizeof (struct tree_common)];
X int complexity;
X union tree_node *operands[1];
X};
X
X/* Define fields and accessors for nodes representing data types. */
X
X/* See tree.def for documentation of the use of these fields.
X Look at the documentation of the various ..._TYPE tree codes. */
X
X#define TYPE_SIZE(NODE) ((NODE)->type.size)
X#define TYPE_SIZE_UNIT(NODE) ((NODE)->type.size_unit)
X#define TYPE_MODE(NODE) ((NODE)->type.mode)
X#define TYPE_ALIGN(NODE) ((NODE)->type.align)
X#define TYPE_VALUES(NODE) ((NODE)->type.values)
X#define TYPE_DOMAIN(NODE) ((NODE)->type.values)
X#define TYPE_FIELDS(NODE) ((NODE)->type.values)
X#define TYPE_ARG_TYPES(NODE) ((NODE)->type.values)
X#define TYPE_METHOD_BASETYPE(NODE) ((NODE)->type.max)
X#define TYPE_OFFSET_BASETYPE(NODE) ((NODE)->type.max)
X#define TYPE_SEP(NODE) ((NODE)->type.sep)
X#define TYPE_SEP_UNIT(NODE) ((NODE)->type.sep_unit)
X#define TYPE_POINTER_TO(NODE) ((NODE)->type.pointer_to)
X#define TYPE_REFERENCE_TO(NODE) ((NODE)->type.reference_to)
X#define TYPE_MIN_VALUE(NODE) ((NODE)->type.sep)
X#define TYPE_MAX_VALUE(NODE) ((NODE)->type.max)
X#define TYPE_PRECISION(NODE) ((NODE)->type.sep_unit)
X#define TYPE_SYMTAB_ADDRESS(NODE) ((NODE)->type.symtab_address)
X#define TYPE_NAME(NODE) ((NODE)->type.name)
X#define TYPE_NEXT_VARIANT(NODE) ((NODE)->type.next_variant)
X#define TYPE_MAIN_VARIANT(NODE) ((NODE)->type.main_variant)
X#define TYPE_BASETYPES(NODE) ((NODE)->type.basetypes)
X#define TYPE_NONCOPIED_PARTS(NODE) ((NODE)->type.noncopied_parts)
X#define TYPE_LANG_SPECIFIC(NODE) ((NODE)->type.lang_specific)
X
Xstruct tree_type
X{
X char common[sizeof (struct tree_common)];
X union tree_node *values;
X union tree_node *sep;
X union tree_node *size;
X
X enum machine_mode mode : 8;
X unsigned char size_unit;
X unsigned char align;
X unsigned char sep_unit;
X
X union tree_node *pointer_to;
X union tree_node *reference_to;
X int symtab_address;
X union tree_node *name;
X union tree_node *max;
X union tree_node *next_variant;
X union tree_node *main_variant;
X union tree_node *basetypes;
X union tree_node *noncopied_parts;
X /* Points to a structure whose details depend on the language in use. */
X struct lang_type *lang_specific;
X};
X
X/* Define fields and accessors for nodes representing declared names. */
X
X#define DECL_VOFFSET(NODE) ((NODE)->decl.voffset) /* In FIELD_DECLs and maybe PARM_DECLs. */
X#define DECL_RESULT_TYPE(NODE) ((NODE)->decl.voffset) /* In FUNCTION_DECLs. */
X#define DECL_VOFFSET_UNIT(NODE) ((NODE)->decl.voffset_unit)
X#define DECL_OFFSET(NODE) ((NODE)->decl.offset)
X#define DECL_FUNCTION_CODE(NODE) ((enum built_in_function) (NODE)->decl.offset)
X#define DECL_SET_FUNCTION_CODE(NODE,VAL) ((NODE)->decl.offset = (int) (VAL))
X#define DECL_NAME(NODE) ((NODE)->decl.name)
X#define DECL_CONTEXT(NODE) ((NODE)->decl.context)
X#define DECL_FIELD_CONTEXT(NODE) ((NODE)->decl.context)
X#define DECL_ARGUMENTS(NODE) ((NODE)->decl.arguments) /* In FUNCTION_DECL. */
X#define DECL_ARG_TYPE(NODE) ((NODE)->decl.initial) /* In PARM_DECL. */
X#define DECL_INITIAL(NODE) ((NODE)->decl.initial)
X#define DECL_SOURCE_FILE(NODE) ((NODE)->decl.filename)
X#define DECL_SOURCE_LINE(NODE) ((NODE)->decl.linenum)
X#define DECL_SIZE(NODE) ((NODE)->decl.size)
X#define DECL_SIZE_UNIT(NODE) ((NODE)->decl.size_unit)
X#define DECL_ALIGN(NODE) ((NODE)->decl.align)
X#define DECL_MODE(NODE) ((NODE)->decl.mode)
X#define DECL_RTL(NODE) ((NODE)->decl.rtl)
X#define DECL_ASSEMBLER_NAME(NODE) ((NODE)->decl.assembler_name)
X#define DECL_LANG_SPECIFIC(NODE) ((NODE)->decl.lang_specific)
X
Xstruct tree_decl
X{
X char common[sizeof (struct tree_common)];
X union tree_node *size;
X enum machine_mode mode : 8;
X unsigned char size_unit;
X unsigned char align;
X unsigned char voffset_unit;
X union tree_node *name;
X union tree_node *context;
X int offset;
X union tree_node *voffset;
X union tree_node *initial;
X union tree_node *arguments;
X struct rtx_def *rtl; /* acts as link to register transfer language
X (rtl) info */
X /* Points to a structure whose details depend on the language in use. */
X struct lang_decl *lang_specific;
X
X /* Unused by PARM_DECL. */
X char *filename;
X int linenum;
X char *assembler_name;
X};
X
X#define DECL_PRINT_NAME(NODE) ((NODE)->function_decl.print_name)
X#define DECL_RESULT(NODE) ((NODE)->function_decl.result)
X#define DECL_BLOCK_SYMTAB_ADDRESS(NODE) ((NODE)->function_decl.block_symtab_address)
X#define DECL_SAVED_INSNS(NODE) ((NODE)->function_decl.saved_insns)
X#define DECL_FRAME_SIZE(NODE) ((NODE)->function_decl.frame_size)
X
Xstruct tree_function_decl
X{
X struct tree_decl ignore;
X
X char *print_name;
X union tree_node *result;
X int frame_size; /* For FUNCTION_DECLs: size of stack frame */
X struct rtx_def *saved_insns; /* For FUNCTION_DECLs: points to insn that
X constitutes its definition on the
X permanent obstack. */
X int block_symtab_address;
X};
X
X/* Define fields and accessors for nodes representing statements.
X These are now obsolete for C, except for LET_STMT, which is used
X to record the structure of binding contours (and the names declared
X in each contour) for the sake of outputting debugging info.
X Perhaps they will be used once again for other languages. */
X
X/* For LABEL_STMT, GOTO_STMT, RETURN_STMT, COMPOUND_STMT, ASM_STMT. */
X#define STMT_SOURCE_LINE(NODE) ((NODE)->stmt.linenum)
X#define STMT_SOURCE_FILE(NODE) ((NODE)->stmt.filename)
X#define STMT_BODY(NODE) ((NODE)->stmt.body)
X
Xstruct tree_stmt
X{
X char common[sizeof (struct tree_common)];
X char *filename;
X int linenum;
X union tree_node *body;
X};
X
X/* For IF_STMT. */
X
X/* #define STMT_SOURCE_LINE(NODE) */
X/* #define STMT_SOURCE_FILE(NODE) */
X#define STMT_COND(NODE) ((NODE)->if_stmt.cond)
X#define STMT_THEN(NODE) ((NODE)->if_stmt.thenpart)
X#define STMT_ELSE(NODE) ((NODE)->if_stmt.elsepart)
X
Xstruct tree_if_stmt
X{
X char common[sizeof (struct tree_common)];
X char *filename;
X int linenum;
X union tree_node *cond, *thenpart, *elsepart;
X};
X
X/* For LOOP_STMT. */
X#define STMT_LOOP_VARS(NODE) ((NODE)->loop_stmt.vars)
X#define STMT_LOOP_COND(NODE) ((NODE)->loop_stmt.cond)
X#define STMT_LOOP_BODY(NODE) ((NODE)->loop_stmt.body)
X
Xstruct tree_loop_stmt
X{
X char common[sizeof (struct tree_common)];
X char *filename;
X int linenum;
X union tree_node *vars, *cond, *body;
X};
X
X/* For LET_STMT and WITH_STMT. */
X
X/* #define STMT_SOURCE_LINE(NODE) */
X/* #define STMT_SOURCE_FILE(NODE) */
X/* #define STMT_BODY(NODE) */
X#define STMT_VARS(NODE) ((NODE)->bind_stmt.vars)
X#define STMT_SUPERCONTEXT(NODE) ((NODE)->bind_stmt.supercontext)
X#define STMT_BIND_SIZE(NODE) ((NODE)->bind_stmt.bind_size)
X#define STMT_TYPE_TAGS(NODE) ((NODE)->bind_stmt.type_tags)
X#define STMT_SUBBLOCKS(NODE) ((NODE)->bind_stmt.subblocks)
X
Xstruct tree_bind_stmt
X{
X char common[sizeof (struct tree_common)];
X char *filename;
X int linenum;
X union tree_node *body, *vars, *supercontext, *bind_size, *type_tags;
X union tree_node *subblocks;
X};
X
X/* For CASE_STMT. */
X
X#define STMT_CASE_INDEX(NODE) ((NODE)->case_stmt.index)
X#define STMT_CASE_LIST(NODE) ((NODE)->case_stmt.case_list)
X
Xstruct tree_case_stmt
X{
X char common[sizeof (struct tree_common)];
X char *filename;
X int linenum;
X union tree_node *index, *case_list;
X};
X
X/* Define the overall contents of a tree node.
X It may be any of the structures declared above
X for various types of node. */
X
Xunion tree_node
X{
X struct tree_common common;
X struct tree_int_cst int_cst;
X struct tree_real_cst real_cst;
X struct tree_string string;
X struct tree_complex complex;
X struct tree_identifier identifier;
X struct tree_decl decl;
X struct tree_function_decl function_decl;
X struct tree_type type;
X struct tree_list list;
X struct tree_vec vec;
X struct tree_exp exp;
X struct tree_stmt stmt;
X struct tree_if_stmt if_stmt;
X struct tree_loop_stmt loop_stmt;
X struct tree_bind_stmt bind_stmt;
X struct tree_case_stmt case_stmt;
X};
X
Xextern char *oballoc ();
Xextern char *permalloc ();
X
X/* Lowest level primitive for allocating a node.
X The TREE_CODE is the only argument. Contents are initialized
X to zero except for a few of the common fields. */
X
Xextern tree make_node ();
X
X/* Make a copy of a node, with all the same contents except
X for TREE_UID and TREE_PERMANENT. (The copy is permanent
X iff nodes being made now are permanent.) */
X
Xextern tree copy_node ();
X
X/* Make a copy of a chain of TREE_LIST nodes. */
X
Xextern tree copy_list ();
X
X/* Make a TREE_VEC. */
X
Xextern tree make_tree_vec ();
X
X/* Return the (unique) IDENTIFIER_NODE node for a given name.
X The name is supplied as a char *. */
X
Xextern tree get_identifier ();
X
X/* Construct various types of nodes. */
X
Xextern tree build_int_2 ();
Xextern tree build_real ();
Xextern tree build_real_from_string ();
Xextern tree build_real_from_int_cst ();
Xextern tree build_complex ();
Xextern tree build_string ();
Xextern tree build (), build1 ();
Xextern tree build_nt (), build_parse_node ();
Xextern tree build_tree_list (), build_decl_list (), build_decl_list_1 ();
Xextern tree build_op_identifier ();
Xextern tree build_decl ();
Xextern tree build_if ();
Xextern tree build_loop ();
Xextern tree build_let ();
X
X/* Construct various nodes representing data types. */
X
Xextern tree make_signed_type ();
Xextern tree make_unsigned_type ();
Xextern void fixup_unsigned_type ();
Xextern tree build_pointer_type ();
Xextern tree build_reference_type ();
Xextern tree build_index_type ();
Xextern tree build_array_type ();
Xextern tree build_function_type ();
Xextern tree build_method_type ();
Xextern tree build_offset_type ();
Xextern tree array_type_nelts ();
X
X/* Construct expressions, performing type checking. */
X
Xextern tree build_binary_op ();
Xextern tree build_indirect_ref ();
Xextern tree build_unary_op ();
X
X/* Given a type node TYPE, and CONSTP and VOLATILEP, return a type
X for the same kind of data as TYPE describes.
X Variants point to the "main variant" (which has neither CONST nor VOLATILE)
X via TYPE_MAIN_VARIANT, and it points to a chain of other variants
X so that duplicate variants are never made.
X Only main variants should ever appear as types of expressions. */
X
Xextern tree build_type_variant ();
X
X/* Given a ..._TYPE node, calculate the TYPE_SIZE, TYPE_SIZE_UNIT,
X TYPE_ALIGN and TYPE_MODE fields.
X If called more than once on one node, does nothing except
X for the first time. */
X
Xextern void layout_type ();
X
X/* Given a hashcode and a ..._TYPE node (for which the hashcode was made),
X return a canonicalized ..._TYPE node, so that duplicates are not made.
X How the hash code is computed is up to the caller, as long as any two
X callers that could hash identical-looking type nodes agree. */
X
Xextern tree type_hash_canon ();
X
X/* Given a VAR_DECL, PARM_DECL, RESULT_DECL or FIELD_DECL node,
X calculates the DECL_SIZE, DECL_SIZE_UNIT, DECL_ALIGN and DECL_MODE
X fields. Call this only once for any given decl node.
X
X Second argument is the boundary that this field can be assumed to
X be starting at (in bits). Zero means it can be assumed aligned
X on any boundary that may be needed. */
X
Xextern void layout_decl ();
X
X/* Fold constants as much as possible in an expression.
X Returns the simplified expression.
X Acts only on the top level of the expression;
X if the argument itself cannot be simplified, its
X subexpressions are not changed. */
X
Xextern tree fold ();
X
X/* combine (tree_code, exp1, exp2) where EXP1 and EXP2 are constants
X returns a constant expression for the result of performing
X the operation specified by TREE_CODE on EXP1 and EXP2. */
X
Xextern tree combine ();
X
Xextern tree convert ();
Xextern tree convert_units ();
Xextern tree size_in_bytes ();
Xextern tree genop ();
Xextern tree build_int ();
Xextern tree get_pending_sizes ();
X
X/* Type for sizes of data-type. */
X
Xextern tree sizetype;
X
X/* Concatenate two lists (chains of TREE_LIST nodes) X and Y
X by making the last node in X point to Y.
X Returns X, except if X is 0 returns Y. */
X
Xextern tree chainon ();
X
X/* Make a new TREE_LIST node from specified PURPOSE, VALUE and CHAIN. */
X
Xextern tree tree_cons (), perm_tree_cons (), temp_tree_cons ();
Xextern tree saveable_tree_cons (), decl_tree_cons ();
X
X/* Return the last tree node in a chain. */
X
Xextern tree tree_last ();
X
X/* Reverse the order of elements in a chain, and return the new head. */
X
Xextern tree nreverse ();
X
X/* Returns the length of a chain of nodes
X (number of chain pointers to follow before reaching a null pointer). */
X
Xextern int list_length ();
X
X/* integer_zerop (tree x) is nonzero if X is an integer constant of value 0 */
X
Xextern int integer_zerop ();
X
X/* integer_onep (tree x) is nonzero if X is an integer constant of value 1 */
X
Xextern int integer_onep ();
X
X/* integer_all_onesp (tree x) is nonzero if X is an integer constant
X all of whose significant bits are 1. */
X
Xextern int integer_all_onesp ();
X
X/* type_unsigned_p (tree x) is nonzero if the type X is an unsigned type
X (all of its possible values are >= 0).
X If X is a pointer type, the value is 1.
X If X is a real type, the value is 0. */
X
Xextern int type_unsigned_p ();
X
X/* staticp (tree x) is nonzero if X is a reference to data allocated
X at a fixed address in memory. */
X
Xextern int staticp ();
X
X/* Gets an error if argument X is not an lvalue.
X Also returns 1 if X is an lvalue, 0 if not. */
X
Xextern int lvalue_or_else ();
X
X/* save_expr (EXP) returns an expression equivalent to EXP
X but it can be used multiple times within context CTX
X and only evaluate EXP once. */
X
Xextern tree save_expr ();
X
X/* stabilize_reference (EXP) returns an reference equivalent to EXP
X but it can be used multiple times
X and only evaluate the subexpressions once. */
X
Xextern tree stabilize_reference ();
X
X/* Return EXP, stripped of any conversions to wider types
X in such a way that the result of converting to type FOR_TYPE
X is the same as if EXP were converted to FOR_TYPE.
X If FOR_TYPE is 0, it signifies EXP's type. */
X
Xextern tree get_unwidened ();
X
X/* Return OP or a simpler expression for a narrower value
X which can be sign-extended or zero-extended to give back OP.
X Store in *UNSIGNEDP_PTR either 1 if the value should be zero-extended
X or 0 if the value should be sign-extended. */
X
Xextern tree get_narrower ();
X
X/* Given PRECISION and UNSIGNEDP, return a suitable type-tree
X for an integer type with at least that precision.
X The definition of this resides in language-specific code
X as the repertoire of available types may vary. */
X
Xextern tree type_for_size ();
X
X/* Given an integer type T, return a type like T but unsigned.
X If T is unsigned, the value is T.
X The definition of this resides in language-specific code
X as the repertoire of available types may vary. */
X
Xextern tree unsigned_type ();
X
X/* Given an integer type T, return a type like T but signed.
X If T is signed, the value is T.
X The definition of this resides in language-specific code
X as the repertoire of available types may vary. */
X
Xextern tree signed_type ();
X
X/* Return the floating type node for a given floating machine mode. */
X
Xextern tree get_floating_type ();
X
X/* Given the FUNCTION_DECL for the current function,
X return zero if it is ok for this function to be inline.
X Otherwise return a warning message with a single %s
X for the function's name. */
X
Xextern char *function_cannot_inline_p ();
X
X/* Declare commonly used variables for tree structure. */
X
X/* An integer constant with value 0 */
Xextern tree integer_zero_node;
X
X/* An integer constant with value 1 */
Xextern tree integer_one_node;
X
X/* An integer constant with value 0 whose type is sizetype. */
Xextern tree size_zero_node;
X
X/* An integer constant with value 1 whose type is sizetype. */
Xextern tree size_one_node;
X
X/* A constant of type pointer-to-int and value 0 */
Xextern tree null_pointer_node;
X
X/* A node of type ERROR_MARK. */
Xextern tree error_mark_node;
X
X/* The type node for the void type. */
Xextern tree void_type_node;
X
X/* The type node for the ordinary (signed) integer type. */
Xextern tree integer_type_node;
X
X/* The type node for the unsigned integer type. */
Xextern tree unsigned_type_node;
X
X/* The type node for the ordinary character type. */
Xextern tree char_type_node;
X
X/* Points to the name of the input file from which the current input
X being parsed originally came (before it went into cpp). */
Xextern char *input_filename;
X
X/* Nonzero for -pedantic switch: warn about anything
X that standard C forbids. */
Xextern int pedantic;
X
X/* Nonzero means can safely call expand_expr now;
X otherwise layout_type puts variable sizes onto `pending_sizes' instead. */
X
Xextern int immediate_size_expand;
X
X/* Points to the FUNCTION_DECL of the function whose body we are reading. */
X
Xextern tree current_function_decl;
X
X/* Nonzero if function being compiled can call setjmp. */
X
Xextern int current_function_calls_setjmp;
X
X/* Nonzero means all ..._TYPE nodes should be allocated permanently. */
X
Xextern int all_types_permanent;
X
X/* Pointer to function to compute the name to use to print a declaration. */
X
Xextern char *(*decl_printable_name) ();
X
X/* In stmt.c */
X
Xextern tree expand_start_stmt_expr ();
Xextern tree expand_end_stmt_expr ();
Xextern void expand_expr_stmt (), clear_last_expr ();
Xextern void expand_label (), expand_goto (), expand_asm ();
Xextern void expand_start_cond (), expand_end_cond ();
Xextern void expand_start_else (), expand_end_else ();
Xextern void expand_start_loop (), expand_start_loop_continue_elsewhere ();
Xextern void expand_loop_continue_here ();
Xextern void expand_end_loop ();
Xextern int expand_continue_loop ();
Xextern int expand_exit_loop (), expand_exit_loop_if_false ();
Xextern int expand_exit_something ();
X
Xextern void expand_start_delayed_expr ();
Xextern tree expand_end_delayed_expr ();
Xextern void expand_emit_delayed_expr ();
X
Xextern void expand_null_return (), expand_return ();
Xextern void expand_start_bindings (), expand_end_bindings ();
Xextern void expand_start_case (), expand_end_case ();
Xextern int pushcase (), pushcase_range ();
Xextern void expand_start_function (), expand_end_function ();
!EOF
echo "Extracting cplus-decl.h..."
sed 's/^X//' >cplus-decl.h << '!EOF'
X/* In grokdeclarator, distinguish syntactic contexts of declarators. */
Xenum decl_context
X{ NORMAL, /* Ordinary declaration */
X FUNCDEF, /* Function definition */
X PARM, /* Declaration of parm before function body */
X FIELD, /* Declaration inside struct or union */
X TYPENAME, /* Typename (inside cast or sizeof) */
X MEMFUNCDEF, /* Member function definition */
X};
X
X/* C++: Keep these around to reduce calls to `get_identifier'.
X Identifiers for `this' in member functions and the auto-delete
X parameter for destructors. */
Xextern tree this_identifier, in_charge_identifier;
X
X/* Parsing a function declarator leaves a list of parameter names
X or a chain or parameter decls here. */
Xextern tree last_function_parms;
X
X/* A list of static class variables. This is needed, because a
X static class variable can be declared inside the class without
X an initializer, and then initialized, staticly, outside the class. */
Xextern tree pending_statics;
X
X/* A list of objects which have constructors or destructors
X which reside in the global scope. The decl is stored in
X the TREE_VALUE slot and the initializer is stored
X in the TREE_PURPOSE slot. */
Xextern tree static_aggregates;
X
X/* A list of functions which were declared inline, but later had their
X address taken. Used only for non-virtual member functions, since we can
X find other functions easily enough. */
Xextern tree pending_addressable_inlines;
X
X#ifdef SOS
X/* SOS extensions. */
Xextern tree zlink_type, zret_type;
Xextern tree zlink, zret;
X#endif
!EOF
echo "Extracting crt1.c..."
sed 's/^X//' >crt1.c << '!EOF'
X/* This file provides the startup routine for files loaded with
X the -A option of GNU ld. When a program makes a call to the
X initial address of code incrementally loaded, it expects that the
X first function laid out in the object files loaded will
X be the function called. In GNU C++, we slip crt1.o in front of
X the object files the user specifies, so that we can call any needed
X global constructors, and set up calls for global destructors.
X Control is then passed to the first routine that the user specified. */
X
Xtypedef struct set_vector {
X unsigned int length;
X unsigned int vector[1];
X} set_vector;
X
X/* ******** WARNING ********
X Note that the address of _incstart() should be the start
X of text space.
X
X Michael Tiemann, Stanford University. */
X
Xextern set_vector __CTOR_LIST__;
Xextern set_vector __DTOR_LIST__;
Xextern set_vector *__dlp;
Xextern int __dli;
Xvoid (*_initfn)() = 0;
X
Xstatic void
X_incstart ()
X{
X register void (**ppf)() = (void (**)())__CTOR_LIST__.vector;
X int i, len = __CTOR_LIST__.length;
X
X __dli = __DTOR_LIST__.length;
X __DTOR_LIST__.vector[__dli] = (int)__dlp;
X __dlp = &__DTOR_LIST__;
X
X for (i = 0; i < len; i++)
X (*ppf[i]) ();
X (*_initfn)();
X}
X
Xstatic int
X_end_crt1 ()
X{
X}
!EOF
echo "Extracting integrate.c..."
sed 's/^X//' >integrate.c << '!EOF'
X/* Procedure integration for GNU CC.
X Copyright (C) 1988 Free Software Foundation, Inc.
X Contributed by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X#include
X
X#include "config.h"
X#include "rtl.h"
X#include "tree.h"
X#include "flags.h"
X#include "insn-flags.h"
X#include "expr.h"
X
X#include "obstack.h"
X#define obstack_chunk_alloc xmalloc
X#define obstack_chunk_free free
Xextern int xmalloc ();
Xextern void free ();
X
Xextern struct obstack permanent_obstack, maybepermanent_obstack;
Xextern struct obstack *rtl_obstack, *saveable_obstack, *current_obstack;
X
Xextern rtx stack_slot_list;
X
X#define MIN(x,y) ((x < y) ? x : y)
X
Xextern tree pushdecl ();
Xextern tree poplevel ();
X
X/* Default max number of insns a function can have and still be inline.
X This is overridden on RISC machines. */
X#ifndef INTEGRATE_THRESHOLD
X#define INTEGRATE_THRESHOLD(DECL) \
X (8 * (8 + list_length (DECL_ARGUMENTS (DECL)) + 16*TREE_INLINE (DECL)))
X#endif
X
X/* This is the target of the inline function being expanded,
X or NULL if there is none. */
Xstatic rtx inline_target;
X
X/* We must take special care not to disrupt life too severely
X when performing procedure integration. One thing that that
X involves is not creating illegitimate address which reload
X cannot fix. Since we don't know what the frame pointer is
X not capable of (in a machine independent way), we create
X a pseudo-frame pointer which will have to do for now. */
Xstatic rtx before_inline_fp_rtx, inline_fp_rtx;
X
X/* Convert old frame-pointer offsets to new. Parameters which only
X produce values (no addresses, and are never assigned), map directly
X to the pseudo-reg of the incoming value. Parameters that are
X assigned to but do not have their address taken are given a fresh
X pseudo-register. Parameters that have their address take are
X given a fresh stack-slot. */
Xstatic rtx *parm_map;
X
X/* ?? Should this be done here?? It is not right now.
X Keep track of whether a given pseudo-register is the sum
X of the frame pointer and a const_int (or zero). */
Xstatic char *fp_addr_p;
X
X/* For the local variables of the procdure being integrated that live
X on the frame, FRAME_POINTER_DELTA says how much to change their
X offsets by, so that they now live in the correct place on the
X frame of the function being compiled. */
Xstatic int fp_delta;
X
X/* When an insn is being copied by copy_rtx_and_substitute,
X this is nonzero if we have copied an ASM_OPERANDS.
X In that case, it is the original input-operand vector.
X Likewise in copy_for_inline. */
Xstatic rtvec orig_asm_operands_vector;
X
X/* When an insn is being copied by copy_rtx_and_substitute,
X this is nonzero if we have copied an ASM_OPERANDS.
X In that case, it is the copied input-operand vector.
X Likewise in copy_for_inline. */
Xstatic rtvec copy_asm_operands_vector;
X
X/* Likewise, this is the copied constraints vector. */
Xstatic rtvec copy_asm_constraints_vector;
X
X/* In save_for_inline, nonzero if past the parm-initialization insns. */
Xstatic int in_nonparm_insns;
X
X/* Return a copy of an rtx (as needed), substituting pseudo-register,
X labels, and frame-pointer offsets as necessary. */
Xstatic rtx copy_rtx_and_substitute ();
X/* Variant, used for memory addresses that are not memory_address_p. */
Xstatic rtx copy_address ();
X
X/* Return the rtx corresponding to a given index in the stack arguments. */
Xstatic rtx access_parm_map ();
X
Xstatic void copy_parm_decls ();
Xstatic void copy_decl_tree ();
X
Xstatic rtx try_fold_cc0 ();
X
X/* We do some simple constant folding optimization. This optimization
X really exists primarily to save time inlining a function. It
X also helps users who ask for inline functions without -O. */
Xstatic rtx fold_out_const_cc0 ();
X
X/* Zero if the current function (whose FUNCTION_DECL is FNDECL)
X is safe and reasonable to integrate into other functions.
X Nonzero means value is a warning message with a single %s
X for the function's name. */
X
Xchar *
Xfunction_cannot_inline_p (fndecl)
X register tree fndecl;
X{
X register rtx insn;
X tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
X int max_insns = INTEGRATE_THRESHOLD (fndecl);
X register int ninsns = 0;
X register tree parms;
X
X /* No inlines with varargs. `grokdeclarator' gives a warning
X message about that if `inline' is specified. This code
X it put in to catch the volunteers. */
X if (last && TREE_VALUE (last) != void_type_node)
X return "varargs function cannot be inline";
X
X /* If its not even close, don't even look. */
X if (get_max_uid () > 4 * max_insns)
X return "function too large to be inline";
X
X /* Don't inline functions with large stack usage,
X since they can make other recursive functions burn up stack. */
X if (!TREE_INLINE (fndecl) && get_frame_size () > 100)
X return "function stack frame too large for inlining";
X
X /* We can't inline functions that return structures
X the old-fashioned PCC way, copying into a static block. */
X#ifdef PCC_STATIC_STRUCT_RETURN
X if (flag_pcc_struct_return
X && (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode
X || RETURN_IN_MEMORY (TREE_TYPE (TREE_TYPE (fndecl)))))
X return "inline functions not supported for this return value type";
X#endif
X
X /* Don't inline functions which have BLKmode arguments.
X Don't inline functions that take the address of
X a parameter and do not specify a function prototype. */
X for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
X {
X if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode)
X {
X#if 0
X return "function with large aggregate parameter cannot be inline";
X#else
X TREE_ADDRESSABLE (parms) = 1;
X#endif
X }
X if (last == NULL_TREE && TREE_ADDRESSABLE (parms))
X return "no prototype, and parameter address used; cannot be inline";
X#if 0
X /* If an aggregate is thought of as "in memory"
X then its components are referred to by narrower memory refs.
X If the actual parameter is a reg, these refs can't be translated,
X esp. since copy_rtx_and_substitute doesn't know whether it is
X reading or writing. */
X if ((TREE_CODE (TREE_TYPE (parms)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE)
X && GET_CODE (DECL_RTL (parms)) == MEM)
X return "address of an aggregate parameter is used; cannot be inline";
X#endif
X }
X
X if (get_max_uid () > max_insns)
X {
X for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns;
X insn = NEXT_INSN (insn))
X {
X if (GET_CODE (insn) == INSN
X || GET_CODE (insn) == JUMP_INSN
X || GET_CODE (insn) == CALL_INSN)
X ninsns++;
X }
X
X if (ninsns >= max_insns)
X return "function too large to be inline";
X }
X
X return 0;
X}
X
X/* Variables used within save_for_inline. */
X
X/* Mapping from old pesudo-register to new pseudo-registers.
X The first element of this map is reg_map[FIRST_PSEUDO_REGISTER].
X It is allocated in `save_for_inline' and `expand_inline_function',
X and deallocated on exit from each of those routines. */
Xstatic rtx *reg_map;
X
X/* Mapping from old code-labels to new code-labels.
X The first element of this map is label_map[min_labelno].
X It is allocated in `save_for_inline' and `expand_inline_function',
X and deallocated on exit from each of those routines. */
Xstatic rtx *label_map;
X
X/* Mapping from old insn uid's to copied insns.
X It is allocated in `save_for_inline' and `expand_inline_function',
X and deallocated on exit from each of those routines. */
Xstatic rtx *insn_map;
X
X/* Map pseudo reg number into the PARM_DECL for the parm living in the reg.
X Zero for a reg that isn't a parm's home.
X Only reg numbers less than max_parm_reg are mapped here. */
Xstatic tree *parmdecl_map;
X
X/* Map pseudo reg number to equivalent constant. We cannot in general
X substitute constants into parameter pseudo registers, since a
X machine descriptions (the Sparc md, maybe others) won't always handle
X the resulting insns. So if an incoming parameter has a constant
X equivalent, we record it here, and if the resulting insn is
X recognizable, we go with it. */
Xstatic rtx *const_equiv_map;
X
X/* Nonzero if we should try using a constant equivalent.
X Set to zero if constant equivalent resulted in insn which could
X not be recognized. */
Xstatic int try_use_const;
X
X/* Use "iteration numbering" to speedily pull constant values
X from registers when testing conditionals. */
Xstatic unsigned int *const_age_map, const_age;
X
X/* Cleared before attempting to inline any functions.
X Set when const equiv is used. Used to test whether insn
X is safe for md or not. */
Xstatic int used_const_equiv;
X
X/* Keep track of first pseudo-register beyond those that are parms. */
Xstatic int max_parm_reg;
X
X/* Offset from arg ptr to the first parm of this inline function. */
Xstatic int first_parm_offset;
X
X/* On machines that perform a function return with a single
X instruction, such as the VAX, these return insns must be
X mapped into branch statements. */
Xextern rtx return_label;
X
X/* Save any constant pool constants in an insn. */
Xstatic void save_constants ();
X
X/* Note when parameter registers are the destination of a SET. */
Xstatic void note_modified_parmregs ();
X
X/* Copy an rtx for save_for_inline. */
Xstatic rtx copy_for_inline ();
X
X/* Make the insns and PARM_DECLs of the current function permanent
X and record other information in DECL_SAVED_INSNS to allow inlining
X of this function in subsequent calls. */
X
Xvoid
Xsave_for_inline (fndecl)
X tree fndecl;
X{
X extern rtx *regno_reg_rtx; /* in emit-rtl.c. */
X extern current_function_args_size;
X
X rtx first_insn, last_insn, insn;
X rtx head, copy;
X tree parms;
X int max_labelno, min_labelno, i, len;
X int max_reg;
X int max_uid;
X rtx first_nonparm_insn;
X
X /* Make and emit a return-label if we have not already done so. */
X
X if (return_label == 0)
X {
X return_label = gen_label_rtx ();
X emit_label (return_label);
X }
X
X /* Get some bounds on the labels and registers used. */
X
X max_labelno = max_label_num ();
X min_labelno = get_first_label_num ();
X max_parm_reg = max_parm_reg_num ();
X max_reg = max_reg_num ();
X
X /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
X
X Set TREE_VOLATILE to 0 if the parm is in a register, otherwise 1.
X Later we set TREE_READONLY to 0 if the parm is modified inside the fn. */
X
X parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));
X bzero (parmdecl_map, max_parm_reg * sizeof (tree));
X
X for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
X {
X rtx p = DECL_RTL (parms);
X
X if (GET_CODE (p) == REG)
X {
X parmdecl_map[REGNO (p)] = parms;
X TREE_VOLATILE (parms) = 0;
X }
X else
X TREE_VOLATILE (parms) = 1;
X TREE_READONLY (parms) = 1;
X }
X
X /* Replace any constant pool references with the actual constant. We will
X put the constant back in the copy made below. */
X for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
X if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
X || GET_CODE (insn) == CALL_INSN)
X save_constants (PATTERN (insn));
X
X /* The list of DECL_SAVED_INSNS, starts off with a header which
X contains the following information:
X
X the first insn of the function (not including the insns that copy
X parameters into registers).
X the first parameter insn of the function,
X the first label used by that function,
X the last label used by that function,
X the highest register number used for parameters,
X the total number of registers used,
X the stack slot list,
X @@ not yet: and some flags that are used to restore compiler globals. */
X
X head = gen_inline_header_rtx (NULL, NULL, min_labelno, max_labelno,
X max_parm_reg, max_reg,
X current_function_args_size, stack_slot_list);
X max_uid = INSN_UID (head);
X
X /* We have now allocated all that needs to be allocated permanently
X on the rtx obstack. Set our high-water mark, so that we
X can free the rest of this when the time comes. */
X
X preserve_data ();
X
X /* Copy the chain insns of this function.
X Install the copied chain as the insns of this function,
X for continued compilation;
X the original chain is recorded as the DECL_SAVED_INSNS
X for inlining future calls. */
X
X /* If there are insns that copy parms from the stack into pseudo registers,
X those insns are not copied. `expand_inline_function' must
X emit the correct code to handle such things. */
X
X insn = get_insns ();
X if (GET_CODE (insn) != NOTE)
X abort ();
X first_insn = rtx_alloc (NOTE);
X NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn);
X NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn);
X INSN_UID (first_insn) = INSN_UID (insn);
X PREV_INSN (first_insn) = NULL;
X NEXT_INSN (first_insn) = NULL;
X last_insn = first_insn;
X
X /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy.
X Make these new rtx's now, and install them in regno_reg_rtx, so they
X will be the official pseudo-reg rtx's for the rest of compilation. */
X
X reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx));
X
X len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion);
X for (i = max_reg - 1; i >= FIRST_PSEUDO_REGISTER; i--)
X reg_map[i] = (rtx)obstack_copy (&maybepermanent_obstack, regno_reg_rtx[i], len);
X bcopy (reg_map + FIRST_PSEUDO_REGISTER,
X regno_reg_rtx + FIRST_PSEUDO_REGISTER,
X (max_reg - FIRST_PSEUDO_REGISTER) * sizeof (rtx));
X
X /* Likewise each label rtx must have a unique rtx as its copy. */
X
X label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
X label_map -= min_labelno;
X
X for (i = min_labelno; i < max_labelno; i++)
X label_map[i] = gen_label_rtx ();
X
X /* Record the mapping of old insns to copied insns. */
X
X insn_map = (rtx *) alloca (max_uid * sizeof (rtx));
X bzero (insn_map, max_uid * sizeof (rtx));
X
X in_nonparm_insns = 0;
X first_nonparm_insn = get_first_nonparm_insn ();
X
X /* Now copy the chain of insns. */
X
X for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
X {
X orig_asm_operands_vector = 0;
X copy_asm_operands_vector = 0;
X
X if (insn == first_nonparm_insn)
X in_nonparm_insns = 1;
X
X switch (GET_CODE (insn))
X {
X case NOTE:
X /* No need to keep these. */
X if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
X continue;
X
X copy = rtx_alloc (NOTE);
X NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
X NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn);
X break;
X
X case INSN:
X case CALL_INSN:
X case JUMP_INSN:
X copy = rtx_alloc (GET_CODE (insn));
X PATTERN (copy) = copy_for_inline (PATTERN (insn));
X INSN_CODE (copy) = -1;
X LOG_LINKS (copy) = NULL;
X REG_NOTES (copy) = copy_for_inline (REG_NOTES (insn));
X RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn);
X break;
X
X case CODE_LABEL:
X copy = label_map[CODE_LABEL_NUMBER (insn)];
X break;
X
X case BARRIER:
X copy = rtx_alloc (BARRIER);
X break;
X
X default:
X abort ();
X }
X INSN_UID (copy) = INSN_UID (insn);
X insn_map[INSN_UID (insn)] = copy;
X NEXT_INSN (last_insn) = copy;
X PREV_INSN (copy) = last_insn;
X last_insn = copy;
X }
X
X NEXT_INSN (last_insn) = NULL;
X
X NEXT_INSN (head) = get_first_nonparm_insn ();
X FIRST_PARM_INSN (head) = get_insns ();
X DECL_SAVED_INSNS (fndecl) = head;
X DECL_FRAME_SIZE (fndecl) = get_frame_size ();
X TREE_INLINE (fndecl) = 1;
X
X parmdecl_map = 0;
X label_map = 0;
X reg_map = 0;
X return_label = 0;
X
X set_new_first_and_last_insn (first_insn, last_insn);
X
X /* The following code does not need preprocessing in the assembler. */
X
X app_disable ();
X
X output_constant_pool (XSTR (XEXP (DECL_RTL (fndecl), 0), 0), fndecl);
X}
X
X/* Make the insns and PARM_DECLs of the current function permanent
X and record other information in DECL_SAVED_INSNS to allow inlining
X of this function in subsequent calls.
X
X Other version. */
X
Xvoid
Xsave_for_outline (fndecl)
X tree fndecl;
X{
X extern rtx *regno_reg_rtx; /* in emit-rtl.c. */
X extern current_function_args_size;
X
X rtx first_insn, last_insn, insn;
X rtx head, copy;
X tree parms;
X int max_labelno, min_labelno, i, len;
X int max_reg;
X int max_uid;
X rtx first_nonparm_insn;
X
X /* Make and emit a return-label if we have not already done so. */
X
X if (return_label == 0)
X {
X return_label = gen_label_rtx ();
X emit_label (return_label);
X }
X
X /* Get some bounds on the labels and registers used. */
X
X max_labelno = max_label_num ();
X min_labelno = get_first_label_num ();
X max_parm_reg = max_parm_reg_num ();
X max_reg = max_reg_num ();
X
X /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
X
X Set TREE_VOLATILE to 0 if the parm is in a register, otherwise 1.
X Later we set TREE_READONLY to 0 if the parm is modified inside the fn. */
X
X parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));
X bzero (parmdecl_map, max_parm_reg * sizeof (tree));
X
X for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
X {
X rtx p = DECL_RTL (parms);
X
X if (GET_CODE (p) == REG)
X {
X parmdecl_map[REGNO (p)] = parms;
X TREE_VOLATILE (parms) = 0;
X }
X else
X TREE_VOLATILE (parms) = 1;
X TREE_READONLY (parms) = 1;
X }
X
X /* The list of DECL_SAVED_INSNS, starts off with a header which
X contains the following information:
X
X the first insn of the function (not including the insns that copy
X parameters into registers).
X the first parameter insn of the function,
X the first label used by that function,
X the last label used by that function,
X the highest register number used for parameters,
X the total number of registers used,
X the stack slot list,
X @@ not yet: and some flags that are used to restore compiler globals. */
X
X head = gen_inline_header_rtx (NULL, NULL, min_labelno, max_labelno,
X max_parm_reg, max_reg,
X current_function_args_size, stack_slot_list);
X /* We have now allocated all that needs to be allocated permanently
X on the rtx obstack. Set our high-water mark, so that we
X can free the rest of this when the time comes. */
X
X preserve_data ();
X
X /* Copy the chain insns of this function.
X Install the copied chain as the insns of this function,
X for continued compilation;
X the original chain is recorded as the DECL_SAVED_INSNS
X for inlining future calls. */
X
X /* If there are insns that copy parms from the stack into pseudo registers,
X those insns are not copied. `expand_inline_function' must
X emit the correct code to handle such things. */
X
X insn = get_insns ();
X if (GET_CODE (insn) != NOTE)
X abort ();
X first_insn = rtx_alloc (NOTE);
X NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn);
X NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn);
X INSN_UID (first_insn) = INSN_UID (insn);
X PREV_INSN (first_insn) = NULL;
X NEXT_INSN (first_insn) = NULL;
X last_insn = first_insn;
X
X in_nonparm_insns = 0;
X first_nonparm_insn = get_first_nonparm_insn ();
X
X /* Now copy the chain of insns. */
X
X for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
X {
X orig_asm_operands_vector = 0;
X copy_asm_operands_vector = 0;
X
X if (insn == first_nonparm_insn)
X in_nonparm_insns = 1;
X
X switch (GET_CODE (insn))
X {
X case INSN:
X case CALL_INSN:
X case JUMP_INSN:
X note_modified_parmregs (PATTERN (insn));
X break;
X
X case NOTE:
X case CODE_LABEL:
X case BARRIER:
X break;
X
X default:
X abort ();
X }
X last_insn = insn;
X }
X
X NEXT_INSN (head) = get_first_nonparm_insn ();
X FIRST_PARM_INSN (head) = get_insns ();
X DECL_SAVED_INSNS (fndecl) = head;
X DECL_FRAME_SIZE (fndecl) = get_frame_size ();
X TREE_INLINE (fndecl) = 1;
X
X /* Have to output these, since other functions may refer to them. */
X
X /* The following code does not need preprocessing in the assembler. */
X
X app_disable ();
X
X output_constant_pool (XSTR (XEXP (DECL_RTL (fndecl), 0), 0), fndecl);
X}
X
X/* References to the constant pool are replaced by the actual constant
X encapsulated with a CONST giving the mode and with RTX_INTEGRATED_P set.
X
X *** Note that the above only works if the address was not manipulated.
X If the address was not valid and had to be loaded into a register,
X we lose track of the fact that it was in the constant pool, which will
X result in either an abort or generating a reference to an undefined
X label in the assembler code. No current machine will run into this, but
X this should probably be fixed someday. */
X
Xstatic void
Xsave_constants (x)
X rtx x;
X{
X int i, j;
X char *fmt = GET_RTX_FORMAT (GET_CODE (x));
X
X for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
X {
X switch (*fmt++)
X {
X case 'E':
X for (j = 0; j < XVECLEN (x, i); j++)
X if (GET_CODE (XVECEXP (x, i, j)) == MEM
X && GET_CODE (XEXP (XVECEXP (x, i, j), 0)) == SYMBOL_REF
X && CONSTANT_POOL_ADDRESS_P (XEXP (XVECEXP (x, i, j), 0)))
X {
X XVECEXP (x, i, j) =
X gen_rtx (CONST, get_pool_mode (XEXP (XVECEXP (x, i, j), 0)),
X get_pool_constant (XEXP (XVECEXP (x, i, j), 0)));
X RTX_INTEGRATED_P (XVECEXP (x, i, j)) = 1;
X }
X else
X save_constants (XVECEXP (x, i, j));
X break;
X
X case 'e':
X if (GET_CODE (XEXP (x, i)) == MEM
X && GET_CODE (XEXP (XEXP (x, i), 0)) == SYMBOL_REF
X && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (x, i), 0)))
X {
X XEXP (x, i) = gen_rtx (CONST,
X get_pool_mode (XEXP (XEXP (x, i), 0)),
X get_pool_constant (XEXP (XEXP (x, i), 0)));
X RTX_INTEGRATED_P (XEXP (x, i)) = 1;
X }
X else
X save_constants (XEXP (x, i));
X break;
X }
X }
X}
X
X/* Note (recursively) whether a parameter is modified or not. */
X
Xstatic void
Xnote_modified_parmregs (orig)
X rtx orig;
X{
X register rtx x = orig;
X register int i, len;
X register enum rtx_code code;
X register char *format_ptr;
X
X again:
X
X if (x == 0)
X return;
X
X code = GET_CODE (x);
X
X /* These types may be freely shared. */
X
X if (code == SET && in_nonparm_insns)
X {
X rtx dest = SET_DEST (x);
X
X if (GET_CODE (dest) == REG
X && REGNO (dest) < max_parm_reg
X && REGNO (dest) >= FIRST_PSEUDO_REGISTER
X && parmdecl_map[REGNO (dest)] != 0)
X TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0;
X return;
X }
X
X /* Now scan the subexpressions recursively. */
X
X format_ptr = GET_RTX_FORMAT (code);
X len = GET_RTX_LENGTH (code);
X
X if (len > 2)
X for (i = len-1; i > 1; i--)
X if (format_ptr[i] == 'e')
X note_modified_parmregs (XEXP (x, 2));
X else if (format_ptr[i] == 'E')
X goto hard;
X
X if (len > 1)
X if (format_ptr[1] == 'e')
X note_modified_parmregs (XEXP (x, 1));
X else if (format_ptr[i=1] == 'E')
X goto hard;
X
X if (len > 0)
X if (format_ptr[0] == 'e')
X {
X x = XEXP (x, 0);
X goto again;
X }
X else if (format_ptr[i=0] == 'E')
X goto hard;
X
X return;
X
X i = len-1;
X
X hard:
X
X while (i >= 0)
X {
X switch (format_ptr[i])
X {
X case 'e':
X if (i == 0)
X {
X x = XEXP (x, 0);
X goto again;
X }
X note_modified_parmregs (XEXP (x, i));
X break;
X
X case 'E':
X if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
X {
X register int j;
X
X for (j = 0; j < XVECLEN (x, i); j++)
X note_modified_parmregs (XVECEXP (x, i, j));
X }
X break;
X }
X i -= 1;
X }
X}
X
X/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels
X according to `reg_map' and `label_map'.
X
X If we find a saved constant pool entry, replace it with the constant.
X Since the pool wasn't touched, this should simply restore the old
X address.
X
X All other kinds of rtx are copied except those that can never be
X changed during compilation. */
X
Xstatic rtx
Xcopy_for_inline (orig)
X rtx orig;
X{
X register rtx x = orig;
X register int i;
X register enum rtx_code code;
X register char *format_ptr;
X
X if (x == 0)
X return x;
X
X code = GET_CODE (x);
X
X /* These types may be freely shared. */
X
X switch (code)
X {
X case QUEUED:
X case CONST_INT:
X case CONST_DOUBLE:
X case SYMBOL_REF:
X case PC:
X case CC0:
X return x;
X
X case CONST:
X /* Get constant pool entry for constant in the pool. */
X if (RTX_INTEGRATED_P (x))
X return force_const_mem (GET_MODE (x), XEXP (x, 0));
X break;
X
X case ASM_OPERANDS:
X /* If a single asm insn contains multiple output operands
X then it contains multiple ASM_OPERANDS rtx's that share operand 3.
X We must make sure that the copied insn continues to share it. */
X if (orig_asm_operands_vector == XVEC (orig, 3))
X {
X x = rtx_alloc (ASM_OPERANDS);
X XSTR (x, 0) = XSTR (orig, 0);
X XSTR (x, 1) = XSTR (orig, 1);
X XINT (x, 2) = XINT (orig, 2);
X XVEC (x, 3) = copy_asm_operands_vector;
X XVEC (x, 4) = copy_asm_constraints_vector;
X XSTR (x, 5) = XSTR (orig, 5);
X XINT (x, 6) = XINT (orig, 6);
X return x;
X }
X break;
X
X case MEM:
X /* A MEM is allowed to be shared if its address is constant
X or is a constant plus one of the special registers. */
X if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
X return x;
X#if 0 /* This is turned off because it is possible for
X unshare_all_rtl to copy the address, into memory that won't be saved.
X Although the MEM can safely be shared, and won't be copied there,
X the address itself cannot be shared, and may need to be copied. */
X if (GET_CODE (XEXP (x, 0)) == PLUS
X && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
X && (REGNO (XEXP (XEXP (x, 0), 0)) == FRAME_POINTER_REGNUM
X || REGNO (XEXP (XEXP (x, 0), 0)) == ARG_POINTER_REGNUM)
X && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
X#if 0
X /* This statement was accidentally deleted in the remote past.
X Reinsert it for 1.37. Don't take the risk now. */
X return x;
X#endif
X if (GET_CODE (XEXP (x, 0)) == REG
X && (REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM
X || REGNO (XEXP (x, 0)) == ARG_POINTER_REGNUM)
X && CONSTANT_ADDRESS_P (XEXP (x, 1)))
X return x;
X#endif /* 0 */
X break;
X
X case LABEL_REF:
X {
X /* Must point to the new insn. */
X return gen_rtx (LABEL_REF, GET_MODE (orig),
X label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]);
X }
X
X case REG:
X if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
X return reg_map [REGNO (x)];
X else
X return x;
X
X /* If a parm that gets modified lives in a pseudo-reg,
X set its TREE_VOLATILE to prevent certain optimizations. */
X case SET:
X if (in_nonparm_insns)
X {
X rtx dest = SET_DEST (x);
X
X if (GET_CODE (dest) == REG
X && REGNO (dest) < max_parm_reg
X && REGNO (dest) >= FIRST_PSEUDO_REGISTER
X && parmdecl_map[REGNO (dest)] != 0)
X TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0;
X }
X /* The insn to load an arg pseudo from a stack slot
X does not count as modifying it. */
X break;
X
X /* Arrange that CONST_INTs always appear as the second operand
X if they appear, and that `frame_pointer_rtx' or `arg_pointer_rtx'
X always appear as the first. */
X case PLUS:
X if (GET_CODE (XEXP (x, 0)) == CONST_INT
X || (XEXP (x, 1) == frame_pointer_rtx
X || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
X && XEXP (x, 1) == arg_pointer_rtx)))
X {
X rtx t = XEXP (x, 0);
X XEXP (x, 0) = XEXP (x, 1);
X XEXP (x, 1) = t;
X }
X break;
X }
X
X /* Replace this rtx with a copy of itself. */
X
X x = rtx_alloc (code);
X bcopy (orig, x, (sizeof (*x) - sizeof (x->fld)
X + sizeof (x->fld[0]) * GET_RTX_LENGTH (code)));
X
X /* Now scan the subexpressions recursively.
X We can store any replaced subexpressions directly into X
X since we know X is not shared! Any vectors in X
X must be copied if X was copied. */
X
X format_ptr = GET_RTX_FORMAT (code);
X
X for (i = 0; i < GET_RTX_LENGTH (code); i++)
X {
X switch (*format_ptr++)
X {
X case 'e':
X XEXP (x, i) = copy_for_inline (XEXP (x, i));
X break;
X
X case 'u':
X /* Change any references to old-insns to point to the
X corresponding copied insns. */
X return insn_map[INSN_UID (XEXP (x, i))];
X
X case 'E':
X if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
X {
X register int j;
X
X XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0));
X for (j = 0; j < XVECLEN (x, i); j++)
X XVECEXP (x, i, j)
X = copy_for_inline (XVECEXP (x, i, j));
X }
X break;
X }
X }
X
X if (code == ASM_OPERANDS && orig_asm_operands_vector == 0)
X {
X orig_asm_operands_vector = XVEC (orig, 3);
X copy_asm_operands_vector = XVEC (x, 3);
X copy_asm_constraints_vector = XVEC (x, 4);
X }
X
X return x;
X}
X
X/* Helper function to deal with using constants for kinds of INSNs.
X Return zero if trouble arose by using constants.
X Return one if not. Caller must know what to do in either case. */
Xstatic int
Xnote_integrated (copy)
X rtx copy;
X{
X if (used_const_equiv)
X {
X used_const_equiv = 0;
X if (recog (PATTERN (copy), copy) < 0)
X {
X int old_optimize = optimize;
X optimize = 1;
X delete_insn (copy);
X optimize = old_optimize;
X try_use_const = 0;
X return 0;
X }
X }
X try_use_const = 1;
X RTX_INTEGRATED_P (copy) = 1;
X return 1;
X}
X
X
X/* Non-zero if we are trying to reduce the amount of debug information output */
Xextern int flag_inline_debug;
X
X/* Integrate the procedure defined by FNDECL. Note that this function
X may wind up calling itself. Since the static variables are not
X reentrant, we do not assign them until after the possibility
X or recursion is eliminated.
X
X If IGNORE is nonzero, do not produce a value.
X Otherwise store the value in TARGET if it is nonzero and that is convenient.
X
X Value is:
X (rtx)-1 if we could not substitute the function
X 0 if we substituted it and it does not produce a value
X else an rtx for where the value is stored. */
X
Xrtx
Xexpand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr)
X tree fndecl, parms;
X rtx target;
X int ignore;
X tree type;
X rtx structure_value_addr;
X{
X extern int lineno;
X tree formal, actual;
X rtx header = DECL_SAVED_INSNS (fndecl);
X rtx insns = FIRST_FUNCTION_INSN (header);
X rtx insn;
X int max_regno = MAX_REGNUM (header) + 1;
X register int i;
X int min_labelno = FIRST_LABELNO (header);
X int max_labelno = LAST_LABELNO (header);
X int nargs;
X rtx *arg_vec;
X rtx local_return_label = 0;
X rtx follows_call = 0;
X rtx this_struct_value_rtx = 0;
X
X /* Hack around non-reentrancy of static variables. */
X rtx *old_const_equiv_map = const_equiv_map;
X unsigned *old_const_age_map = const_age_map;
X unsigned old_const_age = const_age;
X
X /* If we need INLINE_FP_RTX, set it up immediately
X following this insn. */
X
X if (max_regno < FIRST_PSEUDO_REGISTER)
X abort ();
X
X nargs = list_length (DECL_ARGUMENTS (fndecl));
X
X /* We expect PARMS to have the right length; don't crash if not. */
X if (list_length (parms) != nargs)
X return (rtx)-1;
X
X /* Also check that the parms type match. Since the appropriate
X conversions or default promotions have already been applied,
X the machine modes should match exactly. */
X for (formal = DECL_ARGUMENTS (fndecl),
X actual = parms;
X formal;
X formal = TREE_CHAIN (formal),
X actual = TREE_CHAIN (actual))
X {
X tree arg = TREE_VALUE (actual);
X enum machine_mode mode = TYPE_MODE (DECL_ARG_TYPE (formal));
X if (mode != TYPE_MODE (TREE_TYPE (arg)))
X return (rtx)-1;
X /* If they are block mode, the types should match exactly.
X They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE,
X which could happen if the parameter has incomplete type. */
X if (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal))
X return (rtx)-1;
X }
X
X const_equiv_map = (rtx *)alloca (max_regno * sizeof (rtx));
X bzero (const_equiv_map, max_regno * sizeof (rtx));
X const_age_map = (unsigned *)alloca (max_regno * sizeof (unsigned));
X bzero (const_age_map, max_regno * sizeof (unsigned));
X try_use_const = 1;
X /* Trick: set to large number so that variables set in first
X basic block keep their values. After first label is seen,
X we wrap. */
X const_age = (unsigned)-1;
X
X /* Make a binding contour to keep inline cleanups called at
X outer function-scope level from looking like they are shadowing
X parameter declarations. */
X pushlevel (0);
X
X /* Make a fresh binding contour that we can easily remove. */
X pushlevel (0);
X expand_start_bindings (0);
X
X /* Get all the actual args as RTL, and store them in ARG_VEC. */
X
X arg_vec = (rtx *)alloca (nargs * sizeof (rtx));
X
X if (flag_inline_debug)
X /* Say where this function starts. */
X emit_note (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl));
X
X for (formal = DECL_ARGUMENTS (fndecl),
X actual = parms,
X i = 0;
X formal;
X formal = TREE_CHAIN (formal),
X actual = TREE_CHAIN (actual),
X i++)
X {
X /* Actual parameter, already converted to DECL_ARG_TYPE (formal). */
X tree arg = TREE_VALUE (actual);
X /* Mode of the value supplied. */
X enum machine_mode tmode = TYPE_MODE (DECL_ARG_TYPE (formal));
X /* Mode of the variable used within the function. */
X enum machine_mode imode = TYPE_MODE (TREE_TYPE (formal));
X rtx copy;
X
X#if 0
X /* PARM_DECL nodes no longer have this. */
X emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal));
X#endif
X
X /* Make a place to hold the argument value, still in mode TMODE,
X and put it in COPY. */
X if (TREE_ADDRESSABLE (formal))
X {
X int size = int_size_in_bytes (DECL_ARG_TYPE (formal));
X copy = assign_stack_local (tmode, size);
X if (!memory_address_p (DECL_MODE (formal), XEXP (copy, 0)))
X copy = change_address (copy, VOIDmode, copy_rtx (XEXP (copy, 0)));
X store_expr (arg, copy, 0);
X }
X else if (! TREE_READONLY (formal)
X || TREE_VOLATILE (formal))
X {
X /* If parm is modified or if it hasn't a pseudo reg,
X we may not simply substitute the actual value;
X copy it through a register. */
X copy = gen_reg_rtx (tmode);
X store_expr (arg, copy, 0);
X }
X else
X {
X copy = expand_expr (arg, 0, tmode, 0);
X
X /* We do not use CONSTANT_ADDRESS_P here because
X the set of cases where that might make a difference
X are a subset of the cases that arise even when
X it is a CONSTANT_ADDRESS_P (i.e., fp_delta
X gets into the act. */
X if (GET_CODE (copy) != REG)
X {
X#if 0
X if (! CONSTANT_P (copy))
X copy = copy_to_reg (copy);
X else if (! optimize)
X copy = copy_to_mode_reg (imode, copy);
X#else
X /* Sigh. */
X if (! CONSTANT_P (copy))
X copy = copy_to_reg (copy);
X else
X {
X if (GET_CODE (DECL_RTL (formal)) == REG)
X {
X int regno = REGNO (DECL_RTL (formal));
X const_equiv_map[regno] = copy;
X const_age_map[regno] = (unsigned)-2;
X }
X copy = copy_to_mode_reg (imode, copy);
X }
X#endif
X }
X }
X /* If passed mode != nominal mode, COPY is now the passed mode.
X Convert it to the nominal mode (i.e. truncate it). */
X if (tmode != imode)
X copy = convert_to_mode (imode, copy, 0);
X arg_vec[i] = copy;
X }
X
X copy_parm_decls (DECL_ARGUMENTS (fndecl), arg_vec);
X
X /* Perform postincrements before actually calling the function. */
X emit_queue ();
X
X /* clean up stack so that variables might have smaller offsets. */
X do_pending_stack_adjust ();

X
X /* Pass the function the address in which to return a structure value.
X Note that a constructor can cause someone to call us with
X STRUCTURE_VALUE_ADDR, but the initialization takes place
X via the first parameter, rather than the struct return address. */
X if (structure_value_addr && aggregate_value_p (DECL_RESULT (fndecl)))
X {
X if (GET_CODE (structure_value_addr) == REG
X && (struct_value_rtx == 0 || GET_CODE (struct_value_rtx) == MEM))
X this_struct_value_rtx = structure_value_addr;
X else
X this_struct_value_rtx = copy_to_mode_reg (Pmode, structure_value_addr);
X }
X
X /* Now prepare for copying the insns.
X Set up reg_map, parm_map and label_map saying how to translate
X the pseudo-registers, stack-parm references and labels when copying. */
X
X reg_map = (rtx *) alloca (max_regno * sizeof (rtx));
X bzero (reg_map, max_regno * sizeof (rtx));
X
X parm_map = (rtx *)alloca ((FUNCTION_ARGS_SIZE (header) + UNITS_PER_WORD - 1)
X / UNITS_PER_WORD * sizeof (rtx));
X bzero (parm_map, ((FUNCTION_ARGS_SIZE (header) + UNITS_PER_WORD - 1)
X / UNITS_PER_WORD * sizeof (rtx)));
X
X /* Note that expand_expr (called above) can clobber first_parm_offset. */
X first_parm_offset = FIRST_PARM_OFFSET (fndecl);
X parm_map -= first_parm_offset / UNITS_PER_WORD;
X
X if (DECL_ARGUMENTS (fndecl))
X {
X tree decl = DECL_ARGUMENTS (fndecl);
X
X for (formal = decl, i = 0; formal; formal = TREE_CHAIN (formal), i++)
X {
X /* Create an entry in PARM_MAP that says what pseudo register
X is associated with an address we might compute. */
X if (DECL_OFFSET (formal) >= 0)
X {
X /* This parameter has a home in the stack. */
X parm_map[DECL_OFFSET (formal) / BITS_PER_WORD] = arg_vec[i];
X }
X else
X {
X /* Parameter that was passed in a register;
X does it have a home on the stack (as a local)? */
X rtx frtx = DECL_RTL (formal);
X rtx offset = 0;
X if (GET_CODE (frtx) == MEM)
X {
X frtx = XEXP (frtx, 0);
X if (GET_CODE (frtx) == PLUS)
X {
X if (XEXP (frtx, 0) == frame_pointer_rtx
X && GET_CODE (XEXP (frtx, 1)) == CONST_INT)
X offset = XEXP (frtx, 1);
X else if (XEXP (frtx, 1) == frame_pointer_rtx
X && GET_CODE (XEXP (frtx, 0)) == CONST_INT)
X offset = XEXP (frtx, 0);
X#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
X /* If there is a separate arg pointer
X and REG_PARM_STACK_SPACE is defined,
X parms passed in regs can be copied
X to slots reached via the arg pointer. */
X if (XEXP (frtx, 0) == arg_pointer_rtx
X && GET_CODE (XEXP (frtx, 1)) == CONST_INT)
X offset = XEXP (frtx, 1);
X else if (XEXP (frtx, 1) == arg_pointer_rtx
X && GET_CODE (XEXP (frtx, 0)) == CONST_INT)
X offset = XEXP (frtx, 0);
X#endif
X }
X if (offset)
X parm_map[INTVAL (offset) / UNITS_PER_WORD] = arg_vec[i];
X else if (TREE_TYPE (formal) != error_mark_node)
X abort ();
X }
X else if (GET_CODE (frtx) != REG)
X abort ();
X }
X /* Create an entry in REG_MAP that says what rtx is associated
X with a pseudo register from the function being inlined. */
X if (GET_CODE (DECL_RTL (formal)) == REG)
X reg_map[REGNO (DECL_RTL (formal))] = arg_vec[i];
X }
X }
X
X /* Make certain that we can accept struct_value_{incoming_rtx,rtx},
X and map it. */
X if (this_struct_value_rtx == 0)
X ;
X else if (GET_CODE (struct_value_incoming_rtx) == REG)
X reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))]
X = this_struct_value_rtx;
X else if (GET_CODE (struct_value_incoming_rtx) == MEM
X && XEXP (XEXP (struct_value_incoming_rtx, 0), 0) == frame_pointer_rtx
X && GET_CODE (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) == CONST_INT)
X#if 1
X reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))]
X = this_struct_value_rtx;
X#else
X parm_map[INTVAL (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) / UNITS_PER_WORD]
X = this_struct_value_rtx;
X#endif
X else
X abort ();
X
X label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
X label_map -= min_labelno;
X
X for (i = min_labelno; i < max_labelno; i++)
X label_map[i] = gen_label_rtx ();
X
X /* As we copy insns, record the correspondence, so that inter-insn
X references can be copied into isomorphic structure. */
X
X insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx));
X bzero (insn_map, INSN_UID (header) * sizeof (rtx));
X
X /* Set up a target to translate the inline function's value-register. */
X
X if (structure_value_addr != 0 || TYPE_MODE (type) == VOIDmode)
X inline_target = 0;
X else
X {
X /* Machine mode function was declared to return. */
X enum machine_mode departing_mode = TYPE_MODE (type);
X /* (Possibly wider) machine mode it actually computes
X (for the sake of callers that fail to declare it right). */
X enum machine_mode arriving_mode
X = TYPE_MODE (TREE_TYPE (DECL_RESULT (fndecl)));
X
X /* Don't use MEMs as direct targets because on some machines
X substituting a MEM for a REG makes invalid insns.
X Let the combiner substitute the MEM if that is valid. */
X if (target && GET_CODE (target) == REG
X && GET_MODE (target) == departing_mode)
X inline_target = target;
X else
X {
X inline_target = gen_reg_rtx (departing_mode);
X if (target == 0)
X target = inline_target;
X }
X
X /* If function's value was promoted before return,
X avoid machine mode mismatch when we substitute INLINE_TARGET.
X But TARGET is what we will return to the caller. */
X if (arriving_mode != departing_mode)
X inline_target = gen_rtx (SUBREG, arriving_mode, inline_target, 0);
X }
X
X /* Make space in current function's stack frame
X for the stack frame of the inline function.
X Adjust all frame-pointer references by the difference
X between the offset to this space
X and the offset to the equivalent space in the inline
X function's frame.
X This difference equals the size of preexisting locals. */
X
X fp_delta = get_frame_size ();
X#ifdef FRAME_GROWS_DOWNWARD
X fp_delta = - fp_delta;
X#endif
X
X before_inline_fp_rtx = get_last_insn ();
X inline_fp_rtx = 0;
X
X /* Now allocate the space for that to point at. */
X
X assign_stack_local (VOIDmode, DECL_FRAME_SIZE (fndecl));
X
X /* Now copy the insns one by one. */
X
X for (insn = insns; insn; insn = NEXT_INSN (insn))
X {
X rtx copy, pattern, next = 0;
X
X retry:
X orig_asm_operands_vector = 0;
X copy_asm_operands_vector = 0;
X
X switch (GET_CODE (insn))
X {
X case INSN:
X pattern = PATTERN (insn);
X
X /* Special handling for the insn immediately after a CALL_INSN
X that returned a value:
X If it does copy the value, we must avoid the usual translation
X of the return-register into INLINE_TARGET.
X If it just USEs the value, the inline function expects it to
X stay in the return-register and be returned,
X so copy it into INLINE_TARGET. */
X
X if (follows_call
X /* Allow a stack-adjust, handled normally, to come in between
X the call and the value-copying insn. */
X && ! (GET_CODE (pattern) == SET
X && SET_DEST (pattern) == stack_pointer_rtx))
X {
X if (GET_CODE (pattern) == SET
X && rtx_equal_p (SET_SRC (pattern), follows_call))
X /* This insn copies the value: take special care to copy
X that value to this insn's destination. */
X {
X copy = emit_insn (gen_rtx (SET, VOIDmode,
X copy_rtx_and_substitute (SET_DEST (pattern)),
X follows_call));
X if (! note_integrated (copy))
X {
X next = 0;
X goto retry;
X }
X follows_call = 0;
X break;
X }
X else if (GET_CODE (pattern) == USE
X && rtx_equal_p (XEXP (pattern, 0), follows_call))
X /* This insn does nothing but says the value is expected
X to flow through to the inline function's return-value.
X Make that happen, then ignore this insn. */
X {
X copy = emit_insn (gen_rtx (SET, VOIDmode, inline_target,
X follows_call));
X if (! note_integrated (copy))
X {
X next = 0;
X goto retry;
X }
X follows_call = 0;
X break;
X }
X /* If it does neither, this value must be ignored. */
X follows_call = 0;
X }
X
X copy = 0;
X if (GET_CODE (pattern) == USE
X && GET_CODE (XEXP (pattern, 0)) == REG)
X {
X /* The (USE (REG n)) at return from the function should
X be ignored since we are changing (REG n) into
X inline_target. */
X if (! ignore && REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
X break;
X /* Don't emit a (USE (REG n)) of something which
X is now constant. */
X if (REGNO (XEXP (pattern, 0)) >= FIRST_PSEUDO_REGISTER
X && (const_age == (unsigned)-1
X || const_age_map[REGNO (XEXP (pattern, 0))] >= const_age))
X break;
X }
X
X /* Ignore setting a function value that we don't want to use. */
X if (inline_target == 0
X && GET_CODE (pattern) == SET
X && GET_CODE (SET_DEST (pattern)) == REG
X && REG_FUNCTION_VALUE_P (SET_DEST (pattern)))
X break;
X
X /* Try to do some quick constant folding here.
X This will save save execution time of the compiler,
X as well time and space of the program if done here. */
X if (GET_CODE (pattern) == SET
X && SET_DEST (pattern) == cc0_rtx)
X next = try_fold_cc0 (insn);
X
X if (next != 0)
X {
X used_const_equiv = 0;
X insn = next;
X }
X else
X {
X rtx note = find_reg_note (insn, REG_EQUIV, 0);
X copy = emit_insn (copy_rtx_and_substitute (pattern));
X if (! note_integrated (copy))
X {
X next = 0;
X goto retry;
X }
X
X /* If we are copying an insn that loads a constant,
X record the constantness. */
X if (note)
X REG_NOTES (copy)
X = gen_rtx (EXPR_LIST, REG_EQUIV, XEXP (note, 0),
X REG_NOTES (copy));
X
X if (GET_CODE (pattern) == SET)
X {
X rtx dest = SET_DEST (pattern);
X if (GET_CODE (dest) == REG)
X {
X int regno = REGNO (dest);
X
X if (regno >= FIRST_PSEUDO_REGISTER
X && CONSTANT_P (SET_SRC (pattern))
X && (const_equiv_map[regno] == 0
X /* Following clause is a hack to make
X case work where GNU C++ reassigns
X a variable to make cse work right. */
X || ! rtx_equal_p (const_equiv_map[regno],
X SET_SRC (pattern))))
X {
X const_equiv_map[regno] = SET_SRC (pattern);
X const_age_map[regno] = const_age;
X }
X }
X else
X {
X while (GET_CODE (dest) == SUBREG
X || GET_CODE (dest) == STRICT_LOW_PART
X || GET_CODE (dest) == SIGN_EXTRACT
X || GET_CODE (dest) == ZERO_EXTRACT)
X dest = SUBREG_REG (dest);
X
X /* Forget everything we thought we knew. */
X if (GET_CODE (dest) == REG
X && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
X const_equiv_map[REGNO (dest)] = 0;
X }
X }
X }
X break;
X
X case JUMP_INSN:
X follows_call = 0;
X if (GET_CODE (PATTERN (insn)) == RETURN)
X {
X if (local_return_label == 0)
X local_return_label = gen_label_rtx ();
X emit_jump (local_return_label);
X break;
X }
X copy = emit_jump_insn (copy_rtx_and_substitute (PATTERN (insn)));
X if (! note_integrated (copy))
X {
X next = 0;
X goto retry;
X }
X break;
X
X case CALL_INSN:
X copy = emit_call_insn (copy_rtx_and_substitute (PATTERN (insn)));
X if (! note_integrated (copy))
X {
X next = 0;
X goto retry;
X }
X /* Special handling needed for the following INSN depending on
X whether it copies the value from the fcn return reg. */
X if (GET_CODE (PATTERN (insn)) == SET)
X follows_call = SET_DEST (PATTERN (insn));
X else if (GET_CODE (PATTERN (insn)) == PARALLEL
X && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
X follows_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
X break;
X
X case CODE_LABEL:
X const_age += 2;
X copy = emit_label (label_map[CODE_LABEL_NUMBER (insn)]);
X follows_call = 0;
X if (const_age & 1)
X {
X int i;
X
X const_age += 1;
X for (i = max_regno; i >= 0; i--)
X if (const_age_map[i] == (unsigned)-1)
X const_age_map[i] = 0;
X }
X break;
X
X case BARRIER:
X const_age += 2;
X copy = emit_barrier ();
X break;
X
X case NOTE:
X /* It is important to discard function-end and function-beg notes,
X so we have only one of each in the current function. */
X if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
X && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
X && (flag_inline_debug || NOTE_LINE_NUMBER (insn) < 0))
X copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
X else
X copy = 0;
X break;
X
X default:
X abort ();
X break;
X }
X if (! (used_const_equiv == 0))
X abort ();
X insn_map[INSN_UID (insn)] = copy;
X }
X
X if (local_return_label)
X emit_label (local_return_label);
X
X /* Don't try substituting constants when making up a DECLs rtl.
X That would probably only confuse the debugger, but I don't
X know for sure. */
X try_use_const = 0;
X /* Make copies of the decls of the symbols in the inline function, so that
X the copies of the variables get declared in the current function. */
X copy_decl_tree (DECL_INITIAL (fndecl), 0);
X /* For safety. */
X if (try_use_const)
X used_const_equiv = 0;
X
X /* End the scope containing the copied formal parameter variables. */
X
X expand_end_bindings (getdecls (), 1, 1);
X poplevel (1, 1, 0);
X poplevel (0, 0, 0);
X emit_line_note (input_filename, lineno);
X
X reg_map = NULL;
X label_map = NULL;
X
X const_equiv_map = old_const_equiv_map;
X const_age_map = old_const_age_map;
X const_age = old_const_age;
X
X if (ignore || TYPE_MODE (type) == VOIDmode)
X return 0;
X
X if (structure_value_addr)
X {
X if (target)
X return target;
X return gen_rtx (MEM, TYPE_MODE (type),
X memory_address (BLKmode, structure_value_addr));
X }
X else if (target && target != inline_target
X && (GET_CODE (inline_target) != SUBREG
X || SUBREG_REG (inline_target) != target))
X {
X /* Copy result back to TARGET if TARGET is not INLINE_TARGET.
X In general, these should always wind up being the same mode,
X after SUBREGs, if any, are stripped. */
X convert_move (target, inline_target, 0);
X }
X
X return target;
X}
X
X/* Given a chain of PARM_DECLs, ARGS, and a vector of RTL homes VEC,
X copy each decl into a VAR_DECL, push all of those decls
X and give each one the corresponding home. */
X
Xstatic void
Xcopy_parm_decls (args, vec)
X tree args;
X rtx *vec;
X{
X register tree tail;
X register int i;
X
X for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
X {
X register tree decl = build_decl (VAR_DECL, DECL_NAME (tail),
X TREE_TYPE (tail));
X /* These args would always appear unused, if not for this. */
X TREE_USED (decl) = 1;
X /* Prevent warning for shadowing with these. */
X TREE_INLINE (decl) = 1;
X pushdecl (decl);
X DECL_RTL (decl) = vec[i];
X }
X}
X
X/* Given a LET_STMT node, push decls and levels
X so as to construct in the current function a tree of contexts
X isomorphic to the one that is given. */
X
Xstatic void
Xcopy_decl_tree (let, level)
X tree let;
X int level;
X{
X tree t, node;
X
X pushlevel (0);
X
X for (t = STMT_VARS (let); t; t = TREE_CHAIN (t))
X {
X tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t));
X DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t);
X DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t);
X if (DECL_RTL (t) != 0)
X {
X if (GET_CODE (DECL_RTL (t)) == MEM
X && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (t), 0)))
X /* copy_rtx_and_substitute would call memory_address
X which would copy the address into a register.
X Then debugging-output wouldn't know how to handle it. */
X DECL_RTL (d) = DECL_RTL (t);
X else
X DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t));
X }
X TREE_EXTERNAL (d) = TREE_EXTERNAL (t);
X TREE_STATIC (d) = TREE_STATIC (t);
X TREE_PUBLIC (d) = TREE_PUBLIC (t);
X TREE_LITERAL (d) = TREE_LITERAL (t);
X TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t);
X TREE_READONLY (d) = TREE_READONLY (t);
X TREE_VOLATILE (d) = TREE_VOLATILE (t);
X /* These args would always appear unused, if not for this. */
X TREE_USED (d) = 1;
X /* Prevent warning for shadowing with these. */
X TREE_INLINE (d) = 1;
X pushdecl (d);
X }
X
X for (t = STMT_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
X copy_decl_tree (t, level + 1);
X
X node = poplevel (level > 0, 0, 0);
X if (node)
X TREE_USED (node) = TREE_USED (let);
X}
X
X/* Create a new copy of an rtx.
X Recursively copies the operands of the rtx,
X except for those few rtx codes that are sharable.
X
X Handle constants that need to be placed in the constant pool by
X calling `force_const_mem'. */
X
Xstatic rtx
Xcopy_rtx_and_substitute (orig)
X register rtx orig;
X{
X register rtx copy, temp;
X register int i, j;
X register RTX_CODE code;
X register enum machine_mode mode;
X register char *format_ptr;
X int regno;
X
X if (orig == 0)
X return 0;
X
X code = GET_CODE (orig);
X mode = GET_MODE (orig);
X
X switch (code)
X {
X case REG:
X /* If a frame-pointer register shows up, then we
X must `fix' the reference. If the stack pointer
X register shows up, it must be part of stack-adjustments
X (*not* because we eliminated the frame pointer!).
X Small hard registers are returned as-is. Pseudo-registers
X go through their `reg_map'. */
X regno = REGNO (orig);
X if (regno < FIRST_PSEUDO_REGISTER)
X {
X /* Some hard registers are also mapped,
X but others are not translated. */
X if (reg_map[regno] != 0)
X return reg_map[regno];
X if (REG_FUNCTION_VALUE_P (orig))
X {
X /* This is a reference to the function return value. If
X the function doesn't have a return value, error.
X If it does, it may not be the same mode as `inline_target'
X because SUBREG is not required for hard regs.
X If not, adjust mode of inline_target to fit the context. */
X if (inline_target == 0)
X {
X /* If there isn't an inline target, so be it.
X Just fake up a reg--it won't get used
X for anything important anyway. */
X inline_target = gen_reg_rtx (mode);
X return inline_target;
X }
X if (mode == GET_MODE (inline_target))
X return inline_target;
X return gen_rtx (SUBREG, mode, inline_target, 0);
X }
X if (regno == FRAME_POINTER_REGNUM)
X return plus_constant (orig, fp_delta);
X return orig;
X }
X if (try_use_const
X && const_equiv_map[regno] != 0
X && const_age_map[regno] == (unsigned)-2)
X {
X used_const_equiv = 1;
X return const_equiv_map[regno];
X }
X if (reg_map[regno] == NULL)
X reg_map[regno] = gen_reg_rtx (mode);
X return reg_map[regno];
X
X case SUBREG:
X copy = copy_rtx_and_substitute (SUBREG_REG (orig));
X /* SUBREG is ordinary, but don't make nested SUBREGs. */
X if (GET_CODE (copy) == SUBREG)
X return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy),
X SUBREG_WORD (orig) + SUBREG_WORD (copy));
X /* Don't build a SUBREG of a CONST_INT. */
X if (GET_CODE (copy) == CONST_INT)
X return copy;
X return gen_rtx (SUBREG, GET_MODE (orig), copy,
X SUBREG_WORD (orig));
X
X case CODE_LABEL:
X return label_map[CODE_LABEL_NUMBER (orig)];
X
X case LABEL_REF:
X copy = rtx_alloc (LABEL_REF);
X PUT_MODE (copy, mode);
X XEXP (copy, 0) = label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))];
X return copy;
X
X case PC:
X case CC0:
X case CONST_INT:
X case CONST_DOUBLE:
X case SYMBOL_REF:
X return orig;
X
X case CONST:
X /* Make new constant pool entry for a constant
X that was in the pool of the inline function. */
X if (RTX_INTEGRATED_P (orig))
X return force_const_mem (GET_MODE (orig), XEXP (orig, 0));
X break;
X
X case ASM_OPERANDS:
X /* If a single asm insn contains multiple output operands
X then it contains multiple ASM_OPERANDS rtx's that share operand 3.
X We must make sure that the copied insn continues to share it. */
X if (orig_asm_operands_vector == XVEC (orig, 3))
X {
X copy = rtx_alloc (ASM_OPERANDS);
X XSTR (copy, 0) = XSTR (orig, 0);
X XSTR (copy, 1) = XSTR (orig, 1);
X XINT (copy, 2) = XINT (orig, 2);
X XVEC (copy, 3) = copy_asm_operands_vector;
X XVEC (copy, 4) = copy_asm_constraints_vector;
X XSTR (copy, 5) = XSTR (orig, 5);
X XINT (copy, 6) = XINT (orig, 6);
X return copy;
X }
X break;
X
X case CALL:
X /* This is given special treatment because the first
X operand of a CALL is a (MEM ...) which may get
X forced into a register for cse. This is undesirable
X if function-address cse isn't wanted or if we won't do cse. */
X#ifndef NO_FUNCTION_CSE
X if (! (optimize && ! flag_no_function_cse))
X#endif
X return gen_rtx (CALL, GET_MODE (orig),
X gen_rtx (MEM, GET_MODE (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0))),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X break;
X
X case PLUS:
X /* Note: treat the PLUS case as though it might be needed
X to be part of an address. If it turns out that the machine's
X PLUS insns can handle something more exciting than a ``load
X effective address'', the optimizer will discover this fact. */
X /* Take care of the easy case quickly. */
X if (XEXP (orig, 0) == frame_pointer_rtx
X || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
X && XEXP (orig, 0) == arg_pointer_rtx))
X {
X rtx reg = XEXP (orig, 0), copy = XEXP (orig, 1);
X
X if (GET_CODE (copy) == CONST_INT)
X {
X int c = INTVAL (copy);
X
X if (reg == arg_pointer_rtx && c >= first_parm_offset)
X {
X copy = access_parm_map (c, VOIDmode);
X if (GET_CODE (copy) != MEM)
X /* Should not happen, because a parm we need to address
X should not be living in a register.
X (expand_inline_function copied it to a stack slot.) */
X abort ();
X return XEXP (copy, 0);
X }
X return gen_rtx (PLUS, mode,
X frame_pointer_rtx,
X gen_rtx (CONST_INT, SImode,
X c + fp_delta));
X }
X copy = copy_rtx_and_substitute (copy);
X temp = force_reg (mode, gen_rtx (PLUS, mode, frame_pointer_rtx, copy));
X return plus_constant (temp, fp_delta);
X }
X else if (reg_mentioned_p (frame_pointer_rtx, orig)
X || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
X && reg_mentioned_p (arg_pointer_rtx, orig)))
X {
X if (GET_CODE (XEXP (orig, 1)) == CONST_INT)
X {
X copy = copy_rtx_and_substitute (XEXP (orig, 0));
X temp = plus_constant (copy, INTVAL (XEXP (orig, 1)));
X }
X else
X {
X temp = gen_rtx (PLUS, GET_MODE (orig),
X copy_rtx_and_substitute (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X }
X if (memory_address_p (mode, orig))
X temp = memory_address (mode, temp);
X }
X else
X {
X int old_used_const_equiv = used_const_equiv;
X
X used_const_equiv = 0;
X temp = gen_rtx (PLUS, GET_MODE (orig),
X copy_rtx_and_substitute (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X if (used_const_equiv)
X {
X if (GET_CODE (XEXP (temp, 0)) == CONST_INT)
X temp = plus_constant (XEXP (temp, 1), INTVAL (XEXP (temp, 0)));
X else if (GET_CODE (XEXP (temp, 1)) == CONST_INT)
X temp = plus_constant (XEXP (temp, 0), INTVAL (XEXP (temp, 1)));
X else if (memory_address_p (mode, orig))
X {
X try_use_const = 0;
X used_const_equiv = 0;
X temp = gen_rtx (PLUS, GET_MODE (orig),
X copy_rtx_and_substitute (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X }
X }
X else if (memory_address_p (mode, orig))
X temp = memory_address (mode, temp);
X
X used_const_equiv |= old_used_const_equiv;
X }
X return temp;
X
X case MULT:
X {
X int old_used_const_equiv = used_const_equiv;
X
X used_const_equiv = 0;
X
X temp = gen_rtx (MULT, GET_MODE (orig),
X copy_rtx_and_substitute (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X
X if (used_const_equiv)
X {
X if (GET_CODE (XEXP (temp, 0)) == CONST_INT
X && GET_CODE (XEXP (temp, 1)) == CONST_INT)
X temp = gen_rtx (CONST_INT, VOIDmode,
X INTVAL (XEXP (temp, 0)) * INTVAL (XEXP (temp, 1)));

X else
X {
X try_use_const = 0;
X used_const_equiv = 0;
X temp = gen_rtx (MULT, GET_MODE (orig),
X copy_rtx_and_substitute (XEXP (orig, 0)),
X copy_rtx_and_substitute (XEXP (orig, 1)));
X }
X }
X used_const_equiv |= old_used_const_equiv;
X }
X return temp;
X
X case MEM:
X /* Take care of easiest case here. */
X copy = XEXP (orig, 0);
X if (copy == frame_pointer_rtx || copy == arg_pointer_rtx)
X return gen_rtx (MEM, mode,
X plus_constant (frame_pointer_rtx, fp_delta));
X
X /* Allow a pushing-address even if that is not valid as an
X ordinary memory address. It indicates we are inlining a special
X push-insn. These must be copied; otherwise unshare_all_rtl
X might clobber them to point at temporary rtl of this function. */
X#ifdef STACK_GROWS_DOWNWARD
X if (GET_CODE (copy) == PRE_DEC && XEXP (copy, 0) == stack_pointer_rtx)
X return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy));
X#else
X if (GET_CODE (copy) == PRE_INC && XEXP (copy, 0) == stack_pointer_rtx)
X return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy));
X#endif
X
X /* If this is some other sort of address that isn't generally valid,
X break out all the registers referred to. */
X if (! memory_address_p (mode, copy))
X return gen_rtx (MEM, mode, copy_address (copy));
X
X /* There is no easy way to get our mode to `access_parm_map', which
X may need to know it, so here is most of the PLUS code duplicated. */
X if (GET_CODE (copy) == PLUS)
X {
X if (XEXP (copy, 0) == frame_pointer_rtx
X || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
X && XEXP (copy, 0) == arg_pointer_rtx))
X {
X rtx reg;
X reg = XEXP (copy, 0), copy = XEXP (copy, 1);
X
X if (GET_CODE (copy) == CONST_INT)
X {
X int c = INTVAL (copy);
X
X if (reg == arg_pointer_rtx && c >= first_parm_offset)
X return access_parm_map (c, mode);
X
X temp = gen_rtx (PLUS, Pmode,
X frame_pointer_rtx,
X gen_rtx (CONST_INT, SImode,
X c + fp_delta));
X if (! memory_address_p (Pmode, temp))
X {
X if (inline_fp_rtx == 0)
X {
X rtx last = get_last_insn ();
X inline_fp_rtx
X = copy_to_mode_reg (Pmode,
X plus_constant (frame_pointer_rtx, fp_delta));
X reorder_insns (NEXT_INSN (last), get_last_insn (), before_inline_fp_rtx);
X }
X return gen_rtx (MEM, mode, plus_constant (inline_fp_rtx, c));
X }
X }
X copy = copy_rtx_and_substitute (copy);
X temp = gen_rtx (PLUS, Pmode, frame_pointer_rtx, copy);
X temp = plus_constant (temp, fp_delta);
X temp = memory_address (Pmode, temp);
X }
X else if (reg_mentioned_p (frame_pointer_rtx, copy)
X || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
X && reg_mentioned_p (arg_pointer_rtx, copy)))
X {
X if (GET_CODE (XEXP (copy, 1)) == CONST_INT)
X {
X temp = copy_rtx_and_substitute (XEXP (copy, 0));
X temp = plus_constant (temp, INTVAL (XEXP (copy, 1)));
X }
X else
X {
X temp = gen_rtx (PLUS, GET_MODE (copy),
X copy_rtx_and_substitute (XEXP (copy, 0)),
X copy_rtx_and_substitute (XEXP (copy, 1)));
X }
X }
X else
X {
X if (GET_CODE (XEXP (copy, 1)) == CONST_INT)
X temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 0)),
X INTVAL (XEXP (copy, 1)));
X else
X {
X rtx left = copy_rtx_and_substitute (XEXP (copy, 0));
X rtx right = copy_rtx_and_substitute (XEXP (copy, 1));
X
X temp = gen_rtx (PLUS, GET_MODE (copy), left, right);
X }
X }
X }
X else
X temp = copy_rtx_and_substitute (copy);
X
X temp = change_address (orig, mode, temp);
X /* Deals with GCC bug for now. */
X RTX_UNCHANGING_P (temp) = 0;
X return temp;
X
X case RETURN:
X abort ();
X }
X
X copy = rtx_alloc (code);
X PUT_MODE (copy, mode);
X copy->in_struct = orig->in_struct;
X copy->volatil = orig->volatil;
X copy->unchanging = orig->unchanging;
X
X format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
X
X for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
X {
X switch (*format_ptr++)
X {
X case '0':
X break;
X
X case 'e':
X XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i));
X break;
X
X case 'u':
X /* Change any references to old-insns to point to the
X corresponding copied insns. */
X XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))];
X break;
X
X case 'E':
X XVEC (copy, i) = XVEC (orig, i);
X if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
X {
X XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
X for (j = 0; j < XVECLEN (copy, i); j++)
X XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j));
X }
X break;
X
X case 'i':
X XINT (copy, i) = XINT (orig, i);
X break;
X
X case 's':
X XSTR (copy, i) = XSTR (orig, i);
X break;
X
X default:
X abort ();
X }
X }
X
X if (code == ASM_OPERANDS && orig_asm_operands_vector == 0)
X {
X orig_asm_operands_vector = XVEC (orig, 3);
X copy_asm_operands_vector = XVEC (copy, 3);
X copy_asm_constraints_vector = XVEC (copy, 4);
X }
X
X return copy;
X}
X
X/* Get the value corresponding to an address relative to the arg pointer
X at index RELADDRESS. MODE is the machine mode of the reference.
X MODE is used only when the value is a REG.
X Pass VOIDmode for MODE when the mode is not known;
X in such cases, you should make sure the value is a MEM. */
X
Xstatic rtx
Xaccess_parm_map (reladdress, mode)
X int reladdress;
X enum machine_mode mode;
X{
X /* Index in parm_map. */
X int index = reladdress / UNITS_PER_WORD;
X /* Offset of the data being referenced
X from the beginning of the value for that parm. */
X int offset = reladdress % UNITS_PER_WORD;
X rtx copy;
X
X /* If we are referring to the middle of a multiword parm,
X find the beginning of that parm.
X OFFSET gets the offset of the reference from
X the beginning of the parm. */
X
X while (parm_map[index] == 0)
X {
X index--;
X if (index < first_parm_offset / UNITS_PER_WORD)
X /* If this abort happens, it means we need
X to handle "decrementing" INDEX back far
X enough to start looking among the reg parms
X instead of the stack parms. What a mess! */
X abort ();
X offset += UNITS_PER_WORD;
X }
X
X copy = parm_map[index];
X
X#ifdef BYTES_BIG_ENDIAN
X /* Subtract from OFFSET the offset of where
X the actual (non-BLKmode) parm value would start. */
X if (GET_MODE (copy) != BLKmode
X && GET_MODE_SIZE (GET_MODE (copy)) < UNITS_PER_WORD)
X offset
X -= (UNITS_PER_WORD
X - GET_MODE_SIZE (GET_MODE (copy)));
X#endif
X
X /* For memory ref, adjust it by the desired offset. */
X if (GET_CODE (copy) == MEM)
X {
X if (offset != 0 || GET_MODE (copy) != mode)
X return change_address (copy, mode,
X plus_constant (XEXP (copy, 0),
X offset));
X return copy;
X }
X
X if (GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG
X && ! CONSTANT_P (copy))
X abort ();
X if (mode == VOIDmode)
X abort ();
X
X /* A REG cannot be offset by bytes, so use a subreg
X (which is possible only in certain cases). */
X if (GET_MODE (copy) != mode
X && GET_MODE (copy) != VOIDmode)
X {
X int word;
X /* Crash if the portion of the arg wanted
X is not the least significant.
X Functions with refs to other parts of a
X parameter should not be inline--
X see function_cannot_inline_p. */
X#ifdef BYTES_BIG_ENDIAN
X if (offset + GET_MODE_SIZE (mode)
X != GET_MODE_SIZE (GET_MODE (copy)))
X abort ();
X#else
X if (offset != 0)
X abort ();
X#endif
X word = 0;
X if (GET_CODE (copy) == SUBREG)
X word = SUBREG_WORD (copy), copy = SUBREG_REG (copy);
X if (CONSTANT_P (copy))
X copy = force_reg (GET_MODE (copy), copy);
X return gen_rtx (SUBREG, mode, copy, word);
X }
X
X return copy;
X}
X
X/* Like copy_rtx_and_substitute but produces different output, suitable
X for an ideosyncractic address that isn't memory_address_p.
X The output resembles the input except that REGs and MEMs are replaced
X with new psuedo registers. All the "real work" is done in separate
X insns which set up the values of these new registers. */
X
Xstatic rtx
Xcopy_address (orig)
X register rtx orig;
X{
X register rtx copy;
X register int i, j;
X register RTX_CODE code;
X register enum machine_mode mode;
X register char *format_ptr;
X
X if (orig == 0)
X return 0;
X
X code = GET_CODE (orig);
X mode = GET_MODE (orig);
X
X switch (code)
X {
X case REG:
X if (REGNO (orig) != FRAME_POINTER_REGNUM)
X return copy_rtx_and_substitute (orig);
X return plus_constant (frame_pointer_rtx, fp_delta);
X
X case PLUS:
X if (XEXP (orig, 0) == frame_pointer_rtx)
X return plus_constant (orig, fp_delta);
X break;
X
X case MEM:
X return copy_to_reg (copy_rtx_and_substitute (orig));
X
X case CODE_LABEL:
X case LABEL_REF:
X return copy_rtx_and_substitute (orig);
X
X case PC:
X case CC0:
X case CONST_INT:
X case CONST_DOUBLE:
X case SYMBOL_REF:
X return orig;
X }
X
X copy = rtx_alloc (code);
X PUT_MODE (copy, mode);
X copy->in_struct = orig->in_struct;
X copy->volatil = orig->volatil;
X copy->unchanging = orig->unchanging;
X
X format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
X
X for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
X {
X switch (*format_ptr++)
X {
X case '0':
X break;
X
X case 'e':
X XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i));
X break;
X
X case 'u':
X /* Change any references to old-insns to point to the
X corresponding copied insns. */
X XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))];
X break;
X
X case 'E':
X XVEC (copy, i) = XVEC (orig, i);
X if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
X {
X XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
X for (j = 0; j < XVECLEN (copy, i); j++)
X XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j));
X }
X break;
X
X case 'i':
X XINT (copy, i) = XINT (orig, i);
X break;
X
X case 's':
X XSTR (copy, i) = XSTR (orig, i);
X break;
X
X default:
X abort ();
X }
X }
X return copy;
X}
X
X/* Return the constant equivalent of X. If X is a manifest
X constant, it is returned. If X is a register, we check
X to see if we happen to know its value as a constant. */
Xstatic rtx
Xconst_equiv (x)
X rtx x;
X{
X if (GET_CODE (x) == REG)
X {
X int regno = REGNO (x);
X if (const_equiv_map[regno]
X && const_age_map[regno] >= const_age)
X return const_equiv_map[regno];
X return 0;
X }
X if (CONSTANT_P (x))
X return x;
X return 0;
X}
X
X/* Attempt to simplify INSN while copying it from an inline fn,
X assuming it is a SET that sets CC0.
X
X If we simplify it, we emit the appropriate insns and return
X the last insn that we have handled (since we may handle the insn
X that follows INSN as well as INSN itself).
X
X Otherwise we do nothing and return zero. */
X
Xstatic rtx
Xtry_fold_cc0 (insn)
X rtx insn;
X{
X rtx cnst = copy_rtx_and_substitute (SET_SRC (PATTERN (insn)));
X rtx pat, copy;
X
X if (! CONSTANT_P (cnst))
X /* Constant equivlancies are with old, not new rtl. */
X cnst = const_equiv (SET_SRC (PATTERN (insn)));
X if (cnst
X /* @@ Cautious: Don't know how many of these tests we need. */
X && NEXT_INSN (insn)
X && GET_CODE (pat = PATTERN (NEXT_INSN (insn))) == SET
X && SET_DEST (pat) == pc_rtx
X && GET_CODE (pat = SET_SRC (pat)) == IF_THEN_ELSE
X && GET_RTX_LENGTH (GET_CODE (XEXP (pat, 0))) == 2)
X {
X rtx cnst2;
X rtx cond = XEXP (pat, 0);
X
X if ((XEXP (cond, 0) == cc0_rtx
X && (cnst2 = const_equiv (XEXP (cond, 1))))
X || (XEXP (cond, 1) == cc0_rtx
X && (cnst2 = const_equiv (XEXP (cond, 0)))))
X {
X copy = fold_out_const_cc0 (cond, XEXP (pat, 1), XEXP (pat, 2),
X cnst, cnst2);
X if (copy)
X {
X if (GET_CODE (copy) == LABEL_REF)
X {
X /* We will branch unconditionally to
X the label specified by COPY.
X Eliminate dead code by running down the
X list of insn until we see a CODE_LABEL.
X If the CODE_LABEL is the one specified
X by COPY, we win, and can delete all code
X up to (but not necessarily including)
X that label. Otherwise only win a little:
X emit the branch insn, and continue expanding. */
X rtx tmp = NEXT_INSN (insn);
X while (tmp && GET_CODE (tmp) != CODE_LABEL)
X tmp = NEXT_INSN (tmp);
X if (! tmp)
X abort ();
X if (label_map[CODE_LABEL_NUMBER (tmp)] == XEXP (copy, 0))
X {
X /* Big win. */
X return PREV_INSN (tmp);
X }
X else
X {
X /* Small win. Emit the unconditional branch,
X followed by a BARRIER, so that jump optimization
X will know what to do. */
X emit_jump (XEXP (copy, 0));
X return NEXT_INSN (insn);
X }
X }
X else if (copy == pc_rtx)
X {
X /* Do not take the branch, just fall through.
X Jump optimize should handle the elimination of
X dead code if appropriate. */
X return NEXT_INSN (insn);
X }
X else
X abort ();
X }
X }
X }
X return 0;
X}
X
X/* If (COND_RTX CNST1 CNST2) yield a result we can treat
X as being constant, return THEN_RTX if the result is always
X non-zero, and return ELSE_RTX otherwise. */
Xstatic rtx
Xfold_out_const_cc0 (cond_rtx, then_rtx, else_rtx, cnst1, cnst2)
X rtx cond_rtx, then_rtx, else_rtx;
X rtx cnst1, cnst2;
X{
X int value1, value2;
X int int1 = GET_CODE (cnst1) == CONST_INT;
X int int2 = GET_CODE (cnst2) == CONST_INT;
X if (int1)
X value1 = INTVAL (cnst1);
X else
X value1 = 1;
X if (int2)
X value2 = INTVAL (cnst2);
X else
X value2 = 1;
X
X switch (GET_CODE (cond_rtx))
X {
X case NE:
X if (int1 && int2)
X if (value1 != value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0 || value2 == 0)
X return copy_rtx_and_substitute (then_rtx);
X if (int1 == 0 && int2 == 0)
X if (rtx_equal_p (cnst1, cnst2))
X return copy_rtx_and_substitute (else_rtx);
X break;
X case EQ:
X if (int1 && int2)
X if (value1 == value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0 || value2 == 0)
X return copy_rtx_and_substitute (else_rtx);
X if (int1 == 0 && int2 == 0)
X if (rtx_equal_p (cnst1, cnst2))
X return copy_rtx_and_substitute (then_rtx);
X break;
X case GE:
X if (int1 && int2)
X if (value1 >= value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (else_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (then_rtx);
X break;
X case GT:
X if (int1 && int2)
X if (value1 > value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (else_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (then_rtx);
X break;
X case LE:
X if (int1 && int2)
X if (value1 <= value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (then_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (else_rtx);
X break;
X case LT:
X if (int1 && int2)
X if (value1 < value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (then_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (else_rtx);
X break;
X case GEU:
X if (int1 && int2)
X if ((unsigned)value1 >= (unsigned)value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (else_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (then_rtx);
X break;
X case GTU:
X if (int1 && int2)
X if ((unsigned)value1 > (unsigned)value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (else_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (then_rtx);
X break;
X case LEU:
X if (int1 && int2)
X if ((unsigned)value1 <= (unsigned)value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (then_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (else_rtx);
X break;
X case LTU:
X if (int1 && int2)
X if ((unsigned)value1 < (unsigned)value2)
X return copy_rtx_and_substitute (then_rtx);
X else
X return copy_rtx_and_substitute (else_rtx);
X if (value1 == 0)
X return copy_rtx_and_substitute (then_rtx);
X if (value2 == 0)
X return copy_rtx_and_substitute (else_rtx);
X break;
X }
X /* Could not hack it. */
X return 0;
X}
X
X/* If any CONST expressions with RTX_INTEGRATED_P are present in X,
X they should be in the constant pool.
X Run force_const_mem to put them there. */
X
Xstatic void
Xrestore_constants (x)
X rtx x;
X{
X int i, j;
X char *fmt = GET_RTX_FORMAT (GET_CODE (x));
X
X for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
X {
X switch (*fmt++)
X {
X case 'E':
X for (j = 0; j < XVECLEN (x, i); j++)
X if (RTX_INTEGRATED_P (XVECEXP (x, i, j))
X && GET_CODE (XVECEXP (x, i, j)) == CONST)
X XVECEXP (x, i, j) = force_const_mem (GET_MODE (XVECEXP (x, i, j)),
X XEXP (XVECEXP (x, i, j), 0));
X else
X restore_constants (XVECEXP (x, i, j));
X break;
X
X case 'e':
X if (RTX_INTEGRATED_P (XEXP (x, i))
X && GET_CODE (XEXP (x, i)) == CONST)
X XEXP (x, i) = force_const_mem (GET_MODE (XEXP (x, i)),
X XEXP (XEXP (x, i), 0));
X else
X restore_constants (XEXP (x, i));
X break;
X }
X }
X}
X
X/* Output the assembly language code for the function FNDECL
X from its DECL_SAVED_INSNS. Used for inline functions that are output
X at end of compilation instead of where they came in the source. */
X
Xvoid
Xoutput_inline_function (fndecl)
X tree fndecl;
X{
X rtx head = DECL_SAVED_INSNS (fndecl);
X rtx last;
X extern rtx stack_slot_list;
X
X temporary_allocation ();
X
X current_function_decl = fndecl;
X
X /* This call is only used to initialize global variables. */
X init_function_start (fndecl);
X
X /* Set stack frame size. */
X assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl));
X
X restore_reg_data (FIRST_PARM_INSN (head));
X
X stack_slot_list = XEXP (head, 9);
X
X /* There is no need to output a return label again. */
X return_label = 0;
X expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl));
X
X /* Find last insn and rebuild the constant pool. */
X for (last = FIRST_PARM_INSN (head);
X NEXT_INSN (last); last = NEXT_INSN (last))
X {
X#if 0
X /* No need to restore these constants again. */
X if (GET_CODE (last) == INSN || GET_CODE (last) == JUMP_INSN
X || GET_CODE (last) == CALL_INSN)
X restore_constants (PATTERN (last));
X#endif
X }
X
X set_new_first_and_last_insn (FIRST_PARM_INSN (head), last);
X
X /* Compile this function all the way down to assembly code. */
X rest_of_compilation (fndecl);
X
X current_function_decl = 0;
X
X permanent_allocation ();
X}
X#if 0
X
X/* Hashing of rtxs so that we don't make duplicates.
X The entry point is `rtx_hash_canon'. */
X
X/* Each hash table slot is a bucket containing a chain
X of these structures. */
X
Xstruct rtx_hash
X{
X struct rtx_hash *next; /* Next structure in the bucket. */
X int hashcode; /* Hash code of this type. */
X rtx x; /* The rtx recorded here. */
X};
X
X/* Now here is the hash table. This works exactly the same way
X that types are hashed in tree.c, except this is for rtxs. */
X
X#define RTX_HASH_SIZE 199
Xstruct rtx_hash *rtx_hash_table[RTX_HASH_SIZE];
X
X/* Here is how primitive or already-canonicalized types' hash
X codes are made. */
X#define RTX_HASH(RTX) (RTX)
X
X/* Look in the type hash table for a type isomorphic to RTX.
X If one is found, return it. Otherwise return 0. */
X
Xtree
Xrtx_hash_lookup (hashcode, x)
X int hashcode;
X tree x;
X{
X register struct rtx_hash *h;
X for (h = rtx_hash_table[hashcode % RTX_HASH_SIZE]; h; h = h->next)
X if (h->hashcode == hashcode
X && GET_CODE (h->x) == GET_CODE (x)
X && GET_MODE (h->x) == GET_MODE (x)
X#if 0
X && h->x->jump == x->jump
X && h->x->call == x->call
X && h->x->unchanging == x->unchanging
X && h->x->volatil == x->volatil
X && h->x->in_struct == x->in_struct
X && h->x->used == x->used
X && h->x->integrated == x->integrated
X#endif
X )
X {
X int i, j;
X int len = GET_RTX_LENGTH (GET_CODE (x));
X char *fmt = GET_RTX_FORMAT (GET_CODE (x));
X
X for (i = 0; i < len; i++)
X switch (fmt[i])
X {
X case '0':
X break;
X
X case 'e':
X case 'u':
X case 's':
X case 'S':
X if (XEXP (h->x, i) != XEXP (x, i))
X goto no_dice;
X break;
X
X case 'E':
X if (XVECLEN (h->x, i) != XVECLEN (x, i))
X goto no_dice;
X for (j = 0; j < XVECLEN (x, i); j++)
X if (XVECEXP (h->x, i, j) != XVECEXP (x, i, j))
X goto no_dice;
X break;
X
X case 'i':
X case 'n':
X if (INTVAL (XEXP (h->x, i)) != INTVAL (XEXP (x, i)))
X goto no_dice;
X
X default:
X abort ();
X }
X
X /* Everything matched. */
X return h->x;
X
X /* Try more. */
X no_dice:
X ;
X }
X
X /* Nothing matched. */
X return 0;
X}
X
X/* Add an entry to the rtx-hash-table
X for a type RTX whose hash code is HASHCODE. */
X
Xvoid
Xrtx_hash_add (hashcode, x)
X int hashcode;
X tree x;
X{
X register struct rtx_hash *h;
X
X h = (struct rtx_hash *) oballoc (sizeof (struct rtx_hash));
X h->hashcode = hashcode;
X h->x = x;
X h->next = rtx_hash_table[hashcode % RTX_HASH_SIZE];
X rtx_hash_table[hashcode % RTX_HASH_SIZE] = h;
X}
X
X/* Given RTX, and HASHCODE its hash code, return the canonical
X object for an identical rtx if one already exists.
X Otherwise, return RTX, and record it as the canonical object
X if it is a permanent object.
X
X To use this function, first create a rtx of the sort you want.
X Then compute its hash code from the fields of the rtx that
X make it different from other similar rtxs.
X Then call this function and use the value.
X This function frees the rtx you pass in if it is a duplicate. */
X
X/* Set to 1 to debug without canonicalization. Never set by program. */
Xint debug_no_rtx_hash = 0;
X
Xtree
Xrtx_hash_canon (hashcode, x)
X int hashcode;
X tree x;
X{
X tree x1;
X
X if (debug_no_rtx_hash)
X return x;
X
X x1 = rtx_hash_lookup (hashcode, x);
X if (x1 != 0)
X {
X struct obstack *o = maybepermanent_obstack;
X obstack_free (o, x);
X return x1;
X }
X
X /* If this is a new type, record it for later reuse. */
X rtx_hash_add (hashcode, x);
X
X return x;
X}
X#endif
!EOF
echo "Extracting varasm.c..."
sed 's/^X//' >varasm.c << '!EOF'
X/* Output variables, constants and external declarations, for GNU compiler.
X Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* This file handles generation of all the assembler code
X *except* the instructions of a function.
X This includes declarations of variables and their initial values.
X
X We also output the assembler code for constants stored in memory
X and are responsible for combining constants with the same value. */
X
X#include
X#include
X/* #include */
X#include "config.h"
X#include "rtl.h"
X#include "tree.h"
X#include "flags.h"
X#include "expr.h"
X#include "hard-reg-set.h"
X
X#include "obstack.h"
X
X#define MIN(a, b) ((a) < (b) ? (a) : (b))
X
X/* File in which assembler code is being written. */
X
Xextern FILE *asm_out_file;
X
X/* The (assembler) name of the first globally-visible object output. */
Xchar *first_global_object_name = 0;
X
Xextern struct obstack *current_obstack;
Xextern struct obstack *saveable_obstack;
Xextern struct obstack permanent_obstack;
X#define obstack_chunk_alloc xmalloc
Xextern int xmalloc ();
X
X/* Number for making the label on the next
X constant that is stored in memory. */
X
Xint const_labelno;
X
X/* Number for making the label on the next
X static variable internal to a function. */
X
Xint var_labelno;
X
X/* Nonzero if at least one function definition has been seen. */
Xstatic int function_defined;
X
Xextern FILE *asm_out_file;
X
Xstatic char *compare_constant_1 ();
Xstatic void record_constant_1 ();
Xvoid output_constant_pool ();
Xvoid assemble_name ();
Xvoid output_addressed_constants ();
Xvoid output_constant ();
Xvoid output_constructor ();
X
X#ifdef EXTRA_SECTIONS
Xstatic enum in_section {no_section, in_text, in_data, EXTRA_SECTIONS} in_section
X = no_section;
X#else
Xstatic enum in_section {no_section, in_text, in_data} in_section
X = no_section;
X#endif
X
X/* Define functions like text_section for any extra sections. */
X#ifdef EXTRA_SECTION_FUNCTIONS
XEXTRA_SECTION_FUNCTIONS
X#endif
X
X/* Tell assembler to switch to text section. */
X
Xvoid
Xtext_section ()
X{
X if (in_section != in_text)
X {
X fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
X in_section = in_text;
X }
X}
X
X/* Tell assembler to switch to data section. */
X
Xvoid
Xdata_section ()
X{
X if (in_section != in_data)
X {
X if (flag_shared_data)
X {
X#ifdef SHARED_SECTION_ASM_OP
X fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP);
X#else
X fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
X#endif
X }
X else
X fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
X
X in_section = in_data;
X }
X}
X
X/* Determine if we're in the text section. */
X
Xint
Xin_text_section ()
X{
X return in_section == in_text;
X}
X
X/* Create the rtl to represent a function, for a function definition.
X DECL is a FUNCTION_DECL node which describes which function.
X The rtl is stored into DECL. */
X
Xvoid
Xmake_function_rtl (decl)
X tree decl;
X{
X /* Rename a nested function to avoid conflicts. */
X if (DECL_CONTEXT (decl) != 0 && DECL_INITIAL (decl) != 0
X && DECL_RTL (decl) == 0)
X {
X char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
X char *label;
X
X ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
X DECL_ASSEMBLER_NAME (decl)
X = obstack_copy0 (saveable_obstack, label, strlen (label));
X var_labelno++;
X }
X
X if (DECL_RTL (decl) == 0)
X DECL_RTL (decl)
X = gen_rtx (MEM, DECL_MODE (decl),
X gen_rtx (SYMBOL_REF, Pmode, DECL_ASSEMBLER_NAME (decl)));
X
X /* Record at least one function has been defined. */
X function_defined = 1;
X}
X
X/* Decode an `asm' spec for a declaration as a register name.
X Return the register number, or -1 if nothing specified,
X or -2 if the name is not a register. */
X
Xint
Xdecode_reg_name (asmspec)
X char *asmspec;
X{
X if (asmspec != 0)
X {
X int i;
X extern char *reg_names[];
X
X for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
X if (!strcmp (asmspec, reg_names[i]))
X break;
X
X if (i < FIRST_PSEUDO_REGISTER)
X return i;
X else
X return -2;
X }
X
X return -1;
X}
X
X/* Create the DECL_RTL for a declaration for a static or external variable
X or static or external function.
X ASMSPEC, if not 0, is the string which the user specified
X as the assembler symbol name.
X TOP_LEVEL is nonzero if this is a file-scope variable.
X
X This is never called for PARM_DECL nodes. */
X
Xvoid
Xmake_decl_rtl (decl, asmspec, top_level)
X tree decl;
X char *asmspec;
X int top_level;
X{
X register char *name = DECL_ASSEMBLER_NAME (decl);
X int reg_number = decode_reg_name (asmspec);
X
X if (reg_number == -2)
X {
X name = (char *) obstack_alloc (saveable_obstack,
X strlen (asmspec) + 2);
X name[0] = '*';
X strcpy (&name[1], asmspec);
X }
X
X /* For a duplicate declaration, we can be called twice on the
X same DECL node. Don't alter the RTL already made
X unless the old mode is wrong (which can happen when
X the previous rtl was made when the type was incomplete). */
X if (DECL_RTL (decl) == 0
X || GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl))
X {
X DECL_RTL (decl) = 0;
X
X /* First detect errors in declaring global registers. */
X if (TREE_REGDECL (decl) && reg_number == -1)
X error_with_decl (decl,
X "register name not specified for `%s'");
X else if (TREE_REGDECL (decl) && reg_number == -2)
X error_with_decl (decl,
X "invalid register name for `%s'");
X else if (reg_number >= 0 && ! TREE_REGDECL (decl))
X error_with_decl (decl,
X "register name given for non-register variable `%s'");
X else if (TREE_REGDECL (decl) && TREE_CODE (decl) == FUNCTION_DECL)
X error ("function declared `register'");
X else if (TREE_REGDECL (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
X error_with_decl (decl, "data type of `%s' isn't suitable for a register");
X /* Now handle properly declared static register variables. */
X else if (TREE_REGDECL (decl))
X {
X int nregs;
X if (pedantic)
X warning ("ANSI C forbids global register variables");
X if (DECL_INITIAL (decl) != 0)
X {
X DECL_INITIAL (decl) = 0;
X error ("global register variable has initial value");
X }
X if (fixed_regs[reg_number] == 0
X && function_defined && top_level)
X error ("global register variable follows a function definition");
X if (TREE_THIS_VOLATILE (decl))
X warning ("volatile register variables don't work as you might wish");
X DECL_RTL (decl) = gen_rtx (REG, DECL_MODE (decl), reg_number);
X if (top_level)
X {
X /* Make this register fixed, so not usable for anything else. */
X nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl));
X while (nregs > 0)
X global_regs[reg_number + --nregs] = 1;
X init_reg_sets_1 ();
X }
X }
X
X /* Now handle ordinary static variables and functions (in memory).
X Also handle vars declared register invalidly. */
X if (DECL_RTL (decl) == 0)
X {
X /* Can't use just the variable's own name for a variable
X whose scope is less than the whole file.
X Concatenate a distinguishing number. */
X if (!top_level && !TREE_EXTERNAL (decl) && asmspec == 0)
X {
X char *label;
X
X ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
X name = obstack_copy0 (saveable_obstack, label, strlen (label));
X var_labelno++;
X }
X
X DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl),
X gen_rtx (SYMBOL_REF, Pmode, name));
X if (TREE_VOLATILE (decl))
X MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
X if (TREE_READONLY (decl))
X RTX_UNCHANGING_P (DECL_RTL (decl)) = 1;
X MEM_IN_STRUCT_P (DECL_RTL (decl))
X = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE);
X }
X }
X}
X
X/* Output a string of literal assembler code
X for an `asm' keyword used between functions. */
X
Xvoid
Xassemble_asm (string)
X tree string;
X{
X app_enable ();
X
X fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
X}
X
X/* Output assembler code for the constant pool of a function and associated
X with defining the name of the function. DECL describes the function.
X For the constant pool, we use the current constant pool data. */
X
Xvoid
Xassemble_function (decl)
X tree decl;
X{
X rtx x, n;
X char *fnname;
X int align;
X
X /* Get the function's name, as described by its RTL.
X This may be different from the DECL_NAME name used in the source file. */
X
X x = DECL_RTL (decl);
X if (GET_CODE (x) != MEM)
X abort ();
X n = XEXP (x, 0);
X if (GET_CODE (n) != SYMBOL_REF)
X abort ();
X fnname = XSTR (n, 0);
X
X /* The following code does not need preprocessing in the assembler. */
X
X app_disable ();
X
X output_constant_pool (fnname, decl);
X
X text_section ();
X
X#ifdef SDB_DEBUGGING_INFO
X /* Make sure types are defined for debugger before fcn name is defined. */
X if (write_symbols == SDB_DEBUG)
X sdbout_tags (gettags ());
X#endif
X
X /* Tell assembler to move to target machine's alignment for functions. */
X
X align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
X if (align > 0)
X ASM_OUTPUT_ALIGN (asm_out_file, align);
X
X#ifdef SDB_DEBUGGING_INFO
X /* Output SDB definition of the function. */
X if (write_symbols == SDB_DEBUG)
X sdbout_mark_begin_function ();
X#endif
X
X /* Make function name accessible from other files, if appropriate. */
X
X if (TREE_PUBLIC (decl))
X {
X if (!first_global_object_name)
X first_global_object_name = fnname;
X ASM_GLOBALIZE_LABEL (asm_out_file, fnname);
X }
X
X /* Do any machine/system dependent processing of the function name */
X#ifdef ASM_DECLARE_FUNCTION_NAME
X ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl);
X#else
X /* Standard thing is just output label for the function. */
X ASM_OUTPUT_LABEL (asm_out_file, fnname);
X#endif /* ASM_DECLARE_FUNCTION_NAME */
X}
X
X/* Assemble " .int 0\n" or whatever this assembler wants. */
X
Xvoid
Xassemble_integer_zero ()
X{
X ASM_OUTPUT_INT (asm_out_file, const0_rtx);
X}
X
X/* Assemble a string constant with the specified C string as contents. */
X
Xvoid
Xassemble_string (p, size)
X unsigned char *p;
X int size;
X{
X register int i;
X int pos = 0;
X int maximum = 2000;
X
X /* If the string is very long, split it up. */
X
X while (pos < size)
X {
X int thissize = size - pos;
X if (thissize > maximum)
X thissize = maximum;
X
X#ifdef ASM_OUTPUT_ASCII
X ASM_OUTPUT_ASCII (asm_out_file, p, thissize);
X#else
X fprintf (asm_out_file, "\t.ascii \"");
X
X for (i = 0; i < thissize; i++)
X {
X register int c = p[i];
X if (c == '\"' || c == '\\')
X putc ('\\', asm_out_file);
X if (c >= ' ' && c < 0177)
X putc (c, asm_out_file);
X else
X {
X fprintf (asm_out_file, "\\%o", c);
X /* After an octal-escape, if a digit follows,
X terminate one string constant and start another.
X The Vax assembler fails to stop reading the escape
X after three digits, so this is the only way we
X can get it to parse the data properly. */
X if (i < thissize - 1
X && p[i + 1] >= '0' && p[i + 1] <= '9')
X fprintf (asm_out_file, "\"\n\t.ascii \"");
X }
X }
X fprintf (asm_out_file, "\"\n");
X#endif /* no ASM_OUTPUT_ASCII */
X
X pos += thissize;
X p += thissize;
X }
X}
X
X/* Assemble everything that is needed for a variable or function declaration.
X Not used for automatic variables, and not used for function definitions.
X Should not be called for variables of incomplete structure type.
X
X TOP_LEVEL is nonzero if this variable has file scope.
X WRITE_SYMBOLS is DBX_DEBUG if writing dbx symbol output.
X The dbx data for a file-scope variable is written here.
X AT_END is nonzero if this is the special handling, at end of compilation,
X to define things that have had only tentative definitions. */
X
Xvoid
Xassemble_variable (decl, top_level, write_symbols, at_end)
X tree decl;
X int top_level;
X enum debugger write_symbols;
X int at_end;
X{
X register char *name;
X register int i;
X int align;
X
X /* Do nothing for global register variables. */
X
X if (GET_CODE (DECL_RTL (decl)) == REG)
X return;
X
X /* Normally no need to say anything for external references,
X since assembler considers all undefined symbols external. */
X
X if (TREE_EXTERNAL (decl))
X return;
X
X /* Output no assembler code for a function declaration.
X Only definitions of functions output anything. */
X
X if (TREE_CODE (decl) == FUNCTION_DECL)
X return;
X
X /* If type was incomplete when the variable was declared,
X see if it is complete now. */
X
X if (DECL_SIZE (decl) == 0)
X layout_decl (decl, 0);
X
X /* Still incomplete => don't allocate it; treat the tentative defn
X (which is what it must have been) as an `extern' reference. */
X
X if (DECL_SIZE (decl) == 0)
X {
X error_with_file_and_line (DECL_SOURCE_FILE (decl),
X DECL_SOURCE_LINE (decl),
X "storage size of static var `%s' isn't known",
X IDENTIFIER_POINTER (DECL_NAME (decl)));
X return;
X }
X
X /* The first declaration of a variable that comes through this function
X decides whether it is global (in C, has external linkage)
X or local (in C, has internal linkage). So do nothing more
X if this function has already run. */
X
X if (TREE_ASM_WRITTEN (decl))
X return;
X
X TREE_ASM_WRITTEN (decl) = 1;
X
X#ifdef DBX_DEBUGGING_INFO
X /* File-scope global variables are output here. */
X if (write_symbols == DBX_DEBUG && top_level)
X dbxout_symbol (decl, 0);
X#endif
X#ifdef SDB_DEBUGGING_INFO
X if (write_symbols == SDB_DEBUG && top_level)
X sdbout_symbol (decl, 0);
X#endif
X if (write_symbols == GDB_DEBUG)
X /* Make sure the file is known to GDB even if it has no functions. */
X set_current_gdbfile (DECL_SOURCE_FILE (decl));
X
X /* If storage size is erroneously variable, just continue.
X Error message was already made. */
X
X if (! TREE_LITERAL (DECL_SIZE (decl)))
X return;
X
X app_disable ();
X
X name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
X
X /* Handle uninitialized definitions. */
X
X if (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)
X {
X int size = (TREE_INT_CST_LOW (DECL_SIZE (decl))
X * DECL_SIZE_UNIT (decl)
X / BITS_PER_UNIT);
X int rounded = size;
X /* Don't allocate zero bytes of common,
X since that means "undefined external" in the linker. */
X if (size == 0) rounded = 1;
X /* Round size up to multiple of BIGGEST_ALIGNMENT bits
X so that each uninitialized object starts on such a boundary. */
X rounded = ((rounded + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
X / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
X * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
X if (flag_shared_data)
X data_section ();
X if (TREE_PUBLIC (decl))
X ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded);
X else
X ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
X return;
X }
X
X /* Handle initialized definitions. */
X
X /* First make the assembler name(s) global if appropriate. */
X if (TREE_PUBLIC (decl) && DECL_NAME (decl))
X {
X if (!first_global_object_name
X && (DECL_INITIAL (decl) == 0
X || TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR
X || CONSTRUCTOR_ELTS (DECL_INITIAL (decl)) != 0))
X first_global_object_name = name + (name[0] == '*');
X ASM_GLOBALIZE_LABEL (asm_out_file, name);
X }
X#if 0
X for (d = equivalents; d; d = TREE_CHAIN (d))
X {
X tree e = TREE_VALUE (d);
X if (TREE_PUBLIC (e) && DECL_NAME (e))
X ASM_GLOBALIZE_LABEL (asm_out_file,
X XSTR (XEXP (DECL_RTL (e), 0), 0));
X }
X#endif
X
X /* Output any data that we will need to use the address of. */
X if (DECL_INITIAL (decl))
X output_addressed_constants (DECL_INITIAL (decl));
X
X /* Switch to the proper section for this data. */
X#ifdef SELECT_SECTION
X SELECT_SECTION (decl);
X#else
X if (TREE_READONLY (decl) && ! TREE_VOLATILE (decl))
X text_section ();
X else
X data_section ();
X#endif
X
X /* Output the alignment of this data. */
X#ifdef DATA_ALIGNMENT
X /* On some machines, it is good to increase alignment sometimes. */
X align = DATA_ALIGNMENT (decl, DECL_ALIGN (decl));
X#else /* no DATA_ALIGNMENT */
X align = DECL_ALIGN (decl);
X#endif /* DATA_ALIGNMENT */
X
X for (i = 0; align >= BITS_PER_UNIT << (i + 1); i++);
X if (i > 0)
X ASM_OUTPUT_ALIGN (asm_out_file, i);
X
X /* Output the name(s) of this data. */
X ASM_OUTPUT_LABEL (asm_out_file, name);
X#if 0
X for (d = equivalents; d; d = TREE_CHAIN (d))
X {
X tree e = TREE_VALUE (d);
X ASM_OUTPUT_LABEL (asm_out_file, XSTR (XEXP (DECL_RTL (e), 0), 0));
X }
X#endif
X
X if (DECL_INITIAL (decl))
X /* Output the actual data. */
X output_constant (DECL_INITIAL (decl), int_size_in_bytes (TREE_TYPE (decl)));
X else
X /* Leave space for it. */
X ASM_OUTPUT_SKIP (asm_out_file, int_size_in_bytes (TREE_TYPE (decl)));
X}
X
X/* Output something to declare an external symbol to the assembler.
X (Most assemblers don't need this, so we normally output nothing.) */
X
Xvoid
Xassemble_external (decl)
X tree decl;
X{
X rtx rtl = DECL_RTL (decl);
X
X if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF)
X {
X#ifdef ASM_OUTPUT_EXTERNAL
X /* Some systems do require some output. */
X ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
X#endif
X }
X}
X
X/* Output to FILE a reference to the assembler name of a C-level name NAME.
X If NAME starts with a *, the rest of NAME is output verbatim.
X Otherwise NAME is transformed in an implementation-defined way
X (usually by the addition of an underscore).
X Many macros in the tm file are defined to call this function. */
X
Xvoid
Xassemble_name (file, name)
X FILE *file;
X char *name;
X{
X if (name[0] == '*')
X fputs (&name[1], file);
X else
X ASM_OUTPUT_LABELREF (file, name);
X}
X
X/* Allocate SIZE bytes writable static space with a gensym name
X and return an RTX to refer to its address. */
X
Xrtx
Xassemble_static_space (size)
X int size;
X{
X char name[12];
X char *namestring;
X rtx x;
X /* Round size up to multiple of BIGGEST_ALIGNMENT bits
X so that each uninitialized object starts on such a boundary. */
X int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
X / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
X * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
X
X if (flag_shared_data)
X data_section ();
X ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno);
X ++const_labelno;
X
X namestring = (char *) obstack_alloc (saveable_obstack,
X strlen (name) + 2);
X strcpy (namestring, name);
X
X x = gen_rtx (SYMBOL_REF, Pmode, namestring);
X ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
X return x;
X}
X#if 0
X/* Assemble the static constant template for function entry trampolines.
X This is done at most once per compilation.
X Returns an RTX for the address of the template. */
X
Xrtx
Xassemble_trampoline_template ()
X{
X char label[256];
X char *name;
X int align;
X
X /* Write the assembler code to define one. */
X align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
X if (align > 0)
X ASM_OUTPUT_ALIGN (asm_out_file, align);
X
X ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LTRAMP", 0);
X TRAMPOLINE_TEMPLATE (asm_out_file);
X
X /* Record the rtl to refer to it. */
X ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0);
X name
X = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
X return gen_rtx (SYMBOL_REF, Pmode, name);
X}
X#endif
X
X/* Here we combine duplicate floating constants to make
X CONST_DOUBLE rtx's, and force those out to memory when necessary. */
X
X/* Chain of all CONST_DOUBLE rtx's constructed for the current function.
X They are chained through the CONST_DOUBLE_CHAIN.
X A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain.
X In that case, CONST_DOUBLE_MEM is either a MEM,
X or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. */
X
Xstatic rtx real_constant_chain;
X
X/* Return a CONST_DOUBLE for a value specified as a pair of ints.
X For an integer, I0 is the low-order word and I1 is the high-order word.
X For a real number, I0 is the word with the low address
X and I1 is the word with the high address. */
X
Xrtx
Ximmed_double_const (i0, i1, mode)
X int i0, i1;
X enum machine_mode mode;
X{
X register rtx r;
X
X if (mode == DImode && i0 == 0 && i1 == 0)
X return const0_rtx;
X
X /* Search the chain for an existing CONST_DOUBLE with the right value.
X If one is found, return it. */
X
X for (r = real_constant_chain; r; r = CONST_DOUBLE_CHAIN (r))
X if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1
X && GET_MODE (r) == mode)
X return r;
X
X /* No; make a new one and add it to the chain. */
X
X r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1);
X
X CONST_DOUBLE_CHAIN (r) = real_constant_chain;
X real_constant_chain = r;
X
X /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain.
X Actual use of mem-slot is only through force_const_double_mem. */
X
X CONST_DOUBLE_MEM (r) = const0_rtx;
X
X return r;
X}
X
X/* Return a CONST_DOUBLE for a specified `double' value
X and machine mode. */
X
Xrtx
Ximmed_real_const_1 (d, mode)
X REAL_VALUE_TYPE d;
X enum machine_mode mode;
X{
X union real_extract u;
X register rtx r;
X REAL_VALUE_TYPE negated;
X
X /* Get the desired `double' value as a sequence of ints
X since that is how they are stored in a CONST_DOUBLE. */
X
X u.d = d;
X
X#if 0
X /* Detect special cases. */
X
X if (REAL_VALUES_EQUAL (dconst0, d))
X return (mode == DFmode ? dconst0_rtx : fconst0_rtx);
X else if (REAL_VALUES_EQUAL (dconst1, d))
X return (mode == DFmode ? dconst1_rtx : fconst1_rtx);
X
X if (sizeof u == 2 * sizeof (int))
X return immed_double_const (u.i[0], u.i[1], mode);
X#else
X /* Detect zero. */
X
X negated = REAL_VALUE_NEGATE (d);
X if (REAL_VALUES_EQUAL (negated, d))
X return (mode == DFmode ? dconst0_rtx : fconst0_rtx);
X
X if (sizeof u == 2 * sizeof (int))
X return immed_double_const (u.i[0], u.i[1], mode);
X#endif
X
X /* The rest of this function handles the case where
X a float value requires more than 2 ints of space.
X It will be deleted as dead code on machines that don't need it. */
X
X /* Search the chain for an existing CONST_DOUBLE with the right value.
X If one is found, return it. */
X
X for (r = real_constant_chain; r; r = CONST_DOUBLE_CHAIN (r))
X if (! bcmp (&CONST_DOUBLE_LOW (r), &u, sizeof u)
X && GET_MODE (r) == mode)
X return r;
X
X /* No; make a new one and add it to the chain. */
X
X r = rtx_alloc (CONST_DOUBLE);
X PUT_MODE (r, mode);
X bcopy (&u, &CONST_DOUBLE_LOW (r), sizeof u);
X
X CONST_DOUBLE_CHAIN (r) = real_constant_chain;
X real_constant_chain = r;
X
X /* Store const0_rtx in slot 2 since this CONST_DOUBLE is on the chain.
X Actual use of slot 2 is only through force_const_double_mem. */
X
X CONST_DOUBLE_MEM (r) = const0_rtx;
X
X return r;
X}
X
X/* Return a CONST_DOUBLE rtx for a value specified by EXP,
X which must be a REAL_CST tree node. */
X
Xrtx
Ximmed_real_const (exp)
X tree exp;
X{
X return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp)));
X}
X
X/* Given a CONST_DOUBLE, cause a constant in memory to be created
X (unless we already have one for the same value)
X and return a MEM rtx to refer to it.
X Put the CONST_DOUBLE on real_constant_chain if it isn't already there. */
X
Xrtx
Xforce_const_double_mem (r)
X rtx r;
X{
X if (GET_CODE (CONST_DOUBLE_MEM (r)) != MEM)
X force_const_mem (GET_MODE (r), r);
X
X /* CONST_DOUBLE_MEM (r) is now a MEM with a constant address.
X If that is legitimate, return it.
X Othewise it will need reloading, so return a copy of it. */
X if (memory_address_p (GET_MODE (r), XEXP (CONST_DOUBLE_MEM (r), 0)))
X return CONST_DOUBLE_MEM (r);
X return gen_rtx (MEM, GET_MODE (r), XEXP (CONST_DOUBLE_MEM (r), 0));
X}
X
X/* At the end of a function, forget the memory-constants
X previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain.
X Also clear out real_constant_chain and clear out all the chain-pointers. */
X
Xvoid
Xclear_const_double_mem ()
X{
X register rtx r, next;
X
X for (r = real_constant_chain; r; r = next)
X {
X next = CONST_DOUBLE_CHAIN (r);
X CONST_DOUBLE_CHAIN (r) = 0;
X CONST_DOUBLE_MEM (r) = cc0_rtx;
X }
X real_constant_chain = 0;
X}
X
X/* Given an expression EXP with a constant value,
X reduce it to the sum of an assembler symbol and an integer.
X Store them both in the structure *VALUE.
X Abort if EXP does not reduce. */
X
Xstruct addr_const
X{
X rtx base;
X int offset;
X};

X
Xstatic void
Xdecode_addr_const (exp, value)
X tree exp;
X struct addr_const *value;
X{
X register tree target = TREE_OPERAND (exp, 0);
X register int offset = 0;
X register rtx x;
X
X while (1)
X {
X if (TREE_CODE (target) == COMPONENT_REF)
X {
X offset += DECL_OFFSET (TREE_OPERAND (target, 1)) / BITS_PER_UNIT;
X target = TREE_OPERAND (target, 0);
X }
X else if (TREE_CODE (target) == ARRAY_REF)
X {
X if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST
X || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST)
X abort ();
X offset += ((TYPE_SIZE_UNIT (TREE_TYPE (target))
X * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target)))
X * TREE_INT_CST_LOW (TREE_OPERAND (target, 1)))
X / BITS_PER_UNIT);
X target = TREE_OPERAND (target, 0);
X }
X else break;
X }
X
X if (TREE_CODE (target) == VAR_DECL
X || TREE_CODE (target) == FUNCTION_DECL)
X x = DECL_RTL (target);
X else if (TREE_LITERAL (target))
X x = TREE_CST_RTL (target);
X else
X abort ();
X
X if (GET_CODE (x) != MEM)
X abort ();
X x = XEXP (x, 0);
X
X value->base = x;
X value->offset = offset;
X}
X
X/* Uniquize all constants that appear in memory.
X Each constant in memory thus far output is recorded
X in `const_hash_table' with a `struct constant_descriptor'
X that contains a polish representation of the value of
X the constant.
X
X We cannot store the trees in the hash table
X because the trees may be temporary. */
X
Xstruct constant_descriptor
X{
X struct constant_descriptor *next;
X char *label;
X char contents[1];
X};
X
X#define HASHBITS 30
X#define MAX_HASH_TABLE 1009
Xstatic struct constant_descriptor *const_hash_table[MAX_HASH_TABLE];
X
X/* Compute a hash code for a constant expression. */
X
Xint
Xconst_hash (exp)
X tree exp;
X{
X register char *p;
X register int len, hi, i;
X register enum tree_code code = TREE_CODE (exp);
X
X if (code == INTEGER_CST)
X {
X p = (char *) &TREE_INT_CST_LOW (exp);
X len = 2 * sizeof TREE_INT_CST_LOW (exp);
X }
X else if (code == REAL_CST)
X {
X p = (char *) &TREE_REAL_CST (exp);
X len = sizeof TREE_REAL_CST (exp);
X }
X else if (code == STRING_CST)
X p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp);
X else if (code == COMPLEX_CST)
X return const_hash (TREE_REALPART (exp)) * 5
X + const_hash (TREE_IMAGPART (exp));
X else if (code == CONSTRUCTOR)
X {
X register tree link;
X
X /* For record type, include the type in the hashing.
X We do not do so for array types
X because (1) the sizes of the elements are sufficient
X and (2) distinct array types can have the same constructor. */
X if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
X hi = ((int) TREE_TYPE (exp) & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE;
X else
X hi = 5;
X
X for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
X hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE;
X
X return hi;
X }
X else if (code == ADDR_EXPR)
X {
X struct addr_const value;
X decode_addr_const (exp, &value);
X p = (char *) &value;
X len = sizeof value;
X }
X else if (code == PLUS_EXPR || code == MINUS_EXPR)
X return const_hash (TREE_OPERAND (exp, 0)) * 9
X + const_hash (TREE_OPERAND (exp, 1));
X else if (code == NOP_EXPR || code == CONVERT_EXPR)
X return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2;
X
X /* Compute hashing function */
X hi = len;
X for (i = 0; i < len; i++)
X hi = ((hi * 613) + (unsigned)(p[i]));
X
X hi &= (1 << HASHBITS) - 1;
X hi %= MAX_HASH_TABLE;
X return hi;
X}
X
X/* Compare a constant expression EXP with a constant-descriptor DESC.
X Return 1 if DESC describes a constant with the same value as EXP. */
X
Xstatic int
Xcompare_constant (exp, desc)
X tree exp;
X struct constant_descriptor *desc;
X{
X return 0 != compare_constant_1 (exp, desc->contents);
X}
X
X/* Compare constant expression EXP with a substring P of a constant descriptor.
X If they match, return a pointer to the end of the substring matched.
X If they do not match, return 0.
X
X Since descriptors are written in polish prefix notation,
X this function can be used recursively to test one operand of EXP
X against a subdescriptor, and if it succeeds it returns the
X address of the subdescriptor for the next operand. */
X
Xstatic char *
Xcompare_constant_1 (exp, p)
X tree exp;
X char *p;
X{
X register char *strp;
X register int len;
X register enum tree_code code = TREE_CODE (exp);
X
X if (code != (enum tree_code) *p++)
X return 0;
X
X if (code == INTEGER_CST)
X {
X /* Integer constants are the same only if the same width of type. */
X if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
X return 0;
X strp = (char *) &TREE_INT_CST_LOW (exp);
X len = 2 * sizeof TREE_INT_CST_LOW (exp);
X }
X else if (code == REAL_CST)
X {
X /* Real constants are the same only if the same width of type. */
X if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
X return 0;
X strp = (char *) &TREE_REAL_CST (exp);
X len = sizeof TREE_REAL_CST (exp);
X }
X else if (code == STRING_CST)
X {
X if (flag_writable_strings)
X return 0;
X strp = TREE_STRING_POINTER (exp);
X len = TREE_STRING_LENGTH (exp);
X if (bcmp (&TREE_STRING_LENGTH (exp), p,
X sizeof TREE_STRING_LENGTH (exp)))
X return 0;
X p += sizeof TREE_STRING_LENGTH (exp);
X }
X else if (code == COMPLEX_CST)
X {
X p = compare_constant_1 (TREE_REALPART (exp), p);
X if (p == 0) return 0;
X p = compare_constant_1 (TREE_IMAGPART (exp), p);
X return p;
X }
X else if (code == CONSTRUCTOR)
X {
X register tree link;
X int length = list_length (CONSTRUCTOR_ELTS (exp));
X tree type;
X
X if (bcmp (&length, p, sizeof length))
X return 0;
X p += sizeof length;
X
X /* For record constructors, insist that the types match.
X For arrays, just verify both constructors are for arrays. */
X if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
X type = TREE_TYPE (exp);
X else
X type = 0;
X if (bcmp (&type, p, sizeof type))
X return 0;
X p += sizeof type;
X
X for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
X if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0)
X return 0;
X return p;
X }
X else if (code == ADDR_EXPR)
X {
X struct addr_const value;
X decode_addr_const (exp, &value);
X strp = (char *) &value;
X len = sizeof value;
X /* Compare SYMBOL_REF address and offset. */
X while (--len >= 0)
X if (*p++ != *strp++)
X return 0;
X /* Compare symbol name. */
X strp = XSTR (value.base, 0);
X len = strlen (strp) + 1;
X }
X else if (code == PLUS_EXPR || code == MINUS_EXPR)
X {
X p = compare_constant_1 (TREE_OPERAND (exp, 0), p);
X if (p == 0) return 0;
X p = compare_constant_1 (TREE_OPERAND (exp, 1), p);
X return p;
X }
X else if (code == NOP_EXPR || code == CONVERT_EXPR)
X {
X p = compare_constant_1 (TREE_OPERAND (exp, 0), p);
X return p;
X }
X
X /* Compare constant contents. */
X while (--len >= 0)
X if (*p++ != *strp++)
X return 0;
X
X return p;
X}
X
X/* Construct a constant descriptor for the expression EXP.
X It is up to the caller to enter the descriptor in the hash table. */
X
Xstatic struct constant_descriptor *
Xrecord_constant (exp)
X tree exp;
X{
X struct constant_descriptor *ptr = 0;
X int buf;
X
X obstack_grow (&permanent_obstack, &ptr, sizeof ptr);
X obstack_grow (&permanent_obstack, &buf, sizeof buf);
X record_constant_1 (exp);
X return (struct constant_descriptor *) obstack_finish (&permanent_obstack);
X}
X
X/* Add a description of constant expression EXP
X to the object growing in `permanent_obstack'.
X No need to return its address; the caller will get that
X from the obstack when the object is complete. */
X
Xstatic void
Xrecord_constant_1 (exp)
X tree exp;
X{
X register char *strp;
X register int len;
X register enum tree_code code = TREE_CODE (exp);
X
X obstack_1grow (&permanent_obstack, (unsigned int) code);
X
X if (code == INTEGER_CST)
X {
X obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
X strp = (char *) &TREE_INT_CST_LOW (exp);
X len = 2 * sizeof TREE_INT_CST_LOW (exp);
X }
X else if (code == REAL_CST)
X {
X obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
X strp = (char *) &TREE_REAL_CST (exp);
X len = sizeof TREE_REAL_CST (exp);
X }
X else if (code == STRING_CST)
X {
X if (flag_writable_strings)
X return;
X strp = TREE_STRING_POINTER (exp);
X len = TREE_STRING_LENGTH (exp);
X obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp),
X sizeof TREE_STRING_LENGTH (exp));
X }
X else if (code == COMPLEX_CST)
X {
X record_constant_1 (TREE_REALPART (exp));
X record_constant_1 (TREE_IMAGPART (exp));
X return;
X }
X else if (code == CONSTRUCTOR)
X {
X register tree link;
X int length = list_length (CONSTRUCTOR_ELTS (exp));
X tree type;
X
X obstack_grow (&permanent_obstack, (char *) &length, sizeof length);
X
X /* For record constructors, insist that the types match.
X For arrays, just verify both constructors are for arrays. */
X if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
X type = TREE_TYPE (exp);
X else
X type = 0;
X obstack_grow (&permanent_obstack, (char *) &type, sizeof type);
X
X for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
X record_constant_1 (TREE_VALUE (link));
X return;
X }
X else if (code == ADDR_EXPR)
X {
X struct addr_const value;
X decode_addr_const (exp, &value);
X /* Record the SYMBOL_REF address and the offset. */
X obstack_grow (&permanent_obstack, (char *) &value, sizeof value);
X /* Record the symbol name. */
X obstack_grow (&permanent_obstack, XSTR (value.base, 0),
X strlen (XSTR (value.base, 0)) + 1);
X return;
X }
X else if (code == PLUS_EXPR || code == MINUS_EXPR)
X {
X record_constant_1 (TREE_OPERAND (exp, 0));
X record_constant_1 (TREE_OPERAND (exp, 1));
X return;
X }
X else if (code == NOP_EXPR || code == CONVERT_EXPR)
X {
X record_constant_1 (TREE_OPERAND (exp, 0));
X return;
X }
X
X /* Record constant contents. */
X obstack_grow (&permanent_obstack, strp, len);
X}
X
X/* Return the constant-label-string for constant value EXP.
X If no constant equal to EXP has yet been output,
X define a new label and output assembler code for it.
X The const_hash_table records which constants already have label strings. */
X
Xstatic char *
Xget_or_assign_label (exp)
X tree exp;
X{
X register int hash, i, align;
X register struct constant_descriptor *desc;
X char label[256];
X
X /* Make sure any other constants whose addresses appear in EXP
X are assigned label numbers. */
X
X output_addressed_constants (exp);
X
X /* Compute hash code of EXP. Search the descriptors for that hash code
X to see if any of them describes EXP. If yes, the descriptor records
X the label number already assigned. */
X
X hash = const_hash (exp) % MAX_HASH_TABLE;
X
X for (desc = const_hash_table[hash]; desc; desc = desc->next)
X if (compare_constant (exp, desc))
X return desc->label;
X
X /* No constant equal to EXP is known to have been output.
X Make a constant descriptor to enter EXP in the hash table.
X Assign the label number and record it in the descriptor for
X future calls to this function to find. */
X
X desc = record_constant (exp);
X desc->next = const_hash_table[hash];
X const_hash_table[hash] = desc;
X
X /* Now output assembler code to define that label
X and follow it with the data of EXP. */
X
X /* First switch to text section, except for writable strings. */
X#ifdef SELECT_SECTION
X SELECT_SECTION (exp);
X#else
X if ((TREE_CODE (exp) == STRING_CST) && flag_writable_strings)
X data_section ();
X else
X text_section ();
X#endif
X
X /* Align the location counter as required by EXP's data type. */
X#ifdef DATA_ALIGNMENT
X align = DATA_ALIGNMENT (exp, TYPE_ALIGN (TREE_TYPE (exp)));
X#else
X align = TYPE_ALIGN (TREE_TYPE (exp));
X#endif
X
X for (i = 0; align >= BITS_PER_UNIT << (i + 1); i++);
X if (i > 0)
X ASM_OUTPUT_ALIGN (asm_out_file, i);
X
X /* Output the label itself. */
X ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", const_labelno);
X
X /* Output the value of EXP. */
X output_constant (exp,
X (TREE_CODE (exp) == STRING_CST
X ? TREE_STRING_LENGTH (exp)
X : int_size_in_bytes (TREE_TYPE (exp))));
X
X /* Create a string containing the label name, in LABEL. */
X ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
X
X ++const_labelno;
X
X desc->label
X = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
X
X return desc->label;
X}
X
X/* Return an rtx representing a reference to constant data in memory
X for the constant expression EXP.
X If assembler code for such a constant has already been output,
X return an rtx to refer to it.
X Otherwise, output such a constant in memory and generate
X an rtx for it. The TREE_CST_RTL of EXP is set up to point to that rtx. */
X
Xrtx
Xoutput_constant_def (exp)
X tree exp;
X{
X register rtx def;
X int temp_p = allocation_temporary_p ();
X
X if (TREE_CODE (exp) == INTEGER_CST)
X abort (); /* No TREE_CST_RTL slot in these. */
X
X if (TREE_CST_RTL (exp))
X return TREE_CST_RTL (exp);
X
X if (TREE_PERMANENT (exp))
X end_temporary_allocation ();
X
X def = gen_rtx (SYMBOL_REF, Pmode, get_or_assign_label (exp));
X
X TREE_CST_RTL (exp)
X = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def);
X RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1;
X
X if (temp_p && TREE_PERMANENT (exp))
X resume_temporary_allocation ();
X
X return TREE_CST_RTL (exp);
X}
X
X/* Similar hash facility for making memory-constants
X from constant rtl-expressions. It is used on RISC machines
X where immediate integer arguments and constant addresses are restricted
X so that such constants must be stored in memory.
X
X This pool of constants is reinitialized for each function
X so each function gets its own constants-pool that comes right before it.
X
X All structures allocated here are discarded when functions are saved for
X inlining, so they do not need to be allocated permanently. */
X
X#define MAX_RTX_HASH_TABLE 61
Xstatic struct constant_descriptor *const_rtx_hash_table[MAX_RTX_HASH_TABLE];
X
X/* Structure to represent sufficient information about a constant so that
X it can be output when the constant pool is output, so that function
X integration can be done, and to simplify handling on machines that reference
X constant pool as base+displacement. */
X
Xstruct pool_constant
X{
X struct constant_descriptor *desc;
X struct pool_constant *next;
X enum machine_mode mode;
X rtx constant;
X int labelno;
X int align;
X int offset;
X};
X
X/* Pointers to first and last constant in pool. */
X
Xstatic struct pool_constant *first_pool, *last_pool;
X
X/* Current offset in constant pool (does not include any machine-specific
X header. */
X
Xstatic int pool_offset;
X
X/* Structure used to maintain hash table mapping symbols used to their
X corresponding constants. */
X
Xstruct pool_sym
X{
X char *label;
X struct pool_constant *pool;
X struct pool_sym *next;
X};
X
Xstatic struct pool_sym *const_rtx_sym_hash_table[MAX_RTX_HASH_TABLE];
X
X/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true.
X The argument is XSTR (... , 0) */
X

X#define SYMHASH(LABEL) \
X ((((int) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE)
X
X/* Initialize constant pool hashing for next function. */
X
Xvoid
Xinit_const_rtx_hash_table ()
X{
X bzero (const_rtx_hash_table, sizeof const_rtx_hash_table);
X bzero (const_rtx_sym_hash_table, sizeof const_rtx_sym_hash_table);
X
X first_pool = last_pool = 0;
X pool_offset = 0;
X}
X
Xstruct rtx_const
X{
X enum kind { RTX_DOUBLE, RTX_INT } kind : 16;
X enum machine_mode mode : 16;
X union {
X union real_extract du;
X struct addr_const addr;
X } un;
X};
X
X/* Express an rtx for a constant integer (perhaps symbolic)
X as the sum of a symbol or label plus an explicit integer.
X They are stored into VALUE. */
X
Xstatic void
Xdecode_rtx_const (mode, x, value)
X enum machine_mode mode;
X rtx x;
X struct rtx_const *value;
X{
X /* Clear the whole structure, including any gaps. */
X
X {
X int *p = (int *) value;
X int *end = (int *) (value + 1);
X while (p < end)
X *p++ = 0;
X }
X
X value->kind = RTX_INT; /* Most usual kind. */
X value->mode = mode;
X
X switch (GET_CODE (x))
X {
X case CONST_DOUBLE:
X value->kind = RTX_DOUBLE;
X value->mode = GET_MODE (x);
X bcopy (&CONST_DOUBLE_LOW (x), &value->un.du, sizeof value->un.du);
X break;
X
X case CONST_INT:
X value->un.addr.offset = INTVAL (x);
X break;
X
X case SYMBOL_REF:
X value->un.addr.base = x;
X break;
X
X case LABEL_REF:
X value->un.addr.base = x;
X break;
X
X case CONST:
X x = XEXP (x, 0);
X if (GET_CODE (x) == PLUS)
X {
X value->un.addr.base = XEXP (XEXP (x, 0), 0);
X if (GET_CODE (XEXP (x, 1)) != CONST_INT)
X abort ();
X value->un.addr.offset = INTVAL (XEXP (x, 1));
X }
X else if (GET_CODE (x) == MINUS)
X {
X value->un.addr.base = XEXP (x, 0);
X if (GET_CODE (XEXP (x, 1)) != CONST_INT)
X abort ();
X value->un.addr.offset = - INTVAL (XEXP (x, 1));
X }
X else
X abort ();
X break;
X
X default:
X abort ();
X }
X
X if (value->kind == RTX_INT && value->un.addr.base != 0)
X switch (GET_CODE (value->un.addr.base))
X {
X case SYMBOL_REF:
X case LABEL_REF:
X /* Use the string's address, not the SYMBOL_REF's address,
X for the sake of addresses of library routines.
X For a LABEL_REF, compare labels. */
X value->un.addr.base = XEXP (value->un.addr.base, 0);
X }
X}
X
X/* Compute a hash code for a constant RTL expression. */
X
Xint
Xconst_hash_rtx (mode, x)
X enum machine_mode mode;
X rtx x;
X{
X register int hi, i;
X
X struct rtx_const value;
X decode_rtx_const (mode, x, &value);
X
X /* Compute hashing function */
X hi = 0;
X for (i = 0; i < sizeof value / sizeof (int); i++)
X hi += ((int *) &value)[i];
X
X hi &= (1 << HASHBITS) - 1;
X hi %= MAX_RTX_HASH_TABLE;
X return hi;
X}
X
X/* Compare a constant rtl object X with a constant-descriptor DESC.
X Return 1 if DESC describes a constant with the same value as X. */
X
Xstatic int
Xcompare_constant_rtx (mode, x, desc)
X enum machine_mode mode;
X rtx x;
X struct constant_descriptor *desc;
X{
X register int *p = (int *) desc->contents;
X register int *strp;
X register int len;
X struct rtx_const value;
X
X decode_rtx_const (mode, x, &value);
X strp = (int *) &value;
X len = sizeof value / sizeof (int);
X
X /* Compare constant contents. */
X while (--len >= 0)
X if (*p++ != *strp++)
X return 0;
X
X return 1;
X}
X
X/* Construct a constant descriptor for the rtl-expression X.
X It is up to the caller to enter the descriptor in the hash table. */
X
Xstatic struct constant_descriptor *
Xrecord_constant_rtx (mode, x)
X enum machine_mode mode;
X rtx x;
X{
X struct constant_descriptor *ptr;
X char *label;
X struct rtx_const value;
X
X decode_rtx_const (mode, x, &value);
X
X obstack_grow (current_obstack, &ptr, sizeof ptr);
X obstack_grow (current_obstack, &label, sizeof label);
X
X /* Record constant contents. */
X obstack_grow (current_obstack, &value, sizeof value);
X
X return (struct constant_descriptor *) obstack_finish (current_obstack);
X}
X
X/* Given a constant rtx X, make (or find) a memory constant for its value
X and return a MEM rtx to refer to it in memory. */
X
Xrtx
Xforce_const_mem (mode, x)
X enum machine_mode mode;
X rtx x;
X{
X register int hash;
X register struct constant_descriptor *desc;
X char label[256];
X char *found = 0;
X rtx def;
X
X if (GET_CODE (x) == CONST_DOUBLE
X && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM)
X return CONST_DOUBLE_MEM (x);
X
X /* Compute hash code of X. Search the descriptors for that hash code
X to see if any of them describes X. If yes, the descriptor records
X the label number already assigned. */
X
X hash = const_hash_rtx (mode, x);
X
X for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next)
X if (compare_constant_rtx (mode, x, desc))
X {
X found = desc->label;
X break;
X }
X
X if (found == 0)
X {
X register struct pool_constant *pool;
X register struct pool_sym *sym;
X int align;
X
X /* No constant equal to X is known to have been output.
X Make a constant descriptor to enter X in the hash table.
X Assign the label number and record it in the descriptor for
X future calls to this function to find. */
X
X desc = record_constant_rtx (mode, x);
X desc->next = const_rtx_hash_table[hash];
X const_rtx_hash_table[hash] = desc;
X
X /* Align the location counter as required by EXP's data type. */
X align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode);
X if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT)
X align = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
X
X pool_offset += align - 1;
X pool_offset &= ~ (align - 1);
X
X /* Allocate a pool constant descriptor, fill it in, and chain it in. */
X
X pool = (struct pool_constant *) oballoc (sizeof (struct pool_constant));
X pool->desc = desc;
X pool->constant = x;
X pool->mode = mode;
X pool->labelno = const_labelno;
X pool->align = align;
X pool->offset = pool_offset;
X pool->next = 0;
X
X if (last_pool == 0)
X first_pool = pool;
X else
X last_pool->next = pool;
X
X last_pool = pool;
X pool_offset += GET_MODE_SIZE (mode);
X
X /* Create a string containing the label name, in LABEL. */
X ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
X
X ++const_labelno;
X
X desc->label = found
X = (char *) obstack_copy0 (saveable_obstack, label, strlen (label));
X
X /* Add label to symbol hash table. */
X hash = SYMHASH (found);
X sym = (struct pool_sym *) oballoc (sizeof (struct pool_sym));
X sym->label = found;
X sym->pool = pool;
X sym->next = const_rtx_sym_hash_table[hash];
X const_rtx_sym_hash_table[hash] = sym;
X }
X
X /* We have a symbol name; construct the SYMBOL_REF and the MEM. */
X
X def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, found));
X
X RTX_UNCHANGING_P (def) = 1;
X /* Mark the symbol_ref as belonging to this constants pool. */
X CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1;
X
X if (GET_CODE (x) == CONST_DOUBLE)
X {
X if (CONST_DOUBLE_MEM (x) == cc0_rtx)
X {
X CONST_DOUBLE_CHAIN (x) = real_constant_chain;
X real_constant_chain = x;
X }
X CONST_DOUBLE_MEM (x) = def;
X }
X
X return def;
X}
X
X/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to
X the corresponding pool_constant structure. */
X
Xstatic struct pool_constant *
Xfind_pool_constant (addr)
X rtx addr;
X{
X struct pool_sym *sym;
X char *label = XSTR (addr, 0);
X
X for (sym = const_rtx_sym_hash_table[SYMHASH (label)]; sym; sym = sym->next)
X if (sym->label == label)
X return sym->pool;
X
X abort ();
X}
X
X/* Given a constant pool SYMBOL_REF, return the corresponding constant. */
X
Xrtx
Xget_pool_constant (addr)
X rtx addr;
X{
X return (find_pool_constant (addr))->constant;
X}
X
X/* Similar, return the mode. */
X
Xenum machine_mode
Xget_pool_mode (addr)
X rtx addr;
X{
X return (find_pool_constant (addr))->mode;
X}
X
X/* Similar, return the offset in the constant pool. */
X
Xint
Xget_pool_offset (addr)
X rtx addr;
X{
X return (find_pool_constant (addr))->offset;
X}
X
X/* Write all the constants in the constant pool. */
X
Xvoid
Xoutput_constant_pool (fnname, fndecl)
X char *fnname;
X tree fndecl;
X{
X struct pool_constant *pool;
X rtx x;
X
X#ifdef ASM_OUTPUT_POOL_PROLOGUE
X ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset);
X#endif
X
X for (pool = first_pool; pool; pool = pool->next)
X {
X x = pool->constant;
X
X /* First switch to correct section. */
X#ifdef SELECT_RTX_SECTION
X SELECT_RTX_SECTION (pool->mode, x);
X#else
X text_section ();
X#endif
X
X if (pool->align > 1)
X ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (pool->align));
X
X /* Output the label. */
X ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", pool->labelno);
X
X /* Output the value of the constant itself. */
X if (GET_CODE (x) == CONST_DOUBLE)
X {
X union real_extract u;
X
X bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u);
X switch (pool->mode)
X {
X /* Perhaps change the following to use
X CONST_DOUBLE_LOW and CONST_DOUBLE_HIGH, rather than u.i. */
X case DImode:
X#ifdef ASM_OUTPUT_DOUBLE_INT
X ASM_OUTPUT_DOUBLE_INT (asm_out_file, x);
X#else /* no ASM_OUTPUT_DOUBLE_INT */
X#ifndef WORDS_BIG_ENDIAN
X /* Output two ints. */
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode, u.i[0]));
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode, u.i[1]));
X#else
X /* Output two ints. */
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode, u.i[1]));
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode, u.i[0]));
X#endif /* WORDS_BIG_ENDIAN */
X#endif /* no ASM_OUTPUT_DOUBLE_INT */
X break;
X
X case DFmode:
X ASM_OUTPUT_DOUBLE (asm_out_file, u.d);
X break;
X
X case SFmode:
X ASM_OUTPUT_FLOAT (asm_out_file, u.d);
X }
X }
X else
X switch (pool->mode)
X {
X case SImode:
X ASM_OUTPUT_INT (asm_out_file, x);
X break;
X
X case HImode:
X ASM_OUTPUT_SHORT (asm_out_file, x);
X break;
X
X case QImode:
X ASM_OUTPUT_CHAR (asm_out_file, x);
X break;
X }
X }
X /* Done with this pool. */
X first_pool = last_pool = 0;
X}
X
X/* Find all the constants whose addresses are referenced inside of EXP,
X and make sure assembler code with a label has been output for each one. */
X
Xvoid
Xoutput_addressed_constants (exp)
X tree exp;
X{
X switch (TREE_CODE (exp))
X {
X case ADDR_EXPR:
X {
X register tree constant = TREE_OPERAND (exp, 0);
X
X while (TREE_CODE (constant) == COMPONENT_REF)
X {
X constant = TREE_OPERAND (constant, 0);
X }
X
X if (TREE_LITERAL (constant))
X /* No need to do anything here
X for addresses of variables or functions. */
X output_constant_def (constant);
X }
X break;
X
X case PLUS_EXPR:
X case MINUS_EXPR:
X output_addressed_constants (TREE_OPERAND (exp, 0));
X output_addressed_constants (TREE_OPERAND (exp, 1));
X break;
X
X case NOP_EXPR:
X case CONVERT_EXPR:
X output_addressed_constants (TREE_OPERAND (exp, 0));
X break;
X
X case CONSTRUCTOR:
X {
X register tree link;
X for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
X output_addressed_constants (TREE_VALUE (link));
X }
X break;
X
X case ERROR_MARK:
X break;
X
X default:
X if (! TREE_LITERAL (exp))
X abort ();
X }
X}
X
X/* Output assembler code for constant EXP to FILE, with no label.
X This includes the pseudo-op such as ".int" or ".byte", and a newline.
X Assumes output_addressed_constants has been done on EXP already.
X
X Generate exactly SIZE bytes of assembler data, padding at the end
X with zeros if necessary. SIZE must always be specified.
X
X SIZE is important for structure constructors,
X since trailing members may have been omitted from the constructor.
X It is also important for initialization of arrays from string constants
X since the full length of the string constant might not be wanted.
X It is also needed for initialization of unions, where the initializer's
X type is just one member, and that may not be as long as the union.
X
X There a case in which we would fail to output exactly SIZE bytes:
X for a structure constructor that wants to produce more than SIZE bytes.
X But such constructors will never be generated for any possible input. */
X
Xvoid
Xoutput_constant (exp, size)
X register tree exp;
X register int size;
X{
X register enum tree_code code = TREE_CODE (TREE_TYPE (exp));
X rtx x;
X
X if (size == 0)
X return;
X
X /* Eliminate the NOP_EXPR that makes a cast not be an lvalue.
X That way we get the constant (we hope) inside it. */
X if (TREE_CODE (exp) == NOP_EXPR
X && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0)))
X exp = TREE_OPERAND (exp, 0);
X
X switch (code)
X {
X case INTEGER_TYPE:
X case ENUMERAL_TYPE:
X case POINTER_TYPE:
X case REFERENCE_TYPE:
X while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR)
X exp = TREE_OPERAND (exp, 0);
X
X#ifndef ASM_OUTPUT_DOUBLE_INT
X if (TYPE_MODE (TREE_TYPE (exp)) == DImode)
X {
X if (TREE_CODE (exp) == INTEGER_CST)
X {
X#ifndef WORDS_BIG_ENDIAN
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode,
X TREE_INT_CST_LOW (exp)));
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode,
X TREE_INT_CST_HIGH (exp)));
X#else
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode,
X TREE_INT_CST_HIGH (exp)));
X ASM_OUTPUT_INT (asm_out_file,
X gen_rtx (CONST_INT, VOIDmode,
X TREE_INT_CST_LOW (exp)));
X#endif
X size -= GET_MODE_SIZE (DImode);
X break;
X }
X else
X error ("8-byte integer constant expression too complicated");
X
X break;
X }
X#endif /* no ASM_OUTPUT_DOUBLE_INT */
X
X x = expand_expr (exp, 0, VOIDmode, EXPAND_SUM);
X
X if (size == GET_MODE_SIZE (QImode))
X {
X ASM_OUTPUT_CHAR (asm_out_file, x);
X size -= GET_MODE_SIZE (QImode);
X }
X else if (size == GET_MODE_SIZE (HImode))
X {
X ASM_OUTPUT_SHORT (asm_out_file, x);
X size -= GET_MODE_SIZE (HImode);
X }
X else if (size == GET_MODE_SIZE (SImode))
X {
X ASM_OUTPUT_INT (asm_out_file, x);
X size -= GET_MODE_SIZE (SImode);
X }
X#ifdef ASM_OUTPUT_DOUBLE_INT
X else if (size == GET_MODE_SIZE (DImode))
X {
X ASM_OUTPUT_DOUBLE_INT (asm_out_file, x);
X size -= GET_MODE_SIZE (DImode);
X }
X#endif /* ASM_OUTPUT_DOUBLE_INT */
X else
X abort ();
X
X break;
X
X case REAL_TYPE:
X if (TREE_CODE (exp) != REAL_CST)
X error ("initializer for floating value is not a floating constant");
X else
X {
X REAL_VALUE_TYPE d;
X jmp_buf output_constant_handler;
X
X d = TREE_REAL_CST (exp);
X if (setjmp (output_constant_handler))
X {
X error ("floating point trap outputting a constant");
X#ifdef REAL_IS_NOT_DOUBLE
X bzero (&d, sizeof d);
X d = REAL_VALUE_ATOF ("0");
X#else
X d = 0;
X#endif
X }
X set_float_handler (output_constant_handler);
X
X if (size < GET_MODE_SIZE (SFmode))
X break;
X else if (size < GET_MODE_SIZE (DFmode))
X {
X ASM_OUTPUT_FLOAT (asm_out_file, d);
X size -= GET_MODE_SIZE (SFmode);
X }
X else
X {

X ASM_OUTPUT_DOUBLE (asm_out_file, d);
X size -= GET_MODE_SIZE (DFmode);
X }
X set_float_handler (0);
X }
X break;
X
X case COMPLEX_TYPE:
X output_constant (TREE_REALPART (exp), size / 2);
X output_constant (TREE_IMAGPART (exp), size / 2);
X size -= (size / 2) * 2;
X break;
X
X case ARRAY_TYPE:
X if (TREE_CODE (exp) == CONSTRUCTOR)
X {
X output_constructor (exp, size);
X return;
X }
X else if (TREE_CODE (exp) == STRING_CST)
X {
X int excess = 0;
X
X if (size > TREE_STRING_LENGTH (exp))
X {
X excess = size - TREE_STRING_LENGTH (exp);
X size = TREE_STRING_LENGTH (exp);
X }
X
X assemble_string (TREE_STRING_POINTER (exp), size);
X size = excess;
X }
X else
X abort ();
X break;
X
X case RECORD_TYPE:
X case UNION_TYPE:
X if (TREE_CODE (exp) == CONSTRUCTOR)
X output_constructor (exp, size);
X else
X abort ();
X return;
X }
X
X if (size > 0)
X ASM_OUTPUT_SKIP (asm_out_file, size);
X}
X
X/* Subroutine of output_constant, used for CONSTRUCTORs
X (aggregate constants).
X Generate at least SIZE bytes, padding if necessary. */
X
Xvoid
Xoutput_constructor (exp, size)
X tree exp;
X int size;
X{
X register tree link, field = 0;
X /* Number of bytes output or skipped so far.
X In other words, current position within the constructor. */
X int total_bytes = 0;
X /* Non-zero means BYTE contains part of a byte, to be output. */
X int byte_buffer_in_use = 0;
X register int byte;
X
X if (HOST_BITS_PER_INT < BITS_PER_UNIT)
X abort ();
X
X if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE)
X field = TYPE_FIELDS (TREE_TYPE (exp));
X
X /* As LINK goes through the elements of the constant,
X FIELD goes through the structure fields, if the constant is a structure.
X But the constant could also be an array. Then FIELD is zero. */
X for (link = CONSTRUCTOR_ELTS (exp);
X link;
X link = TREE_CHAIN (link),
X field = field ? TREE_CHAIN (field) : 0)
X {
X tree val = TREE_VALUE (link);
X
X /* Eliminate the NOP_EXPR that makes a cast not be an lvalue. */
X if (TREE_CODE (val) == NOP_EXPR
X && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0)))
X val = TREE_OPERAND (val, 0);
X
X if (field == 0
X || (DECL_MODE (field) != BImode))
X {
X register int fieldsize;
X
X /* An element that is not a bit-field.
X Output any buffered-up bit-fields preceding it. */
X if (byte_buffer_in_use)
X {
X ASM_OUTPUT_BYTE (asm_out_file, byte);
X total_bytes++;
X byte_buffer_in_use = 0;
X }
X
X /* Advance to offset of this element.
X Note no alignment needed in an array, since that is guaranteed
X if each element has the proper size. */
X if (field != 0 && DECL_OFFSET (field) / BITS_PER_UNIT != total_bytes)
X {
X ASM_OUTPUT_SKIP (asm_out_file,

X (DECL_OFFSET (field) / BITS_PER_UNIT
X - total_bytes));
X total_bytes = DECL_OFFSET (field) / BITS_PER_UNIT;
X }
X
X /* Determine size this element should occupy. */
X if (field)
X {
X if (! TREE_LITERAL (DECL_SIZE (field)))
X abort ();
X fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field))
X * DECL_SIZE_UNIT (field);
X fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
X }
X else
X fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp)));
X
X /* Output the element's initial value. */
X output_constant (val, fieldsize);
X
X /* Count its size. */
X total_bytes += fieldsize;
X }
X else if (TREE_CODE (val) != INTEGER_CST)
X error ("invalid initial value for member `%s'",
X IDENTIFIER_POINTER (DECL_NAME (field)));
X else
X {
X /* Element that is a bit-field. */
X
X int next_offset = DECL_OFFSET (field);
X int end_offset
X = (next_offset
X + (TREE_INT_CST_LOW (DECL_SIZE (field))
X * DECL_SIZE_UNIT (field)));
X
X /* If this field does not start in this (or, next) byte,
X skip some bytes. */
X if (next_offset / BITS_PER_UNIT != total_bytes)
X {
X /* Output remnant of any bit field in previous bytes. */
X if (byte_buffer_in_use)
X {
X ASM_OUTPUT_BYTE (asm_out_file, byte);
X total_bytes++;
X byte_buffer_in_use = 0;
X }
X
X /* If still not at proper byte, advance to there. */
X if (next_offset / BITS_PER_UNIT != total_bytes)
X {
X ASM_OUTPUT_SKIP (asm_out_file,
X next_offset / BITS_PER_UNIT - total_bytes);
X total_bytes = next_offset / BITS_PER_UNIT;
X }
X }
X
X if (! byte_buffer_in_use)
X byte = 0;
X
X /* We must split the element into pieces that fall within
X separate bytes, and combine each byte with previous or
X following bit-fields. */
X
X /* next_offset is the offset n fbits from the begining of
X the structure to the next bit of this element to be processed.
X end_offset is the offset of the first bit past the end of
X this element. */
X while (next_offset < end_offset)
X {
X int this_time;
X int next_byte = next_offset / BITS_PER_UNIT;
X int next_bit = next_offset % BITS_PER_UNIT;
X
X /* Advance from byte to byte
X within this element when necessary. */
X while (next_byte != total_bytes)
X {
X ASM_OUTPUT_BYTE (asm_out_file, byte);
X total_bytes++;
X byte = 0;
X }
X
X /* Number of bits we can process at once
X (all part of the same byte). */
X this_time = MIN (end_offset - next_offset,
X BITS_PER_UNIT - next_bit);
X#ifdef BYTES_BIG_ENDIAN
X /* On big-endian machine, take the most significant bits
X first (of the bits that are significant)
X and put them into bytes from the most significant end. */
X byte |= (((TREE_INT_CST_LOW (val)
X >> (end_offset - next_offset - this_time))
X & ((1 << this_time) - 1))
X << (BITS_PER_UNIT - this_time - next_bit));
X#else
X /* On little-endian machines,
X take first the least significant bits of the value
X and pack them starting at the least significant
X bits of the bytes. */
X byte |= ((TREE_INT_CST_LOW (val)
X >> (next_offset - DECL_OFFSET (field)))
X & ((1 << this_time) - 1)) << next_bit;
X#endif
X next_offset += this_time;
X byte_buffer_in_use = 1;
X }
X }
X }
X if (byte_buffer_in_use)
X {
X ASM_OUTPUT_BYTE (asm_out_file, byte);
X total_bytes++;
X }
X if (total_bytes < size)
X ASM_OUTPUT_SKIP (asm_out_file, size - total_bytes);
X}
!EOF
echo "Extracting cplus-decl2.c..."
sed 's/^X//' >cplus-decl2.c << '!EOF'
X/* Process declarations and variables for C compiler.
X Copyright (C) 1988 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* Process declarations and symbol lookup for C front end.
X Also constructs types; the standard scalar types at initialization,
X and structure, union, array and enum types when they are declared. */
X
X/* ??? not all decl nodes are given the most useful possible
X line numbers. For example, the CONST_DECLs for enum values. */
X
X#include "config.h"
X#include "tree.h"
X#include "flags.h"
X#include "cplus-tree.h"
X#include "cplus-parse.h"
X#include "cplus-decl.h"
X#include "assert.h"
X
X#define NULL 0
X
Xextern tree grokdeclarator ();
Xstatic void grok_function_init ();
X
X/* A list of virtual function tables we must make sure to write out. */
Xtree pending_vtables;
X
X/* A list of static class variables. This is needed, because a
X static class variable can be declared inside the class without
X an initializer, and then initialized, staticly, outside the class. */
Xtree pending_statics;
X
Xextern tree pending_addressable_inlines;
X
X/* Used to help generate temporary names which are unique within
X a function. Reset to 0 by start_function. */
X
Xstatic int temp_name_counter;
X
X/* Same, but not reset. Local temp variables and global temp variables
X can have the same name. */
Xstatic int global_temp_name_counter;
X
X/* The (assembler) name of the first globally-visible object output. */
Xextern char * first_global_object_name;
X
X/* C (and C++) language-specific option variables. */
X
X/* Nonzero means allow type mismatches in conditional expressions;
X just make their values `void'. */
X
Xint flag_cond_mismatch;
X
X/* Nonzero means don't recognize the keyword `asm'. */
X
Xint flag_no_asm;
X
X/* Nonzero means do some things the same way PCC does. */
X
Xint flag_traditional;
X
X/* Nonzero means warn about implicit declarations. */
X
Xint warn_implicit = 1;
X
X/* Nonzero means warn about function definitions that default the return type
X or that use a null return and have a return-type other than void. */
X
Xint warn_return_type;
X
X/* Nonzero means give string constants the type `const char *'
X to get extra warnings from them. These warnings will be too numerous
X to be useful, except in thoroughly ANSIfied programs. */
X
Xint warn_write_strings;
X
X/* Nonzero means warn about pointer casts that can drop a type qualifier
X from the pointer target type. */
X
Xint warn_cast_qual;
X
X/* Nonzero means warn about sizeof(function) or addition/subtraction
X of function pointers. */
X
Xint warn_pointer_arith;
X
X/* Nonzero means warn for all old-style non-prototype function decls. */
X
Xint warn_strict_prototypes;
X
X/* Non-zero means warn in function declared in derived class has the
X same name as a virtual in the base class, but fails to match the
X type signature of any virtual function in the base class. */
Xint warn_overloaded_virtual;
X
X/* Non-zero means warn when converting between different enumeral types. */
Xint warn_enum_clash;
X
X/* Nonzero means `$' can be in an identifier.
X See cccp.c for reasons why this breaks some obscure ANSI C programs. */
X
X#ifndef DOLLARS_IN_IDENTIFIERS
X#define DOLLARS_IN_IDENTIFIERS 0
X#endif
Xint dollars_in_ident = DOLLARS_IN_IDENTIFIERS;
X
X/* Nonzero for -no-strict-prototype switch: do not consider empty
X argument prototype to mean function takes no arguments. */
X
X/* Nonzero causes a call to __main() to be emmitted as first instruction of */
X/* main() */
X
Xint flag__main = 1;
X
Xint strict_prototype = 1;
Xint strict_prototypes_lang_c, strict_prototypes_lang_cplusplus = 1;
X
X/* Nonzero means that labels can be used as first-class objects */
X
Xint flag_labels_ok;
X
X/* Non-zero means to collect statistics which might be expensive
X and to print them when we are done. */
Xint flag_detailed_statistics;
X
X/* C++ specific flags. */
X/* Nonzero for -fall-virtual: make every member function (except
X constructors) lay down in the virtual function table. Calls
X can then either go through the virtual function table or not,
X depending. */
X
Xint flag_all_virtual;
X
X/* Zero means that `this' is a *const. This gives nice behavior in the
X 2.0 world. 1 gives 1.2-compatible behavior. 2 gives Spring behavior. */
X
Xint flag_this_is_variable;
X
X/* Nonzero means memoize our member lookups. */
X
Xint flag_memoize_lookups; int flag_save_memoized_contexts;
X
X/* Nonzero means to implement `dynamic' features a la SOS. */
X
Xint flag_dynamic;
X
X/* 3 means write out only virtuals function tables `defined'
X in this implementation file.
X 2 means write out only specific virtual function tables
X and give them (C) public visibility.
X 1 means write out virtual function tables and give them
X (C) public visibility.
X 0 means write out virtual function tables and give them
X (C) static visibility (default).
X -1 means declare virtual function tables extern. */
X
Xint write_virtuals;
X
X/* Nonzero means we should attempt to elide constructors when possible. */
X
Xint flag_elide_constructors;
X
X/* Nonzero means if the type has methods, only output debugging
X information if methods are actually written to the asm file. */
X
Xint flag_minimal_debug = 0;
X
X/* Same, but for inline functions: nonzero means write out debug info
X for inlines. Zero means do not. */
X
Xint flag_inline_debug;
X
X/* Nonzero means recognize and handle exception handling constructs.
X 2 means handle exceptions the way Spring wants them handled. */
X
Xint flag_handle_exceptions;
X
X/* Nonzero means that member functions defined in class scope are
X inline by default. */
X
Xint flag_default_inline = 1;
X
X/* Nonzero means that functions declared `inline' will be treated
X as `static'. Used in conjunction with -g. */
Xint flag_no_inline = 0;
X
X/* Controls whether enums and ints freely convert.
X 1 means with complete freedom.
X 0 means enums can convert to ints, but not vice-versa. */
Xint flag_int_enum_equivalence;
X
X/* Table of language-dependent -f options.
X STRING is the option name. VARIABLE is the address of the variable.
X ON_VALUE is the value to store in VARIABLE
X if `-fSTRING' is seen as an option.
X (If `-fno-STRING' is seen as an option, the opposite value is stored.) */
X
Xstatic struct { char *string; int *variable; int on_value;} lang_f_options[] =
X{
X {"signed-char", &flag_signed_char, 1},
X {"unsigned-char", &flag_signed_char, 0},
X {"short-enums", &flag_short_enums, 1},
X {"cond-mismatch", &flag_cond_mismatch, 1},
X {"asm", &flag_no_asm, 0},
X {"labels-ok", &flag_labels_ok, 1},
X {"stats", &flag_detailed_statistics, 1},
X {"this-is-variable", &flag_this_is_variable, 1},
X {"strict-prototype", &strict_prototypes_lang_cplusplus, 1},
X {"warn-implicit", &warn_implicit, 1},
X {"all-virtual", &flag_all_virtual, 1},
X {"memoize-lookups", &flag_memoize_lookups, 1},
X {"elide-constructors", &flag_elide_constructors, 1},
X {"minimal-debug", &flag_minimal_debug, 1},
X {"inline-debug", &flag_inline_debug, 0},
X {"handle-exceptions", &flag_handle_exceptions, 1},
X {"spring-exceptions", &flag_handle_exceptions, 2},
X {"default-inline", &flag_default_inline, 1},
X {"inline", &flag_no_inline, 0},
X {"dollars-in-identifiers", &dollars_in_ident, 1},
X {"enum-int-equiv", &flag_int_enum_equivalence, 1},
X {"__main", &flag__main, 1},
X};
X
X/* Decode the string P as a language-specific option.
X Return 1 if it is recognized (and handle it);
X return 0 if not recognized. */
X
Xint
Xlang_decode_option (p)
X char *p;
X{
X if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional"))
X flag_traditional = 1, dollars_in_ident = 1, flag_writable_strings = 1,
X flag_this_is_variable = 1;
X /* The +e options are for cfront compatability. */
X else if (p[0] == '+' && p[1] == 'e')
X {
X int old_write_virtuals = write_virtuals;
X if (p[2] == '1')
X write_virtuals = 1;
X else if (p[2] == '0')
X write_virtuals = -1;
X else if (p[2] == '2')
X write_virtuals = 2;
X else error ("invalid +e option");
X if (old_write_virtuals != 0
X && write_virtuals != old_write_virtuals)
X error ("conflicting +e options given");
X }
X else if (p[0] == '-' && p[1] == 'f')
X {
X /* Some kind of -f option.
X P's value is the option sans `-f'.
X Search for it in the table of options. */
X int found = 0, j;
X
X p += 2;
X /* Try special -f options. */
X
X if (!strcmp (p, "save-memoized"))
X {
X flag_memoize_lookups = 1;
X flag_save_memoized_contexts = 1;
X found = 1;
X }
X else if (!strcmp (p, "SOS"))
X {
X flag_all_virtual = 2;
X found = 1;
X }
X else for (j = 0;
X !found && j < sizeof (lang_f_options) / sizeof (lang_f_options[0]);
X j++)
X {
X if (!strcmp (p, lang_f_options[j].string))
X {
X *lang_f_options[j].variable = lang_f_options[j].on_value;
X /* A goto here would be cleaner,
X but breaks the vax pcc. */
X found = 1;
X }
X if (p[0] == 'n' && p[1] == 'o' && p[2] == '-'
X && ! strcmp (p+3, lang_f_options[j].string))
X {
X *lang_f_options[j].variable = ! lang_f_options[j].on_value;
X found = 1;
X }
X }
X return found;
X }
X else if (p[0] == '-' && p[1] == 'W')
X {
X /* The -W options control the warning behavior of the compiler. */
X p += 2;
X
X if (!strcmp (p, "implicit"))
X warn_implicit = 1;
X else if (!strcmp (p, "return-type"))
X warn_return_type = 1;
X else if (!strcmp (p, "write-strings"))
X warn_write_strings = 1;
X else if (!strcmp (p, "cast-qual"))
X warn_cast_qual = 1;
X else if (!strcmp (p, "pointer-arith"))
X warn_pointer_arith = 1;
X else if (!strcmp (p, "strict-prototypes"))
X warn_strict_prototypes = 1;
X else if (!strcmp (p, "comment"))
X ; /* cpp handles this one. */
X else if (!strcmp (p, "comments"))
X ; /* cpp handles this one. */
X else if (!strcmp (p, "trigraphs"))
X ; /* cpp handles this one. */
X else if (!strcmp (p, "all"))
X {
X extra_warnings = 1;
X warn_implicit = 1;
X warn_return_type = 1;
X warn_unused = 1;
X warn_switch = 1;
X#if 0
X warn_enum_clash = 1;
X#endif
X warn_inline = 1;
X }
X
X else if (!strcmp (p, "overloaded-virtual"))
X warn_overloaded_virtual = 1;
X else if (!strcmp (p, "enum-clash"))
X warn_enum_clash = 1;
X else return 0;
X }
X else if (!strcmp (p, "-ansi"))
X flag_no_asm = 1, dollars_in_ident = 0;
X else
X return 0;
X
X return 1;
X}
X
X/* Incorporate `const' and `volatile' qualifiers for member functions.
X FUNCTION is a TYPE_DECL or a FUNCTION_DECL.
X QUALS is a list of qualifiers. */
Xtree
Xgrok_method_quals (ctype, function, quals)
X tree ctype, function, quals;
X{
X tree fntype = TREE_TYPE (function);
X tree raises = TYPE_RAISES_EXCEPTIONS (fntype);
X
X assert (quals != NULL_TREE);
X do
X {
X extern tree ridpointers[];
X
X if (TREE_VALUE (quals) == ridpointers[(int)RID_CONST])
X {
X if (TREE_READONLY (ctype))
X error ("duplicate `%s' %s",
X IDENTIFIER_POINTER (TREE_VALUE (quals)),
X (TREE_CODE (function) == FUNCTION_DECL
X ? "for member function" : "in type declaration"));
X ctype = build_type_variant (ctype, 1, TREE_VOLATILE (ctype));
X build_pointer_type (ctype);
X }
X else if (TREE_VALUE (quals) == ridpointers[(int)RID_VOLATILE])
X {
X if (TREE_VOLATILE (ctype))
X error ("duplicate `%s' %s",
X IDENTIFIER_POINTER (TREE_VALUE (quals)),
X (TREE_CODE (function) == FUNCTION_DECL
X ? "for member function" : "in type declaration"));
X ctype = build_type_variant (ctype, TREE_READONLY (ctype), 1);
X build_pointer_type (ctype);
X }
X else
X abort ();
X quals = TREE_CHAIN (quals);
X }
X while (quals);
X fntype = build_cplus_method_type (ctype, TREE_TYPE (fntype),
X (TREE_CODE (fntype) == METHOD_TYPE
X ? TREE_CHAIN (TYPE_ARG_TYPES (fntype))
X : TYPE_ARG_TYPES (fntype)));
X if (raises)
X fntype = build_exception_variant (ctype, fntype, raises);
X
X TREE_TYPE (function) = fntype;
X return ctype;
X}
X
X
X/* Classes overload their constituent function names automatically.
X When a function name is declared in a record structure,
X its name is changed to it overloaded name. Since names for
X constructors and destructors can conflict, we place a leading
X '$' for destructors.
X
X CNAME is the name of the class we are grokking for.
X
X FUNCTION is a FUNCTION_DECL. It was created by `grokdeclarator'.
X
X FLAGS contains bits saying what's special about today's
X arguments. 1 == DESTRUCTOR. 2 == OPERATOR.
X
X If FUNCTION is a destructor, then we must add the `auto-delete' field
X as a second parameter. There is some hair associated with the fact
X that we must "declare" this variable in the manner consistent with the
X way the rest of the arguements were declared.
X
X If FUNCTION is a constructor, and we are doing SOS hacks for dynamic
X classes, then the second hidden argument is the virtual function table
X pointer with which to initialize the object.
X
X QUALS are the qualifiers for the this pointer. */
X
Xvoid
Xgrokclassfn (ctype, cname, function, flags, complain, quals)
X tree ctype, cname, function;
X enum overload_flags flags;
X tree quals;
X{
X tree fn_name = DECL_NAME (function);
X tree arg_types;
X tree parm;
X char *name;
X
X if (fn_name == NULL_TREE)
X {
X error ("name missing for member function");
X fn_name = get_identifier ("");
X DECL_NAME (function) = DECL_ORIGINAL_NAME (function) = fn_name;
X }
X
X if (quals)
X ctype = grok_method_quals (ctype, function, quals);
X
X arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
X if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
X {
X /* Must add the class instance variable up front. */
X /* Right now we just make this a pointer. But later
X we may wish to make it special. */
X tree type = TREE_VALUE (arg_types);
X
X if (flags == DTOR_FLAG)
X type = TYPE_MAIN_VARIANT (type);
X else if (DECL_CONSTRUCTOR_P (function))
X {
X if (TYPE_DYNAMIC (ctype))
X {
X parm = build_decl (PARM_DECL, get_identifier (AUTO_VTABLE_NAME), TYPE_POINTER_TO (ptr_type_node));
X TREE_USED (parm) = 1;
X TREE_READONLY (parm) = 1;
X DECL_ARG_TYPE (parm) = TYPE_POINTER_TO (ptr_type_node);
X TREE_CHAIN (parm) = last_function_parms;
X last_function_parms = parm;
X }
X
X if (TYPE_USES_VIRTUAL_BASECLASSES (ctype))
X {
X DECL_CONSTRUCTOR_FOR_VBASE_P (function) = 1;
X /* In this case we need "in-charge" flag saying whether
X this constructor is responsible for initialization
X of virtual baseclasses or not. */
X parm = build_decl (PARM_DECL, in_charge_identifier, integer_type_node);
X DECL_ARG_TYPE (parm) = integer_type_node;
X TREE_REGDECL (parm) = 1;
X TREE_CHAIN (parm) = last_function_parms;
X last_function_parms = parm;
X }
X }
X
X parm = build_decl (PARM_DECL, this_identifier, type);
X DECL_ARG_TYPE (parm) = type;
X /* We can make this a register, so long as we don't
X accidently complain if someone tries to take its address. */
X TREE_REGDECL (parm) = 1;
X if (flags != DTOR_FLAG
X && (!flag_this_is_variable || TREE_READONLY (type)))
X TREE_READONLY (parm) = 1;
X TREE_CHAIN (parm) = last_function_parms;
X last_function_parms = parm;
X }
X
X if (flags == DTOR_FLAG)
X {
X tree const_integer_type = build_type_variant (integer_type_node, 1, 0);
X arg_types = hash_tree_chain (const_integer_type, void_list_node);
X name = (char *)alloca (sizeof (DESTRUCTOR_DECL_FORMAT)
X + IDENTIFIER_LENGTH (cname) + 2);
X sprintf (name, DESTRUCTOR_DECL_FORMAT, IDENTIFIER_POINTER (cname));
X DECL_NAME (function) = get_identifier (name);
X DECL_ASSEMBLER_NAME (function) = IDENTIFIER_POINTER (DECL_NAME (function));
X parm = build_decl (PARM_DECL, in_charge_identifier, const_integer_type);
X TREE_USED (parm) = 1;
X TREE_READONLY (parm) = 1;
X DECL_ARG_TYPE (parm) = const_integer_type;
X /* This is the same chain as DECL_ARGUMENTS (fndecl). */
X TREE_CHAIN (last_function_parms) = parm;
X
X TREE_TYPE (function) = build_cplus_method_type (ctype, void_type_node, arg_types);
X TYPE_HAS_DESTRUCTOR (ctype) = 1;
X }
X else if (flags == WRAPPER_FLAG || flags == ANTI_WRAPPER_FLAG)
X {
X name = (char *)alloca (sizeof (WRAPPER_DECL_FORMAT)
X + sizeof (ANTI_WRAPPER_DECL_FORMAT)
X + IDENTIFIER_LENGTH (cname) + 2);
X sprintf (name,
X flags == WRAPPER_FLAG ? WRAPPER_DECL_FORMAT : ANTI_WRAPPER_DECL_FORMAT,
X IDENTIFIER_POINTER (cname));
X DECL_NAME (function) = build_decl_overload (name, arg_types, 1);
X DECL_ASSEMBLER_NAME (function) = IDENTIFIER_POINTER (DECL_NAME (function));
X sprintf (name, flags == WRAPPER_FLAG ? WRAPPER_NAME_FORMAT : ANTI_WRAPPER_NAME_FORMAT,
X IDENTIFIER_POINTER (cname));
X DECL_ORIGINAL_NAME (function) = fn_name = get_identifier (name);
X }
X else if (flags == WRAPPER_PRED_FLAG)
X {
X name = (char *)alloca (sizeof (WRAPPER_PRED_DECL_FORMAT)
X + sizeof (WRAPPER_PRED_NAME_FORMAT)
X + IDENTIFIER_LENGTH (cname) + 2);
X sprintf (name, WRAPPER_PRED_DECL_FORMAT, IDENTIFIER_POINTER (cname));
X DECL_NAME (function) = build_decl_overload (name, arg_types, 1);
X DECL_ASSEMBLER_NAME (function) = IDENTIFIER_POINTER (DECL_NAME (function));
X sprintf (name, WRAPPER_PRED_NAME_FORMAT, IDENTIFIER_POINTER (cname));
X DECL_ORIGINAL_NAME (function) = fn_name = get_identifier (name);
X }
X else
X {
X tree these_arg_types;
X
X if (TYPE_DYNAMIC (ctype) && DECL_CONSTRUCTOR_P (function))
X {
X arg_types = hash_tree_chain (build_pointer_type (ptr_type_node),
X TREE_CHAIN (arg_types));
X TREE_TYPE (function)
X = build_cplus_method_type (ctype, TREE_TYPE (TREE_TYPE (function)), arg_types);
X arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
X }
X
X if (DECL_CONSTRUCTOR_FOR_VBASE_P (function))
X {
X arg_types = hash_tree_chain (integer_type_node, TREE_CHAIN (arg_types));
X TREE_TYPE (function)
X = build_cplus_method_type (ctype, TREE_TYPE (TREE_TYPE (function)), arg_types);
X arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
X }
X
X these_arg_types = arg_types;
X
X if (TREE_CODE (TREE_TYPE (function)) == FUNCTION_TYPE)
X /* Only true for static member functions. */
X these_arg_types = hash_tree_chain (TYPE_POINTER_TO (ctype), arg_types);
X
X DECL_NAME (function) = build_decl_overload (IDENTIFIER_POINTER (fn_name),
X these_arg_types,
X 1 + DECL_CONSTRUCTOR_P (function));
X DECL_ASSEMBLER_NAME (function) = IDENTIFIER_POINTER (DECL_NAME (function));
X if (flags == TYPENAME_FLAG)
X TREE_TYPE (DECL_NAME (function)) = TREE_TYPE (fn_name);
X }
X
X DECL_ARGUMENTS (function) = last_function_parms;
X
X /* now, the sanity check: report error if this function is not
X really a member of the class it is supposed to belong to. */
X if (complain)
X {
X tree field;
X int need_quotes = 0;
X char *err_name;
X tree method_vec = CLASSTYPE_METHOD_VEC (ctype);
X tree *methods = 0;
X tree *end = 0;
X
X if (method_vec != 0)
X {
X methods = &TREE_VEC_ELT (method_vec, 0);
X end = TREE_VEC_END (method_vec);
X
X if (*methods == 0)
X methods++;
X
X while (methods != end)
X {
X if (fn_name == DECL_ORIGINAL_NAME (*methods))
X {
X field = *methods;
X while (field)
X {
X if (DECL_NAME (function) == DECL_NAME (field))
X return;
X field = TREE_CHAIN (field);
X }
X break; /* loser */
X }
X methods++;
X }
X }
X
X if (OPERATOR_NAME_P (fn_name))
X {
X err_name = (char *)alloca (1024);
X sprintf (err_name, "`operator %s'", operator_name_string (fn_name));
X }
X else if (OPERATOR_TYPENAME_P (fn_name))
X if (complain && TYPE_HAS_CONVERSION (ctype))
X err_name = "such type conversion operator";
X else
X err_name = "type conversion operator";
X else if (flags == WRAPPER_FLAG)
X err_name = "wrapper";
X else if (flags == WRAPPER_PRED_FLAG)
X err_name = "wrapper predicate";
X else
X {
X err_name = IDENTIFIER_POINTER (fn_name);
X need_quotes = 1;
X }
X
X if (methods != end)
X if (need_quotes)
X error ("argument list for `%s' does not match any in class", err_name);
X else
X error ("argument list for %s does not match any in class", err_name);
X else
X {
X methods = 0;
X if (need_quotes)
X error ("no `%s' member function declared in class", err_name);
X else
X error ("no %s declared in class", err_name);
X }
X
X /* If we did not find the method in the class, add it to
X avoid spurious errors. */
X add_method (ctype, methods, function);
X }
X}
X
X/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted)
X of a structure component, returning a FIELD_DECL node.
X QUALS is a list of type qualifiers for this decl (such as for declaring
X const member functions).
X
X This is done during the parsing of the struct declaration.
X The FIELD_DECL nodes are chained together and the lot of them
X are ultimately passed to `build_struct' to make the RECORD_TYPE node.
X
X C++:
X
X If class A defines that certain functions in class B are friends, then
X the way I have set things up, it is B who is interested in permission
X granted by A. However, it is in A's context that these declarations
X are parsed. By returning a void_type_node, class A does not attempt
X to incorporate the declarations of the friends within its structure.
X
X DO NOT MAKE ANY CHANGES TO THIS CODE WITHOUT MAKING CORRESPONDING
X CHANGES TO CODE IN `start_method'. */
X
Xtree
Xgrokfield (declarator, declspecs, raises, init, asmspec_tree)
X tree declarator, declspecs, raises, init;
X tree asmspec_tree;
X{
X register tree value = grokdeclarator (declarator, declspecs, FIELD, init != 0, raises);
X char *asmspec = 0;
X
X if (! value) return NULL_TREE; /* friends went bad. */
X
X /* Pass friendly classes back. */
X if (TREE_CODE (value) == VOID_TYPE)
X return void_type_node;
X
X if (DECL_NAME (value) != NULL_TREE
X && IDENTIFIER_POINTER (DECL_NAME (value))[0] == '_'
X && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (value)), "_vptr"))
X error_with_decl (value, "member `%s' conflicts with virtual function table field name");
X
X /* Stash away type declarations. */
X if (TREE_CODE (value) == TYPE_DECL)
X {
X TREE_NONLOCAL (value) = 1;
X CLASSTYPE_LOCAL_TYPEDECLS (current_class_type) = 1;
X pushdecl_class_level (value);
X return value;
X }
X
X if (DECL_IN_AGGR_P (value))
X {
X error_with_decl (value, "`%s' is already defined in aggregate scope");
X return void_type_node;
X }
X
X if (asmspec_tree)
X asmspec = TREE_STRING_POINTER (asmspec_tree);
X
X if (init != 0)
X {
X if (TREE_CODE (value) == FUNCTION_DECL)
X {
X grok_function_init (value, init);
X init = NULL_TREE;
X }
X else if (pedantic
X && ! TREE_STATIC (value)
X && ! TREE_READONLY (value))
X {
X error ("fields cannot have initializers");
X init = error_mark_node;
X }
X else
X {
X /* We allow initializers to become parameters to base initializers. */
X if (TREE_CODE (init) == CONST_DECL)
X init = DECL_INITIAL (init);
X else if (TREE_READONLY_DECL_P (init))
X init = decl_constant_value (init);
X else if (TREE_CODE (init) == CONSTRUCTOR)
X init = digest_init (TREE_TYPE (value), init, 0);
X assert (TREE_PERMANENT (init));
X if (init == error_mark_node)
X /* We must make this look different than `error_mark_node'
X because `decl_const_value' would mis-interpret it
X as only meaning that this VAR_DECL is defined. */
X init = build1 (NOP_EXPR, TREE_TYPE (value), init);
X else if (! TREE_LITERAL (init))
X {
X /* We can allow references to things that are effectively
X static, since references are initialized with the address. */
X if (TREE_CODE (TREE_TYPE (value)) != REFERENCE_TYPE
X || (TREE_EXTERNAL (init) == 0
X && TREE_STATIC (init) == 0))
X {
X error ("field initializer is not constant");
X init = error_mark_node;
X }
X }
X }
X }
X
X if (TREE_CODE (value) == VAR_DECL)
X {
X /* We cannot call pushdecl here, because that would
X fill in the value of our TREE_CHAIN. Instead, we
X modify finish_decl to do the right thing, namely, to
X put this decl out straight away. */
X if (TREE_STATIC (value))
X {
X if (asmspec == 0)
X {
X char *buf
X = (char *)alloca (IDENTIFIER_LENGTH (current_class_name)
X + IDENTIFIER_LENGTH (DECL_NAME (value))
X + sizeof (STATIC_NAME_FORMAT));
X tree name;
X
X sprintf (buf, STATIC_NAME_FORMAT,
X IDENTIFIER_POINTER (current_class_name),
X IDENTIFIER_POINTER (DECL_NAME (value)));
X name = get_identifier (buf);
X TREE_PUBLIC (value) = 1;
X DECL_INITIAL (value) = error_mark_node;
X asmspec = IDENTIFIER_POINTER (name);
X DECL_ASSEMBLER_NAME (value) = asmspec;
X asmspec_tree = build_string (IDENTIFIER_LENGTH (name), asmspec);
X }
X pending_statics = perm_tree_cons (NULL_TREE, value, pending_statics);
X
X /* Static consts need not be initialized in the class definition. */
X if (init != NULL_TREE && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (value)))
X {
X static int explanation = 0;
X
X error ("initializer invalid for static member with constructor");
X if (explanation++ == 0)
X error ("(you really want to initialize it separately)");
X init = 0;
X }
X /* Force the user to know when an uninitialized static
X member is being used. */
X if (TREE_READONLY (value)
X && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (value)))
X TREE_EXTERNAL (value) = 1;
X }
X DECL_INITIAL (value) = init;
X DECL_IN_AGGR_P (value) = 1;
X finish_decl (value, init, asmspec_tree);
X pushdecl_class_level (value);
X return value;
X }
X if (TREE_CODE (value) == FIELD_DECL)
X {
X DECL_ASSEMBLER_NAME (value) = asmspec;
X if (DECL_INITIAL (value) == error_mark_node)
X init = error_mark_node;
X finish_decl (value, init, asmspec_tree);
X DECL_INITIAL (value) = init;
X DECL_IN_AGGR_P (value) = 1;
X return value;
X }
X if (TREE_CODE (value) == FUNCTION_DECL)
X {
X /* grokdeclarator defers setting this. */
X TREE_PUBLIC (value) = 1;
X if (TREE_CHAIN (value) != NULL_TREE)
X {
X /* Need a fresh node here so that we don't get circularity
X when we link these together. */
X value = copy_node (value);
X /* When does this happen? */
X assert (init == NULL_TREE);
X }
X finish_decl (value, init, asmspec_tree);
X
X /* Pass friends back this way. */
X if (DECL_FRIEND_P (value))
X return void_type_node;
X
X DECL_IN_AGGR_P (value) = 1;
X return value;
X }
X abort ();
X}
X
X/* Like `grokfield', but for bitfields.
X WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node. */
X
Xtree
Xgrokbitfield (declarator, declspecs, width)
X tree declarator, declspecs, width;
X{
X register tree value = grokdeclarator (declarator, declspecs, FIELD, 0, NULL_TREE, NULL_TREE);
X
X if (! value) return NULL_TREE; /* friends went bad. */
X
X /* Pass friendly classes back. */
X if (TREE_CODE (value) == VOID_TYPE)
X return void_type_node;
X
X if (TREE_CODE (value) == TYPE_DECL)
X {
X error_with_decl (value, "cannot declare `%s' to be a bitfield type");
X return NULL_TREE;
X }
X
X if (DECL_IN_AGGR_P (value))
X {
X error_with_decl (value, "`%s' is already defined in aggregate scope");
X return void_type_node;
X }
X
X#ifdef FIELD_XREF
X FIELD_xref_member(current_class_name,value);
X#endif
X
X if (TREE_STATIC (value))
X {
X error_with_decl (value, "static member `%s' cannot be a bitfield");
X return NULL_TREE;
X }
X if (TREE_CODE (value) == FIELD_DECL)
X {
X finish_decl (value, NULL_TREE, NULL_TREE);
X /* detect invalid field size. */
X if (TREE_CODE (width) == CONST_DECL)
X width = DECL_INITIAL (width);
X else if (TREE_READONLY_DECL_P (width))
X width = decl_constant_value (width);
X
X if (TREE_CODE (width) != INTEGER_CST)
X {
X error_with_decl (value, "structure field `%s' width not an integer constant");
X DECL_INITIAL (value) = NULL;
X }
X else
X {
X DECL_INITIAL (value) = width;
X TREE_PACKED (value) = 1;
X }
X DECL_IN_AGGR_P (value) = 1;
X return value;
X }
X abort ();
X}
X
X/* Like GROKFIELD, except that the declarator has been
X buried in DECLSPECS. Find the declarator, and
X return something that looks like it came from
X GROKFIELD. */
Xtree
Xgroktypefield (declspecs, parmlist)
X tree declspecs;
X tree parmlist;
X{
X tree spec = declspecs;
X tree prev = NULL_TREE;
X
X tree type_id = NULL_TREE;
X tree quals = NULL_TREE;
X tree lengths = NULL_TREE;
X tree decl = NULL_TREE;
X
X while (spec)
X {
X register tree id = TREE_VALUE (spec);
X
X if (TREE_CODE (spec) != TREE_LIST)
X /* Certain parse errors slip through. For example,
X `int class ();' is not caught by the parser. Try
X weakly to recover here. */
X return NULL_TREE;
X
X if (TREE_CODE (id) == TYPE_DECL
X || (TREE_CODE (id) == IDENTIFIER_NODE && TREE_TYPE (id)))
X {
X /* We have a constructor/destructor or
X conversion operator. Use it. */
X if (prev)
X TREE_CHAIN (prev) = TREE_CHAIN (spec);
X else
X {
X declspecs = TREE_CHAIN (spec);
X }
X type_id = id;
X goto found;
X }
X prev = spec;
X spec = TREE_CHAIN (spec);
X }
X
X /* Nope, we have a conversion operator to a scalar type. */
X spec = declspecs;
X while (spec)
X {
X tree id = TREE_VALUE (spec);
X
X if (TREE_CODE (id) == IDENTIFIER_NODE)
X {
X if (id == ridpointers[(int)RID_INT]
X || id == ridpointers[(int)RID_DOUBLE]
X || id == ridpointers[(int)RID_FLOAT])
X {
X if (type_id)
X error ("extra `%s' ignored",
X IDENTIFIER_POINTER (id));
X else
X type_id = id;
X }
X else if (id == ridpointers[(int)RID_LONG]
X || id == ridpointers[(int)RID_SHORT]
X || id == ridpointers[(int)RID_CHAR])
X {
X lengths = tree_cons (NULL_TREE, id, lengths);
X }
X else if (id == ridpointers[(int)RID_VOID])
X {
X if (type_id)
X error ("spurious `void' type ignored");
X else
X error ("conversion to `void' type invalid");
X }
X else if (id == ridpointers[(int)RID_AUTO]
X || id == ridpointers[(int)RID_REGISTER]
X || id == ridpointers[(int)RID_TYPEDEF]
X || id == ridpointers[(int)RID_CONST]
X || id == ridpointers[(int)RID_VOLATILE])
X {
X error ("type specifier `%s' used invalidly",
X IDENTIFIER_POINTER (id));
X }
X else if (id == ridpointers[(int)RID_FRIEND]
X || id == ridpointers[(int)RID_VIRTUAL]
X || id == ridpointers[(int)RID_INLINE]
X || id == ridpointers[(int)RID_UNSIGNED]
X || id == ridpointers[(int)RID_SIGNED]
X || id == ridpointers[(int)RID_STATIC]
X || id == ridpointers[(int)RID_EXTERN])
X {
X quals = tree_cons (NULL_TREE, id, quals);
X }
X else
X {
X /* Happens when we have a global typedef
X and a class-local member function with
X the same name. */
X type_id = id;
X goto found;
X }
X }
X else if (TREE_CODE (id) == RECORD_TYPE)
X {
X type_id = TYPE_NAME (id);
X if (TREE_CODE (type_id) == TYPE_DECL)
X type_id = DECL_NAME (type_id);
X if (type_id == NULL_TREE)
X error ("identifier for aggregate type conversion omitted");
X }
X else
X assert (0);
X spec = TREE_CHAIN (spec);
X }
X
X if (type_id)
X {
X declspecs = chainon (lengths, quals);
X }
X else if (lengths)
X {
X if (TREE_CHAIN (lengths))
X error ("multiple length specifiers");
X type_id = ridpointers[(int)RID_INT];
X declspecs = chainon (lengths, quals);
X }
X else if (quals)
X {
X error ("no type given, defaulting to `operator int ...'");
X type_id = ridpointers[(int)RID_INT];
X declspecs = quals;
X }
X else return NULL_TREE;
X found:
X decl = grokdeclarator (build_parse_node (CALL_EXPR, type_id, parmlist, NULL_TREE),
X declspecs, FIELD, 0, NULL_TREE);
X if (decl == NULL_TREE)
X return NULL_TREE;
X
X if (TREE_CODE (decl) == FUNCTION_DECL && TREE_CHAIN (decl) != NULL_TREE)
X {
X /* Need a fresh node here so that we don't get circularity
X when we link these together. */
X decl = copy_node (decl);
X }
X
X if (decl == void_type_node
X || (TREE_CODE (decl) == FUNCTION_DECL
X && TREE_CODE (TREE_TYPE (decl)) != METHOD_TYPE))
X /* bunch of friends. */
X return decl;
X
X if (DECL_IN_AGGR_P (decl))
X {
X error_with_decl (decl, "`%s' already defined in aggregate scope");
X return void_type_node;
X }
X
X finish_decl (decl, NULL_TREE, NULL_TREE);
X
X /* If this declaration is common to another declaration
X complain about such redundancy, and return NULL_TREE
X so that we don't build a circular list. */
X if (TREE_CHAIN (decl))
X {
X error_with_decl (decl, "function `%s' declared twice in aggregate");
X return NULL_TREE;
X }
X DECL_IN_AGGR_P (decl) = 1;
X return decl;
X}
X
X/* The precedence rules of this grammar (or any other deterministic LALR
X grammar, for that matter), place the CALL_EXPR somewhere where we
X may not want it. The solution is to grab the first CALL_EXPR we see,
X pretend that that is the one that belongs to the parameter list of
X the type conversion function, and leave everything else alone.
X We pull it out in place.
X
X CALL_REQUIRED is non-zero if we should complain if a CALL_EXPR
X does not appear in DECL. */
Xtree
Xgrokoptypename (decl, call_required)
X tree decl;
X int call_required;
X{
X tree tmp, last;
X
X assert (TREE_CODE (decl) == TYPE_EXPR);
X
X tmp = TREE_OPERAND (decl, 0);
X last = NULL_TREE;
X
X while (tmp)
X {
X switch (TREE_CODE (tmp))
X {
X case CALL_EXPR:
X {
X tree parms = TREE_OPERAND (tmp, 1);
X
X if (last)
X TREE_OPERAND (last, 0) = TREE_OPERAND (tmp, 0);
X else
X TREE_OPERAND (decl, 0) = TREE_OPERAND (tmp, 0);
X if (parms
X && TREE_CODE (TREE_VALUE (parms)) == TREE_LIST)
X TREE_VALUE (parms)
X = grokdeclarator (TREE_VALUE (TREE_VALUE (parms)),
X TREE_PURPOSE (TREE_VALUE (parms)),
X TYPENAME, 0, NULL_TREE);
X if (parms)
X {
X if (TREE_VALUE (parms) != void_type_node)
X error ("operator requires empty parameter list");
X else
X /* Canonicalize parameter lists. */
X TREE_OPERAND (tmp, 1) = void_list_node;
X }
X
X last = grokdeclarator (TREE_OPERAND (decl, 0),
X TREE_TYPE (decl),
X TYPENAME, 0, NULL_TREE);
X TREE_OPERAND (tmp, 0) = build_typename_overload (last);
X TREE_TYPE (TREE_OPERAND (tmp, 0)) = last;
X return tmp;
X }
X
X case INDIRECT_REF:
X case ADDR_EXPR:
X case ARRAY_REF:
X break;
X
X case SCOPE_REF:
X /* This is legal when declaring a conversion to
X something of type pointer-to-member. */
X if (TREE_CODE (TREE_OPERAND (tmp, 1)) == INDIRECT_REF)
X {
X tmp = TREE_OPERAND (tmp, 1);
X }
X else
X {
X#if 0
X /* We may need to do this if grokdeclarator cannot handle this. */
X error ("type `member of class %s' invalid return type",
X TYPE_NAME_STRING (TREE_OPERAND (tmp, 0)));
X TREE_OPERAND (tmp, 1) = build_parse_node (INDIRECT_REF, TREE_OPERAND (tmp, 1));
X#endif
X tmp = TREE_OPERAND (tmp, 1);
X }
X break;
X
X default:
X assert (0);
X }
X last = tmp;
X tmp = TREE_OPERAND (tmp, 0);
X }
X
X if (call_required)
X error ("operator construct requires parameter list");
X
X last = grokdeclarator (TREE_OPERAND (decl, 0),
X TREE_TYPE (decl),
X TYPENAME, 0, NULL_TREE);
X tmp = build_parse_node (CALL_EXPR, build_typename_overload (last),
X void_list_node, NULL_TREE);
X TREE_TYPE (TREE_OPERAND (tmp, 0)) = last;
X return tmp;
X}
X
X/* Given an encoding for an operator name (see parse.y, the rules
X for making an `operator_name' in the variable DECLARATOR,
X return the name of the operator prefix as an IDENTIFIER_NODE.
X
X CTYPE is the class type to which this operator belongs.
X Needed in case it is a static member function.
X
X TYPE, if nonnull, is the function type for this declarator.
X This information helps to resolve potential ambiguities.
X
X If REPORT_AMBIGUOUS is non-zero, an error message
X is reported, and a default arity of the operator is
X returned. Otherwise, return the operator under an OP_EXPR,
X for later evaluation when type information will enable
X proper instantiation.
X
X IS_DECL is 1 if this is a decl (as opposed to an expression).
X IS_DECL is 2 if this is a static function decl.
X Otherwise IS_DECL is 0. */
Xtree
Xgrokopexpr (declp, ctype, type, report_ambiguous, is_decl)
X tree *declp;
X tree ctype, type;
X int report_ambiguous;
X{
X tree declarator = *declp;
X tree name, parmtypes;
X int seen_classtype_parm
X = (type != NULL_TREE && TREE_CODE (type) == METHOD_TYPE)
X || is_decl == 2;
X
X if (type != NULL_TREE)
X {
X if (ctype == 0 && TREE_CODE (TREE_OPERAND (declarator, 0)) == NEW_EXPR)
X {
X if (TYPE_ARG_TYPES (type)
X && TREE_CHAIN (TYPE_ARG_TYPES (type))
X && TREE_CHAIN (TYPE_ARG_TYPES (type)) != void_list_node)
X return get_identifier (OPERATOR_NEW_FORMAT);
X return get_identifier ("__builtin_new");
X }
X else if (ctype == 0 && TREE_CODE (TREE_OPERAND (declarator, 0)) == DELETE_EXPR)
X return get_identifier ("__builtin_delete");
X else
X {
X /* Now we know the number of parameters,
X so build the real operator fnname. */
X parmtypes = TYPE_ARG_TYPES (type);
X if (is_decl > 1 && TREE_CODE (type) == METHOD_TYPE)
X parmtypes = TREE_CHAIN (parmtypes);
X name = build_operator_fnname (declp, parmtypes, is_decl > 1);
X declarator = *declp;
X }
X }
X else
X {
X if (TREE_PURPOSE (declarator) == NULL_TREE)
X switch (TREE_CODE (TREE_VALUE (declarator)))
X {
X case PLUS_EXPR:
X case CONVERT_EXPR:
X case ADDR_EXPR:
X case BIT_AND_EXPR:
X case INDIRECT_REF:
X case MULT_EXPR:
X case NEGATE_EXPR:
X case MINUS_EXPR:
X if (report_ambiguous)
X {
X error ("operator '%s' ambiguous, (default binary)",
X opname_tab[(int)TREE_CODE (TREE_VALUE (declarator))]);
X name = build_operator_fnname (declp, NULL_TREE, 2);
X declarator = *declp;
X }
X else
X {
X /* do something intellegent. */
X TREE_TYPE (declarator) = unknown_type_node;
X return declarator;
X }
X break;
X default:
X name = build_operator_fnname (declp, NULL_TREE, -1);
X declarator = *declp;
X break;
X }
X else if (TREE_CODE (TREE_PURPOSE (declarator)) == MODIFY_EXPR)
X {
X name = build_operator_fnname (declp, NULL_TREE, -1);
X declarator = *declp;
X }
X else abort ();
X }
X /* Now warn if the parameter list does not contain any args
X which are of aggregate type. */
X if (is_decl && type != NULL_TREE && ! seen_classtype_parm)
X for (parmtypes = TYPE_ARG_TYPES (type);
X parmtypes;
X parmtypes = TREE_CHAIN (parmtypes))
X if (IS_AGGR_TYPE (TREE_VALUE (parmtypes))
X || (TREE_CODE (TREE_VALUE (parmtypes)) == REFERENCE_TYPE
X && IS_AGGR_TYPE (TREE_TYPE (TREE_VALUE (parmtypes)))))
X {
X seen_classtype_parm = 1;
X break;
X }
X
X if (is_decl && seen_classtype_parm == 0)
X if (TREE_CODE (declarator) == OP_IDENTIFIER
X && (TREE_CODE (TREE_OPERAND (declarator, 0)) == NEW_EXPR
X || TREE_CODE (TREE_OPERAND (declarator, 0)) == DELETE_EXPR))
X /* Global operators new and delete are not overloaded. */
X TREE_OVERLOADED (name) = 0;
X else
X error ("operator has no %suser-defined argument type",
X type == NULL_TREE ? "(default) " : "");
X
X return name;
X}
X
X/* When a function is declared with an initialializer,
X do the right thing. Currently, there are two possibilities:
X
X class B
X {
X public:
X // initialization possibility #1.
X virtual void f () = 0;
X int g ();
X };
X
X class D1 : B
X {
X public:
X int d1;
X // error, no f ();
X };
X
X class D2 : B
X {
X public:
X int d2;
X void f ();
X };
X
X class D3 : B
X {
X public:
X int d3;
X // initialization possibility #2
X void f () = B::f;
X };
X
X*/
X
Xstatic void
Xgrok_function_init (decl, init)
X tree decl;
X tree init;
X{
X /* An initializer for a function tells how this function should
X be inherited. */
X tree type = TREE_TYPE (decl);
X extern tree abort_fndecl;
X
X if (TREE_CODE (type) == FUNCTION_TYPE)
X error_with_decl (decl, "initializer specified for non-member function `%s'");
X else if (! DECL_VIRTUAL_P (decl))
X error_with_decl (decl, "initializer specified for non-virtual method `%s'");
X else if (integer_zerop (init))
X {
X /* Mark this function as being "defined". */
X DECL_INITIAL (decl) = error_mark_node;
X /* Give this node rtl from `abort'. */
X DECL_RTL (decl) = DECL_RTL (abort_fndecl);
X DECL_ABSTRACT_VIRTUAL_P (decl) = 1;
X }
X else if (TREE_CODE (init) == OFFSET_REF
X && TREE_OPERAND (init, 0) == NULL_TREE
X && TREE_CODE (TREE_TYPE (init)) == METHOD_TYPE)
X {
X tree basetype = TYPE_METHOD_BASETYPE (TREE_TYPE (init));
X tree basefn = TREE_OPERAND (init, 1);
X if (TREE_CODE (basefn) != FUNCTION_DECL)
X error_with_decl (decl, "non-method initializer invalid for method `%s'");
X else if (DECL_OFFSET (TYPE_NAME (basefn)) != 0)
X sorry ("base member function from other than first base class");
X else
X {
X basetype = get_base_type (basetype, TYPE_METHOD_BASETYPE (type), 1);
X if (basetype == error_mark_node)
X ;
X else if (basetype == 0)
X error_not_base_type (TYPE_METHOD_BASETYPE (TREE_TYPE (init)),
X TYPE_METHOD_BASETYPE (type));
X else
X {
X /* Mark this function as being defined,
X and give it new rtl. */
X DECL_INITIAL (decl) = error_mark_node;
X DECL_RTL (decl) = DECL_RTL (basefn);
X }
X }
X }
X else
X error_with_decl (decl, "invalid initializer for virtual method `%s'");
X}
X
X/* Cache the value of this class's main virtual function table pointer
X in a register variable. This will save one indirection if a
X more than one virtual function call is made this function. */
Xvoid
Xsetup_vtbl_ptr ()
X{
X extern struct rtx_def *base_init_insns;
X
X if (base_init_insns == 0
X && DECL_CONSTRUCTOR_P (current_function_decl))
X emit_base_init (current_class_type, 0);
X
X if ((flag_this_is_variable & 1) == 0
X && optimize
X && current_class_type
X && CLASSTYPE_VSIZE (current_class_type)
X && ! DECL_STATIC_FUNCTION_P (current_function_decl))
X {
X tree vfield = build_vfield_ref (C_C_D, current_class_type);
X current_vtable_decl = CLASSTYPE_VTBL_PTR (current_class_type);
X DECL_RTL (current_vtable_decl) = 0;
X DECL_INITIAL (current_vtable_decl) = error_mark_node;
X /* Have to cast the initializer, since it may have come from a
X more base class then we ascribe CURRENT_VTABLE_DECL to be. */
X finish_decl (current_vtable_decl, convert_force (TREE_TYPE (current_vtable_decl), vfield), 0);
X current_vtable_decl = build_indirect_ref (current_vtable_decl, 0);
X }
X else
X current_vtable_decl = NULL_TREE;
X}
X
X/* Record the existence of an addressable inline function. */
Xvoid
Xmark_inline_for_output (decl)
X tree decl;
X{
X pending_addressable_inlines = perm_tree_cons (NULL_TREE, decl,
X pending_addressable_inlines);
X}
X
Xvoid
Xclear_temp_name ()
X{
X temp_name_counter = 0;
X}
X
X/* Hand off a unique name which can be used for variable we don't really
X want to know about anyway, for example, the anonymous variables which
X are needed to make references work. Declare this thing so we can use it.
X The variable created will be of type TYPE.
X
X STATICP is nonzero if this variable should be static. */
X
Xtree
Xget_temp_name (type, staticp)
X tree type;
X int staticp;
X{
X char buf[sizeof (AUTO_TEMP_FORMAT) + 12];
X tree decl;
X int temp = 0;
X int toplev = global_bindings_p ();
X if (toplev || staticp)
X {
X temp = allocation_temporary_p ();
X if (temp)
X end_temporary_allocation ();
X sprintf (buf, AUTO_TEMP_FORMAT, global_temp_name_counter++);
X decl = pushdecl_top_level (build_decl (VAR_DECL, get_identifier (buf), type));
X }
X else
X {
X sprintf (buf, AUTO_TEMP_FORMAT, temp_name_counter++);
X decl = pushdecl (build_decl (VAR_DECL, get_identifier (buf), type));
X }
X TREE_USED (decl) = 1;
X TREE_STATIC (decl) = staticp;
X
X /* If this is a local variable, then lay out its rtl now.
X Otherwise, callers of this function are responsible for dealing
X with this variable's rtl. */
X if (! toplev)
X {
X expand_decl (decl);
X expand_decl_init (decl);
X }
X else if (temp)
X resume_temporary_allocation ();
X
X return decl;
X}
X
X/* Get a variable which we can use for multiple assignments.
X It is not entered into current_binding_level, because
X that breaks things when it comes time to do final cleanups
X (which take place "outside" the binding contour of the function).
X
X Because it is not entered into the binding contour, `expand_end_bindings'
X does not see this variable automatically. Users of this function
X must either pass this variable to expand_end_bindings or do
X themselves what expand_end_bindings was meant to do (like keeping
X the variable live if -noreg was specified). */
Xtree
Xget_temp_regvar (type, init)
X tree type, init;
X{
X static char buf[sizeof (AUTO_TEMP_FORMAT) + 8] = { '_' };
X tree decl;
X
X sprintf (buf+1, AUTO_TEMP_FORMAT, temp_name_counter++);
X decl = pushdecl (build_decl (VAR_DECL, get_identifier (buf), type));
X TREE_USED (decl) = 1;
X TREE_REGDECL (decl) = 1;
X
X if (init)
X store_init_value (decl, init);
X
X /* We can expand these without fear, since they cannot need
X constructors or destructors. */
X expand_decl (decl);
X expand_decl_init (decl);
X return decl;
X}
X
X/* Make the macro TEMP_NAME_P available to units which do not
X include c-tree.h. */
Xint
Xtemp_name_p (decl)
X tree decl;
X{
X return TEMP_NAME_P (decl);
X}
X
X/* Finish off the processing of a UNION_TYPE structure.
X If there are static members, then all members are
X static, and must be laid out together. If the
X union is an anonymous union, we arrage for that
X as well. PUBLICP is nonzero if this union is
X not declared static. */
Xvoid
Xfinish_anon_union (anon_union_decl)
X tree anon_union_decl;
X{
X tree type = TREE_TYPE (anon_union_decl);
X tree field, decl;
X tree elems = NULL_TREE;
X int public_p = TREE_PUBLIC (anon_union_decl);
X int static_p = TREE_STATIC (anon_union_decl);
X int external_p = TREE_EXTERNAL (anon_union_decl);
X
X if ((field = TYPE_FIELDS (type)) == NULL_TREE)
X return;
X
X if (public_p && (static_p || external_p))
X error ("optimizer cannot handle global anonymous unions");
X
X while (field)
X {
X decl = build_decl (VAR_DECL, DECL_NAME (field), TREE_TYPE (field));
X /* tell `pushdecl' that this is not tentative. */
X DECL_INITIAL (decl) = error_mark_node;
X TREE_PUBLIC (decl) = public_p;
X TREE_STATIC (decl) = static_p;
X TREE_EXTERNAL (decl) = external_p;
X decl = pushdecl (decl);
X DECL_INITIAL (decl) = NULL_TREE;
X elems = tree_cons (DECL_ASSEMBLER_NAME (field), decl, elems);
X TREE_TYPE (elems) = type;
X field = TREE_CHAIN (field);
X }
X if (static_p)
X make_decl_rtl (decl, 0, global_bindings_p ());
X expand_anon_union_decl (decl, NULL_TREE, elems);
X}
X
X/* Finish and output a table which is generated by the compiler.
X NAME is the name to give the table.
X TYPE is the type of the table entry.
X INIT is all the elements in the table.
X PUBLICP is non-zero if this table should be given external visibility. */
Xtree
Xfinish_table (name, type, init, publicp)
X tree name, type, init;
X{
X tree itype, atype, decl;
X
X itype = build_index_type (build_int_2 (list_length (init), 0));
X atype = build_cplus_array_type (type, itype);
X layout_type (atype);
X decl = build_decl (VAR_DECL, name, atype);
X decl = pushdecl (decl);
X TREE_STATIC (decl) = 1;
X TREE_PUBLIC (decl) = publicp;
X init = build (CONSTRUCTOR, atype, NULL_TREE, init);
X TREE_LITERAL (init) = 1;
X TREE_STATIC (init) = 1;
X DECL_INITIAL (decl) = init;
X finish_decl (decl, init,
X build_string (IDENTIFIER_LENGTH (DECL_NAME (decl)),
X IDENTIFIER_POINTER (DECL_NAME (decl))));
X return decl;
X}
X
X/* Auxilliary functions to make type signatures for
X `operator new' and `operator delete' correspond to
X what compiler will be expecting. */
X
Xextern tree sizetype;
X
Xtree
Xcoerce_new_type (ctype, type)
X tree ctype;
X tree type;
X{
X int e1 = 0, e2 = 0;
X
X if (TREE_CODE (type) == METHOD_TYPE)
X type = build_function_type (TREE_TYPE (type), TREE_CHAIN (TYPE_ARG_TYPES (type)));
X if (TREE_TYPE (type) != ptr_type_node)
X e1 = 1, error ("`operator new' must return type `void *'");
X
X /* Technically the type must be `size_t', but we may not know
X what that is. */
X if (TYPE_ARG_TYPES (type) == NULL_TREE)
X e1 = 1, error ("`operator new' takes type `size_t' parameter");
X else if (TREE_CODE (TREE_VALUE (TYPE_ARG_TYPES (type))) != INTEGER_TYPE
X || TYPE_PRECISION (TREE_VALUE (TYPE_ARG_TYPES (type))) != TYPE_PRECISION (sizetype))
X e2 = 1, error ("`operator new' takes type `size_t' as first parameter");
X if (e2)
X type = build_function_type (ptr_type_node, tree_cons (NULL_TREE, sizetype, TREE_CHAIN (TYPE_ARG_TYPES (type))));
X else if (e1)
X type = build_function_type (ptr_type_node, TYPE_ARG_TYPES (type));
X return type;
X}
X
Xtree
Xcoerce_delete_type (ctype, type)
X tree ctype;
X tree type;
X{
X int e1 = 0, e2 = 0, e3 = 0;
X tree arg_types = TYPE_ARG_TYPES (type);
X
X if (TREE_CODE (type) == METHOD_TYPE)
X {
X type = build_function_type (TREE_TYPE (type), TREE_CHAIN (arg_types));
X arg_types = TREE_CHAIN (arg_types);
X }
X if (TREE_TYPE (type) != void_type_node)
X e1 = 1, error ("`operator delete' must return type `void'");
X if (arg_types == NULL_TREE
X || TREE_VALUE (arg_types) != ptr_type_node)
X e2 = 1, error ("`operator delete' takes type `void *' as first parameter");
X
X if (arg_types
X && TREE_CHAIN (arg_types)
X && TREE_CHAIN (arg_types) != void_list_node)
X {
X /* Again, technically this argument must be `size_t', but again
X we may not know what that is. */
X tree t2 = TREE_VALUE (TREE_CHAIN (arg_types));
X if (TREE_CODE (t2) != INTEGER_TYPE
X || TYPE_PRECISION (t2) != TYPE_PRECISION (sizetype))
X e3 = 1, error ("second argument to `operator delete' must be of type `size_t'");
X else if (TREE_CHAIN (TREE_CHAIN (arg_types)) != void_list_node)
X {
X e3 = 1;
X if (TREE_CHAIN (TREE_CHAIN (arg_types)))
X error ("too many arguments in declaration of `operator delete'");
X else
X error ("`...' invalid in specification of `operator delete'");
X }
X }
X if (e3)
X arg_types = tree_cons (NULL_TREE, ptr_type_node, build_tree_list (NULL_TREE, sizetype));
X else if (e3 |= e2)
X {
X if (arg_types == NULL_TREE)
X arg_types = void_list_node;
X else
X arg_types = tree_cons (NULL_TREE, ptr_type_node, TREE_CHAIN (arg_types));
X }
X else e3 |= e1;
X
X if (e3)
X type = build_function_type (void_type_node, arg_types);
X
X return type;
X}
X
Xstatic void
Xwrite_vtable_entries (decl)
X tree decl;
X{
X tree entries;
X
X for (entries = TREE_CHAIN (CONSTRUCTOR_ELTS (DECL_INITIAL (decl)));
X entries; entries = TREE_CHAIN (entries))
X {
X tree fnaddr = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (entries));
X tree fn = TREE_OPERAND (fnaddr, 0);
X if (! TREE_ASM_WRITTEN (fn)
X && DECL_SAVED_INSNS (fn))
X {
X if (TREE_PUBLIC (DECL_CONTEXT (fn)))
X TREE_PUBLIC (fn) = 1;
X TREE_ADDRESSABLE (fn) = 1;
X output_inline_function (fn);
X }
X }
X}
X
Xstatic void
Xfinish_vtable_typedecl (prev, vars)
X tree prev, vars;
X{
X tree decl = CLASS_ASSOC_VTABLE (TREE_TYPE (vars));
X
X /* If we are controlled by `+e2', obey. */
X if (write_virtuals == 2)
X {
X tree assoc = value_member (DECL_NAME (vars), pending_vtables);
X if (assoc)
X TREE_PURPOSE (assoc) = void_type_node;
X else
X decl = NULL_TREE;
X }
X /* If this type has inline virtual functions, then
X write those functions out now. */
X if (decl && (TREE_PUBLIC (decl)
X || (! TREE_EXTERNAL (decl) && TREE_USED (decl))))
X write_vtable_entries (decl);
X}
X
Xstatic void
Xfinish_vtable_vardecl (prev, vars)
X tree prev, vars;
X{
X if (write_virtuals < 0)
X ;
X else if (write_virtuals == 0
X ? TREE_USED (vars)
X : (TREE_PUBLIC (vars)
X || (! TREE_EXTERNAL (vars) && TREE_USED (vars))))
X {
X extern tree the_null_vtable_entry;
X
X /* Stuff this virtual function table's size into
X `pfn' slot of `the_null_vtable_entry'. */
X tree nelts = array_type_nelts (TREE_TYPE (vars));
X tree *ppfn = &FNADDR_FROM_VTABLE_ENTRY (the_null_vtable_entry);
X *ppfn = nelts;
X assert (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (TREE_VALUE (CONSTRUCTOR_ELTS (DECL_INITIAL (vars))))))) == nelts);
X /* Write it out. */
X write_vtable_entries (vars);
X if (TREE_TYPE (DECL_INITIAL (vars)) == 0)
X store_init_value (vars, DECL_INITIAL (vars));
X rest_of_decl_compilation (vars, 0, 1, 1);
X }
X /* We know that PREV must be non-zero here. */
X TREE_CHAIN (prev) = TREE_CHAIN (vars);
X}
X
Xvoid
Xwalk_vtables (typedecl_fn, vardecl_fn)
X register void (*typedecl_fn)();
X register void (*vardecl_fn)();
X{
X tree prev, vars;
X
X for (prev = 0, vars = getdecls (); vars; vars = TREE_CHAIN (vars))
X {
X if (TREE_CODE (vars) == TYPE_DECL
X && TYPE_LANG_SPECIFIC (TREE_TYPE (vars))
X && CLASSTYPE_VSIZE (TREE_TYPE (vars)))
X {
X if (typedecl_fn) (*typedecl_fn) (prev, vars);
X }
X else if (TREE_CODE (vars) == VAR_DECL && DECL_VIRTUAL_P (vars))
X {
X if (vardecl_fn) (*vardecl_fn) (prev, vars);
X }
X else
X prev = vars;
X }
X}
X
Xextern int parse_time, varconst_time;
X
X#define TIMEVAR(VAR, BODY) \
Xdo { int otime = gettime (); BODY; VAR += gettime () - otime; } while (0)
X
X/* This routine is called from the last rule in yyparse ().
X Its job is to create all the code needed to initialize and
X destroy the global aggregates. We do the destruction
X first, since that way we only need to reverse the decls once. */
X
Xvoid
Xfinish_file ()
X{
X extern struct rtx_def *const0_rtx;
X extern int lineno;
X extern struct _iob *asm_out_file;
X int start_time, this_time;
X char *init_function_name;
X
X char *buf;
X char *p;
X tree fnname;
X tree vars = static_aggregates;
X int needs_cleaning = 0, needs_messing_up = 0;
X
X if (main_input_filename == 0)
X main_input_filename = input_filename;
X if (!first_global_object_name)
X first_global_object_name = main_input_filename;
X
X buf = (char *) alloca (sizeof (FILE_FUNCTION_FORMAT)
X + strlen (first_global_object_name));
X
X#ifndef MERGED
X if (flag_detailed_statistics)
X dump_tree_statistics ();
X#endif
X
X /* Bad parse errors. Just forget about it. */
X if (! global_bindings_p ())
X return;
X
X#ifndef MERGED
X /* This is the first run of an unexec'd program, so save this till
X we come back again. -- [email protected] */
X {
X extern int just_done_unexec;
X if (just_done_unexec)
X return;
X }
X#endif
X
X start_time = gettime ();
X
X /* Push into C language context, because that's all
X we'll need here. */
X push_lang_context (lang_name_c);
X
X /* Set up the name of the file-level functions we may need. */
X /* Use a global object (which is already required to be unique over
X the program) rather than the file name (which imposes extra
X constraints). -- [email protected], 10 Jan 1990. */
X sprintf (buf, FILE_FUNCTION_FORMAT, first_global_object_name);
X
X /* Don't need to pull wierd characters out of global names. */
X if (first_global_object_name == main_input_filename)
X {
X for (p = buf+11; *p; p++)
X if (! ((*p >= '0' && *p <= '9')
X#if 0 /* we want labels, which are proper C code */
X#ifndef ASM_IDENTIFY_GCC /* this is required if `.' is invalid -- k. raeburn */
X || *p == '.'
X#endif
X#endif
X#ifndef NO_DOLLAR_IN_LABEL /* this for `$'; unlikely, but... -- kr */
X || *p == '$'
X#endif
X || (*p >= 'A' && *p <= 'Z')
X || (*p >= 'a' && *p <= 'z')))
X *p = '_';
X }
X
X /* See if we really need the hassle. */
X while (vars && needs_cleaning == 0)
X {
X tree decl = TREE_VALUE (vars);
X tree type = TREE_TYPE (decl);
X if (TYPE_NEEDS_DESTRUCTOR (type))
X {
X needs_cleaning = 1;
X needs_messing_up = 1;
X break;
X }
X else
X needs_messing_up |= TYPE_NEEDS_CONSTRUCTING (type);
X vars = TREE_CHAIN (vars);
X }
X if (needs_cleaning == 0)
X goto mess_up;
X
X /* Otherwise, GDB can get confused, because in only knows
X about source for LINENO-1 lines. */
X lineno -= 1;
X
X#if defined(sun)
X /* Point Sun linker at this function. */
X fprintf (asm_out_file, ".stabs \"_fini\",10,0,0,0\n.stabs \"");
X assemble_name (asm_out_file, buf);
X fprintf (asm_out_file, "\",4,0,0,0\n");
X#endif
X
X fnname = get_identifier (buf);
X start_function (void_list_node, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE), 0, 0);
X DECL_PRINT_NAME (current_function_decl) = "file cleanup";
X fnname = DECL_NAME (current_function_decl);
X store_parm_decls ();
X
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X
X /* These must be done in backward order to destroy,
X in which they happen to be! */
X while (vars)
X {
X tree decl = TREE_VALUE (vars);
X tree type = TREE_TYPE (decl);
X tree temp = TREE_PURPOSE (vars);
X
X if (TYPE_NEEDS_DESTRUCTOR (type))
X {
X if (TREE_STATIC (vars))
X expand_start_cond (build_binary_op (NE_EXPR, temp, integer_zero_node), 0);
X if (TREE_CODE (type) == ARRAY_TYPE)
X temp = decl;
X else
X {
X mark_addressable (decl);
X temp = build1 (ADDR_EXPR, TYPE_POINTER_TO (type), decl);
X }
X temp = build_delete (TREE_TYPE (temp), temp, integer_two_node, LOOKUP_NORMAL, 0);
X expand_expr_stmt (temp);
X
X if (TREE_STATIC (vars))
X expand_end_cond ();
X }
X vars = TREE_CHAIN (vars);
X }
X
X expand_end_bindings (getdecls (), 1, 0);
X poplevel (1, 0, 1);
X pop_momentary ();
X
X finish_function (lineno, 0);
X
X#if defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER) && !defined(SDB_DEBUGGING_INFO)
X /* Now tell GNU LD that this is part of the static destructor set. */
X {
X extern struct _iob *asm_out_file;
X fprintf (asm_out_file, ".stabs \"___DTOR_LIST__\",22,0,0,");
X assemble_name (asm_out_file, IDENTIFIER_POINTER (fnname));
X fputc ('\n', asm_out_file);
X }
X#endif
X
X /* if it needed cleaning, then it will need messing up: drop through */
X
X mess_up:
X /* Must do this while we think we are at the top level. */
X vars = nreverse (static_aggregates);
X if (vars != NULL_TREE)
X {
X buf[FILE_FUNCTION_PREFIX_LEN] = 'I';
X
X#if defined(sun)
X /* Point Sun linker at this function. */
X fprintf (asm_out_file, ".stabs \"_init\",10,0,0,0\n.stabs \"");
X assemble_name (asm_out_file, buf);
X fprintf (asm_out_file, "\",4,0,0,0\n");
X#endif
X
X fnname = get_identifier (buf);
X start_function (void_list_node, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE), 0, 0);
X DECL_PRINT_NAME (current_function_decl) = "file initialization";
X fnname = DECL_NAME (current_function_decl);
X store_parm_decls ();
X
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X
X#ifdef SOS
X if (flag_all_virtual == 2)
X {
X tree decl;
X char c = buf[FILE_FUNCTION_PREFIX_LEN];
X buf[FILE_FUNCTION_PREFIX_LEN] = 'Z';
X
X decl = pushdecl (build_lang_decl (FUNCTION_DECL, get_identifier (buf), default_function_type));
X finish_decl (decl, NULL_TREE, NULL_TREE);
X expand_expr_stmt (build_function_call (decl, NULL_TREE));
X buf[FILE_FUNCTION_PREFIX_LEN] = c;
X }
X#endif
X
X while (vars)
X {
X tree decl = TREE_VALUE (vars);
X tree init = TREE_PURPOSE (vars);
X
X /* If this was a static attribute within some function's scope,
X then don't initialize it here. Also, don't bother
X with initializers that contain errors. */
X if (TREE_STATIC (vars)
X || (init && TREE_CODE (init) == TREE_LIST
X && value_member (error_mark_node, init)))
X {
X vars = TREE_CHAIN (vars);
X continue;
X }
X
X if (TREE_CODE (decl) == VAR_DECL)
X {
X /* Set these global variables so that GDB at least puts
X us near the declaration which required the initialization. */
X input_filename = DECL_SOURCE_FILE (decl);
X lineno = DECL_SOURCE_LINE (decl);
X emit_note (input_filename, lineno);
X
X if (init)
X {
X if (TREE_CODE (init) == VAR_DECL)
X {
X /* This behavior results when there are
X multiple declarations of an aggregate,
X the last of which defines it. */
X if (DECL_RTL (init) == DECL_RTL (decl))
X {
X if (! (DECL_INITIAL (decl) == error_mark_node
X || (TREE_CODE (DECL_INITIAL (decl)) == CONSTRUCTOR
X && CONSTRUCTOR_ELTS (DECL_INITIAL (decl)) == NULL_TREE)))
X abort ();
X
X init = DECL_INITIAL (init);
X if (TREE_CODE (init) == CONSTRUCTOR
X && CONSTRUCTOR_ELTS (init) == NULL_TREE)
X init = NULL_TREE;
X }
X#if 0
X else if (TREE_TYPE (decl) == TREE_TYPE (init))
X {
X#if 1
X assert (0);
X#else
X /* point to real decl's rtl anyway. */
X DECL_RTL (init) = DECL_RTL (decl);
X assert (DECL_INITIAL (decl) == error_mark_node);
X init = DECL_INITIAL (init);
X#endif /* 1 */
X }
X#endif /* 0 */
X }
X }
X if (IS_AGGR_TYPE (TREE_TYPE (decl))
X || init == 0
X || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
X expand_aggr_init (decl, init, 0);
X else if (TREE_CODE (init) == TREE_VEC)
X expand_expr (expand_vec_init (decl, TREE_VEC_ELT (init, 0),
X TREE_VEC_ELT (init, 1),
X TREE_VEC_ELT (init, 2), 0),
X const0_rtx, VOIDmode, 0);
X else
X expand_assignment (decl, init, 0, 0);
X }
X else if (TREE_CODE (decl) == SAVE_EXPR)
X {
X if (! PARM_DECL_EXPR (decl))
X {
X /* a `new' expression at top level. */
X expand_expr (decl, const0_rtx, VOIDmode, 0);
X expand_aggr_init (build_indirect_ref (decl, 0), init, 0);
X }
X }
X else if (decl == error_mark_node)
X ;
X else abort ();
X vars = TREE_CHAIN (vars);
X }
X
X expand_end_bindings (getdecls (), 1, 0);

X poplevel (1, 0, 1);
X pop_momentary ();
X
X finish_function (lineno, 0);
X#if defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER) && !defined(SDB_DEBUGGING_INFO)
X /* Now tell GNU LD that this is part of the static constructor set. */
X {
X extern struct _iob *asm_out_file;
X fprintf (asm_out_file, ".stabs \"___CTOR_LIST__\",22,0,0,");
X assemble_name (asm_out_file, IDENTIFIER_POINTER (fnname));
X fputc ('\n', asm_out_file);
X }
X#endif
X }
X
X#ifdef SOS
X if (flag_all_virtual == 2)
X {
X tree __sosDynError = default_conversion (lookup_name (get_identifier ("sosDynError")));
X
X tree null_string = build1 (ADDR_EXPR, string_type_node, combine_strings (build_string (0, "")));
X tree tags = gettags ();
X tree decls = getdecls ();
X tree entry;
X int i;
X
X entry = NULL_TREE;
X for (i = 0; i < 3; i++)
X entry = tree_cons (NULL_TREE, integer_zero_node, entry);
X zlink = build_tree_list (NULL_TREE, build (CONSTRUCTOR, zlink_type, NULL_TREE, entry));
X TREE_LITERAL (TREE_VALUE (zlink)) = 1;
X TREE_STATIC (TREE_VALUE (zlink)) = 1;
X
X entry = NULL_TREE;
X for (i = 0; i < 5; i++)
X entry = tree_cons (NULL_TREE, integer_zero_node, entry);
X zret = build_tree_list (NULL_TREE, build (CONSTRUCTOR, zret_type, NULL_TREE, entry));
X TREE_LITERAL (TREE_VALUE (zret)) = 1;
X TREE_STATIC (TREE_VALUE (zret)) = 1;
X
X /* Symbols with external visibility (except globally visible
X dynamic member functions) into the `zlink' table. */
X while (decls)
X {
X if (TREE_PUBLIC (decls)
X && TREE_ASM_WRITTEN (decls)
X && (TREE_CODE (decls) != FUNCTION_DECL
X || TREE_CODE (TREE_TYPE (decls)) != METHOD_TYPE
X || TYPE_DYNAMIC (TYPE_METHOD_BASETYPE (TREE_TYPE (decls))) == 0))
X {
X entry = build (CONSTRUCTOR, zlink_type, NULL_TREE,
X tree_cons (NULL_TREE,
X build1 (ADDR_EXPR, string_type_node,
X combine_strings (build_string (IDENTIFIER_LENGTH (DECL_NAME (decls)),
X IDENTIFIER_POINTER (DECL_NAME (decls))))),
X tree_cons (NULL_TREE, integer_one_node,
X build_tree_list (NULL_TREE, build_unary_op (ADDR_EXPR, decls, 0)))));
X TREE_LITERAL (entry) = 1;
X TREE_STATIC (entry) = 1;
X zlink = tree_cons (NULL_TREE, entry, zlink);
X }
X decls = TREE_CHAIN (decls);
X }
X
X buf[FILE_FUNCTION_PREFIX_LEN] = 'Z';
X
X fnname = get_identifier (buf);
X start_function (void_list_node, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE), 0, 0);
X fnname = DECL_NAME (current_function_decl);
X store_parm_decls ();
X
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X
X {
X tree decl, type;
X /* Lay out a table static to this function
X with information about all text and data that
X this file provides. */
X tree zlink_table = finish_table (get_identifier ("__ZLINK_tbl"), zlink_type, zlink, 0);
X decl = pushdecl (build_decl (VAR_DECL, get_identifier ("_ZLINK_once"), integer_type_node));
X TREE_STATIC (decl) = 1;
X finish_decl (decl, NULL_TREE, NULL_TREE);
X expand_start_cond (truthvalue_conversion (decl), 0);
X expand_null_return ();
X expand_end_cond ();
X finish_stmt ();
X expand_expr_stmt (build_modify_expr (decl, NOP_EXPR, integer_one_node));
X type = build_function_type (void_type_node, NULL_TREE);
X decl = pushdecl (build_lang_decl (FUNCTION_DECL, get_identifier ("_Ztable_from_cfront"), type));
X TREE_EXTERNAL (decl) = 1;
X finish_decl (decl, NULL_TREE, NULL_TREE);
X expand_expr_stmt (build_function_call (decl, build_tree_list (NULL_TREE, zlink_table)));
X expand_null_return ();
X }
X expand_end_bindings (0, 1, 0);
X poplevel (1, 0, 1);
X pop_momentary ();
X finish_function (lineno, 0);
X
X buf[FILE_FUNCTION_PREFIX_LEN] = 'Y';
X fnname = get_identifier (buf);
X start_function (build_tree_list (NULL_TREE, get_identifier ("int")),
X build_parse_node (INDIRECT_REF, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE)), 0, 0);
X fnname = DECL_NAME (current_function_decl);
X store_parm_decls ();
X
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X
X {
X#define SOS_VERSION 2
X tree sosVersionNumber = build_int_2 (SOS_VERSION, 0);
X tree zret_table;
X
X /* For each type defined, if is a dynamic type, write out
X an entry linking its member functions with their names. */
X
X while (tags)
X {
X tree type = TREE_VALUE (tags);
X if (TYPE_DYNAMIC (type))
X {
X /* SOS currently only implements single inheritance,
X so we just pick up one string, if this class
X has a base class. */
X tree base_name = CLASSTYPE_N_BASECLASSES (type) > 0 && TYPE_DYNAMIC (CLASSTYPE_BASECLASS (type, 1))
X ? build1 (ADDR_EXPR, string_type_node, CLASSTYPE_TYPENAME_AS_STRING (CLASSTYPE_BASECLASS (type, 1)))
X : null_string;
X tree dyn_table, dyn_entry = NULL_TREE, fns = CLASS_ASSOC_VIRTUALS (type);
X while (fns)
X {
X if (TREE_ASM_WRITTEN (TREE_OPERAND (TREE_VALUE (fns), 0)))
X dyn_entry = tree_cons (NULL_TREE, TREE_VALUE (fns), dyn_entry);
X else
X dyn_entry = tree_cons (NULL_TREE, __sosDynError, dyn_entry);
X fns = TREE_CHAIN (fns);
X }
X dyn_entry = nreverse (dyn_entry);
X dyn_entry = tree_cons (NULL_TREE, build1 (NOP_EXPR, TYPE_POINTER_TO (default_function_type), sosVersionNumber),
X tree_cons (NULL_TREE, integer_zero_node,
X tree_cons (NULL_TREE, integer_zero_node,
X dyn_entry)));
X dyn_table = finish_table (DECL_NAME (TYPE_NAME (type)), TYPE_POINTER_TO (default_function_type), dyn_entry, 0);
X entry = build (CONSTRUCTOR, zret_type, NULL_TREE,
X tree_cons (NULL_TREE, build1 (ADDR_EXPR, string_type_node, CLASSTYPE_TYPENAME_AS_STRING (type)),
X tree_cons (NULL_TREE, default_conversion (dyn_table),
X tree_cons (NULL_TREE, build_int_2 (CLASSTYPE_VSIZE (type), 0),
X tree_cons (NULL_TREE, base_name,
X build_tree_list (NULL_TREE, integer_zero_node))))));
X TREE_LITERAL (entry) = 1;
X TREE_STATIC (entry) = 1;
X zret = tree_cons (NULL_TREE, entry, zret);
X }
X tags = TREE_CHAIN (tags);
X }
X zret_table = finish_table (get_identifier ("__Zret"), zret_type, zret, 0);
X c_expand_return (convert (build_pointer_type (integer_type_node), default_conversion (zret_table)));
X }
X expand_end_bindings (0, 1, 0);
X poplevel (1, 0, 1);
X pop_momentary ();
X finish_function (lineno, 0);
X
X#if defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER)
X {
X extern struct _iob *asm_out_file;
X fprintf (asm_out_file, ".stabs \"___ZTOR_LIST__\",22,0,0,");
X assemble_name (asm_out_file, IDENTIFIER_POINTER (fnname));
X fputc ('\n', asm_out_file);
X }
X#endif
X }
X#endif
X
X /* Done with C language context needs. */
X pop_lang_context ();
X
X /* Now write out any static class variables (which may have since
X learned how to be initialized). */
X while (pending_statics)
X {
X tree decl = TREE_VALUE (pending_statics);
X if (TREE_USED (decl) == 1
X || TREE_READONLY (decl) == 0
X || DECL_INITIAL (decl) == 0)
X rest_of_decl_compilation (decl, DECL_ASSEMBLER_NAME (decl), 1, 1);
X pending_statics = TREE_CHAIN (pending_statics);
X }
X
X this_time = gettime ();
X parse_time -= this_time - start_time;
X varconst_time += this_time - start_time;
X
X /* Now write out inline functions which had their addresses taken
X and which were not declared virtual and which were not declared
X `extern inline'. */
X while (pending_addressable_inlines)
X {
X tree decl = TREE_VALUE (pending_addressable_inlines);
X if (! TREE_ASM_WRITTEN (decl)
X && ! TREE_EXTERNAL (decl)
X && DECL_SAVED_INSNS (decl))
X output_inline_function (decl);
X pending_addressable_inlines = TREE_CHAIN (pending_addressable_inlines);
X }
X
X start_time = gettime ();
X
X /* Now delete from the chain of variables all virtual function tables.
X We output them all ourselves, because each will be treated specially. */
X
X /* Make last thing in global scope not be a virtual function table. */
X vars = build_decl (VAR_DECL, get_identifier (" @%$#@!"), integer_type_node);
X TREE_EXTERNAL (vars) = 1;
X pushdecl (vars);
X /* Make this dummy variable look used. */
X TREE_ASM_WRITTEN (vars) = 1;
X DECL_INITIAL (vars) = error_mark_node;
X
X walk_vtables (finish_vtable_typedecl, finish_vtable_vardecl);
X
X if (write_virtuals == 2)
X {
X /* Now complain about an virtual function tables promised
X but not delivered. */
X while (pending_vtables)
X {
X if (TREE_PURPOSE (pending_vtables) == NULL_TREE)
X error ("virtual function table for `%s' not defined",
X IDENTIFIER_POINTER (TREE_VALUE (pending_vtables)));
X pending_vtables = TREE_CHAIN (pending_vtables);
X }
X }
X
X permanent_allocation ();
X this_time = gettime ();
X parse_time -= this_time - start_time;
X varconst_time += this_time - start_time;
X
X if (flag_detailed_statistics)
X dump_time_statistics ();
X}
!EOF
echo "Extracting dbxout.c..."
sed 's/^X//' >dbxout.c << '!EOF'
X/* Output dbx-format symbol table information from GNU compiler.
X Copyright (C) 1987, 1988 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* Output dbx-format symbol table data.
X This consists of many symbol table entries, each of them
X a .stabs assembler pseudo-op with four operands:
X a "name" which is really a description of one symbol and its type,
X a "code", which is a symbol defined in stab.h whose name starts with N_,
X an unused operand always 0,
X and a "value" which is an address or an offset.
X The name is enclosed in doublequote characters.
X
X Each function, variable, typedef, and structure tag
X has a symbol table entry to define it.
X The beginning and end of each level of name scoping within
X a function are also marked by special symbol table entries.
X
X The "name" consists of the symbol name, a colon, a kind-of-symbol letter,
X and a data type number. The data type number may be followed by
X "=" and a type definition; normally this will happen the first time
X the type number is mentioned. The type definition may refer to
X other types by number, and those type numbers may be followed
X by "=" and nested definitions.
X
X This can make the "name" quite long.
X When a name is more than 80 characters, we split the .stabs pseudo-op
X into two .stabs pseudo-ops, both sharing the same "code" and "value".
X The first one is marked as continued with a double-backslash at the
X end of its "name".
X
X The kind-of-symbol letter distinguished function names from global
X variables from file-scope variables from parameters from auto
X variables in memory from typedef names from register variables.
X See `dbxout_symbol'.
X
X The "code" is mostly redundant with the kind-of-symbol letter
X that goes in the "name", but not entirely: for symbols located
X in static storage, the "code" says which segment the address is in,
X which controls how it is relocated.
X
X The "value" for a symbol in static storage
X is the core address of the symbol (actually, the assembler
X label for the symbol). For a symbol located in a stack slot
X it is the stack offset; for one in a register, the register number.
X For a typedef symbol, it is zero.
X
X If DEBUG_SYMS_TEXT is defined, all debugging symbols must be
X output while in the text section.
X
X For more on data type definitions, see `dbxout_type'. */
X
X#include "config.h"
X#include "tree.h"
X#include "cplus-tree.h"
X#include "rtl.h"
X#include "flags.h"
X#include
X
X/* Typical USG systems don't have stab.h, and they also have
X no use for DBX-format debugging info. */
X
X#ifdef DBX_DEBUGGING_INFO
X
X#ifdef DEBUG_SYMS_TEXT
X#define FORCE_TEXT text_section ();
X#else
X#define FORCE_TEXT
X#endif
X
X#ifdef USG
X#include "stab.h" /* If doing DBX on sysV, use our own stab.h. */
X#else
X#include /* On BSD, use the system's stab.h. */
X#endif /* not USG */
X
X/* Stream for writing to assembler file. */
X
Xstatic FILE *asmfile;
X
Xenum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED};
X
X/* Vector recording the status of describing C data types.
X When we first notice a data type (a tree node),
X we assign it a number using next_type_number.
X That is its index in this vector.
X The vector element says whether we have yet output
X the definition of the type. TYPE_XREF says we have
X output it as a cross-reference only. */
X
Xenum typestatus *typevec;
X
X/* Number of elements of space allocated in `typevec'. */
X
Xstatic int typevec_len;
X
X/* In dbx output, each type gets a unique number.
X This is the number for the next type output.
X The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field. */
X
Xstatic int next_type_number;
X
X/* In dbx output, we must assign symbol-blocks id numbers
X in the order in which their beginnings are encountered.
X We output debugging info that refers to the beginning and
X end of the ranges of code in each block
X with assembler labels LBBn and LBEn, where n is the block number.
X The labels are generated in final, which assigns numbers to the
X blocks in the same way. */
X
Xstatic int next_block_number;
X
X/* These variables are for dbxout_symbol to communicate to
X dbxout_finish_symbol.
X current_sym_code is the symbol-type-code, a symbol N_... define in stab.h.
X current_sym_value and current_sym_addr are two ways to address the
X value to store in the symtab entry.
X current_sym_addr if nonzero represents the value as an rtx.
X If that is zero, current_sym_value is used. This is used
X when the value is an offset (such as for auto variables,
X register variables and parms). */
X
Xstatic int current_sym_code;
Xstatic int current_sym_value;
Xstatic rtx current_sym_addr;
X
X/* Number of chars of symbol-description generated so far for the
X current symbol. Used by CHARS and CONTIN. */
X
Xstatic int current_sym_nchars;
X
X/* Report having output N chars of the current symbol-description. */
X
X#define CHARS(N) (current_sym_nchars += (N))
X
X/* Break the current symbol-description, generating a continuation,
X if it has become long. */
X
X#ifndef DBX_CONTIN_LENGTH
X#define DBX_CONTIN_LENGTH 80
X#endif
X
X#if DBX_CONTIN_LENGTH > 0
X#define CONTIN \
X do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0)
X#else
X#define CONTIN
X#endif
X
Xvoid dbxout_types ();
Xvoid dbxout_tags ();
Xvoid dbxout_args ();
Xvoid dbxout_symbol ();
Xstatic void dbxout_type_name ();
Xstatic void dbxout_type ();
Xstatic void dbxout_finish_symbol ();
Xstatic void dbxout_continue ();
X
X/* At the beginning of compilation, start writing the symbol table.
X Initialize `typevec' and output the standard data types of C. */
X
Xvoid
Xdbxout_init (asm_file, input_file_name)
X FILE *asm_file;
X char *input_file_name;
X{
X asmfile = asm_file;
X
X typevec_len = 100;
X typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]);
X bzero (typevec, typevec_len * sizeof typevec[0]);
X
X /* Used to put `Ltext:' before the reference, but that loses on sun 4. */
X fprintf (asmfile,
X "\t.stabs \"%s\",%d,0,0,Ltext\nLtext:\n",
X input_file_name, N_SO);
X
X next_type_number = 1;
X next_block_number = 2;
X
X /* Make sure that types `int' and `char' have numbers 1 and 2.
X Definitions of other integer types will refer to those numbers. */
X
X dbxout_symbol (TYPE_NAME (integer_type_node), 0);
X dbxout_symbol (TYPE_NAME (char_type_node), 0);
X
X /* Get all permanent types not yet gotten, and output them. */
X
X dbxout_types (get_permanent_types ());
X}
X
X/* Change by Bryan Boreham, Kewill, Sun Aug 13 15:31:25 1989.
X Added to support unexecing of compiler. */
X
Xvoid
Xre_init_dbxout_for_unexec (asm_file, input_file_name)
X FILE *asm_file;
X char *input_file_name;
X{
X asmfile = asm_file;
X}
X
X/* Continue a symbol-description that gets too big.
X End one symbol table entry with a double-backslash
X and start a new one, eventually producing something like
X .stabs "start......\\",code,0,value
X .stabs "...rest",code,0,value */
X
Xstatic void
Xdbxout_continue ()
X{
X#ifdef DBX_CONTIN_CHAR
X fprintf (asmfile, "%c", DBX_CONTIN_CHAR);
X#else
X fprintf (asmfile, "\\\\");
X#endif
X dbxout_finish_symbol ();
X fprintf (asmfile, ".stabs \"");
X current_sym_nchars = 0;
X}
X
X/* Output a reference to a type. If the type has not yet been
X described in the dbx output, output its definition now.
X For a type already defined, just refer to its definition
X using the type number.
X
X If FULL is nonzero, and the type has been described only with
X a forward-reference, output the definition now.
X If FULL is zero in this case, just refer to the forward-reference
X using the number previously allocated. */
X
Xstatic void
Xdbxout_type (type, full)
X tree type;
X int full;
X{
X register tree fields, tem, method_vec;
X tree *methods, *end;
X char *vfield_name = 0;
X tree virtual_basetype = 0;
X
X /* If there was an input error and we don't really have a type,
X avoid crashing and write something that is at least valid
X by assuming `int'. */
X if (type == error_mark_node)
X type = integer_type_node;
X else /* if (TYPE_SIZE (type) == 0) */
X type = TYPE_MAIN_VARIANT (type);
X
X if (TYPE_SYMTAB_ADDRESS (type) == 0)
X {
X /* Type has no dbx number assigned. Assign next available number. */
X TYPE_SYMTAB_ADDRESS (type) = next_type_number++;
X
X /* Make sure type vector is long enough to record about this type. */
X
X if (next_type_number == typevec_len)
X {
X typevec = (enum typestatus *) xrealloc (typevec, typevec_len * 2 * sizeof typevec[0]);
X bzero (typevec + typevec_len, typevec_len * sizeof typevec[0]);
X typevec_len *= 2;
X }
X }
X
X /* Output the number of this type, to refer to it. */
X fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
X CHARS (3);
X
X /* If this type's definition has been output or is now being output,
X that is all. */
X
X switch (typevec[TYPE_SYMTAB_ADDRESS (type)])
X {
X case TYPE_UNSEEN:
X break;
X case TYPE_XREF:
X if (! full)
X return;
X break;
X case TYPE_DEFINED:
X return;
X }
X
X#ifdef DBX_NO_XREFS
X /* For systems where dbx output does not allow the `=xsNAME:' syntax,
X leave the type-number completely undefined rather than output
X a cross-reference. */
X if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
X || TREE_CODE (type) == ENUMERAL_TYPE)
X
X if ((TYPE_NAME (type) != 0 && !full)
X || TYPE_SIZE (type) == 0)
X {
X typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
X return;
X }
X#endif
X
X /* Output a definition now. */
X
X fprintf (asmfile, "=");
X CHARS (1);
X
X /* Mark it as defined, so that if it is self-referent
X we will not get into an infinite recursion of definitions. */
X
X typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED;
X
X switch (TREE_CODE (type))
X {
X case VOID_TYPE:
X case LANG_TYPE:
X /* For a void type, just define it as itself; ie, "5=5".
X This makes us consider it defined
X without saying what it is. The debugger will make it
X a void type when the reference is seen, and nothing will
X ever override that default. */
X fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
X CHARS (3);
X break;
X
X case INTEGER_TYPE:
X if (type == char_type_node && ! TREE_UNSIGNED (type))
X /* Output the type `char' as a subrange of itself!
X I don't understand this definition, just copied it
X from the output of pcc. */
X fprintf (asmfile, "r2;0;127;");
X else
X /* Output other integer types as subranges of `int'. */
X fprintf (asmfile, "r1;%d;%d;",
X TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)),
X TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)));
X CHARS (25);
X break;
X
X case REAL_TYPE:
X /* This must be magic. */
X fprintf (asmfile, "r1;%d;0;",
X TREE_INT_CST_LOW (size_in_bytes (type)));
X CHARS (16);
X break;
X
X case ARRAY_TYPE:
X /* Output "a" followed by a range type definition
X for the index type of the array
X followed by a reference to the target-type.
X ar1;0;N;M for an array of type M and size N. */
X fprintf (asmfile, "ar1;0;%d;",
X (TYPE_DOMAIN (type)
X ? TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
X : -1));
X CHARS (17);
X dbxout_type (TREE_TYPE (type), 0);
X break;
X
X case RECORD_TYPE:
X case UNION_TYPE:
X {
X int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (type);
X
X /* Output a structure type. */
X if ((TYPE_NAME (type) != 0 && !full)
X || TYPE_SIZE (type) == 0)
X {
X /* If the type is just a cross reference, output one
X and mark the type as partially described.
X If it later becomes defined, we will output
X its real definition.
X If the type has a name, don't nest its name within
X another type's definition; instead, output an xref
X and let the definition come when the name is defined. */
X fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu");
X CHARS (3);
X dbxout_type_name (type);
X fprintf (asmfile, ":");
X typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
X break;
X }
X tem = size_in_bytes (type);
X fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d",
X TREE_INT_CST_LOW (tem));
X
X if (use_gdb_dbx_extensions)
X {
X if (n_baseclasses)
X {
X fprintf (asmfile, "!%d,", n_baseclasses);
X CHARS (8);
X }
X }
X for (i = 1; i <= n_baseclasses; i++)
X {
X tree basetype = CLASSTYPE_BASECLASS (type, i);
X if (use_gdb_dbx_extensions)
X {
X putc (CLASSTYPE_VIA_VIRTUAL (type, 1) ? '1'
X : '0',
X asmfile);

X putc (CLASSTYPE_VIA_PUBLIC (type, 1) ? '2'
X : '0',
X asmfile);
X fprintf (asmfile, "%d,",
X BITS_PER_UNIT * DECL_OFFSET (TYPE_NAME (basetype)));
X CHARS (15);
X dbxout_type (basetype, full);
X putc (';', asmfile);
X }
X else
X {
X /* Print out the base class information with fields
X which have the same names at the types they hold. */
X tree name = TREE_CODE (TYPE_NAME (basetype)) == TYPE_DECL
X ? DECL_NAME (TYPE_NAME (basetype)) : TYPE_NAME (basetype);
X
X fprintf (asmfile, "%s:", IDENTIFIER_POINTER (name));
X CHARS (2 + IDENTIFIER_LENGTH (name));
X dbxout_type (basetype, full);
X fprintf (asmfile, ",%d,%d;",
X BITS_PER_UNIT * DECL_OFFSET (TYPE_NAME (basetype)),
X TREE_INT_CST_LOW (TYPE_SIZE (basetype)) * DECL_SIZE_UNIT (basetype));
X CHARS (20);
X }
X }
X }
X
X CHARS (11);
X
X for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
X /* Output the name, type, position (in bits), size (in bits)
X of each field. */
X /* Omit here local type decls until we know how to support them. */
X if (TREE_CODE (tem) == TYPE_DECL)
X continue;
X /* Omit here the nameless fields that are used to skip bits. */
X else if (DECL_NAME (tem) != 0 && TREE_CODE (tem) != CONST_DECL)
X {
X /* Continue the line if necessary,
X but not before the first field. */
X if (tem != TYPE_FIELDS (type))
X CONTIN;
X fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem)));
X CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem)));
X if (use_gdb_dbx_extensions)
X {
X putc ('/', asmfile);
X#ifdef TREE_PRIVATE
X putc ((TREE_PRIVATE (tem) ? '0'
X : TREE_PROTECTED (tem) ? '1' : '2'),
X asmfile);
X#endif
X CHARS (2);
X }
X
X dbxout_type (TREE_TYPE (tem), 0);
X if (TREE_CODE (tem) == VAR_DECL && TREE_STATIC (tem))
X {
X if (use_gdb_dbx_extensions)
X {
X char *name = DECL_ASSEMBLER_NAME (tem);
X
X /* Adding 1 here only works on systems
X which flush an initial underscore from
X the .stabs entry. This loses for static names
X which have an initial leading '_' on systems which
X don't use leading underscores. */
X if (name[0] == '_')
X name += 1;
X
X fprintf (asmfile, ":%s;", name);
X CHARS (strlen (name));
X }
X else
X {
X fprintf (asmfile, ",0,0;");
X }
X }
X#if 0
X else if (TREE_CODE (tem) == VAR_DECL || TREE_CODE (tem) == CONST_DECL)
X {
X /* GDB 3.2 can't understand these declarations yet. */
X continue;
X }
X#endif
X else
X {
X fprintf (asmfile, ",%d,%d;", DECL_OFFSET (tem),
X TREE_INT_CST_LOW (DECL_SIZE (tem)) * DECL_SIZE_UNIT (tem));
X }
X CHARS (23);
X }
X /* C++: put out the method names and their parameter lists */
X /* We do constructors, destructor, if any, followed by the method names. */
X method_vec = use_gdb_dbx_extensions ? CLASSTYPE_METHOD_VEC (type) : NULL_TREE;
X methods = 0;
X end = 0;
X if (use_gdb_dbx_extensions
X && TREE_CODE (type) == RECORD_TYPE
X && (TYPE_HAS_DESTRUCTOR (type) | TYPE_HAS_CONSTRUCTOR (type)))
X {
X tree dtor;
X
X end = TREE_VEC_END (method_vec);
X /* Destructors lie in a special place. */
X if (TYPE_HAS_DESTRUCTOR (type))
X {
X methods = &TREE_VEC_ELT (method_vec, 0);
X dtor = TREE_VEC_ELT (method_vec, 0);
X tem = TREE_CHAIN (dtor);
X }
X else if (TREE_VEC_LENGTH (method_vec) > 1)
X {
X methods = &TREE_VEC_ELT (method_vec, 1);
X dtor = NULL_TREE;
X tem = *methods;
X }
X else
X {
X methods = end;
X tem = 0;
X }
X
X CHARS (2);
X
X if (tem)
X {
X if (TREE_OPERATOR (tem))
X {
X char *name1 = operator_name_string (DECL_NAME (tem));
X fprintf (asmfile, "op$::%s.", name1);
X CHARS (strlen (name1) + 7);
X }
X else
X {
X fprintf (asmfile, "%s::", IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (tem)));
X CHARS (IDENTIFIER_LENGTH (DECL_ORIGINAL_NAME (tem)) + 3);
X }
X
X while (tem)
X {
X /* Output the name of the field (after overloading), as
X well as the name of the field before overloading, along
X with its parameter list. */
X tree t;
X char c;
X
X CONTIN;
X /* Get to the FUNCTION_DECL */
X t = tem;
X dbxout_type (TREE_TYPE (t), 0); /* METHOD_TYPE */
X if (DECL_VIRTUAL_P (tem))
X c = '*';
X else if (DECL_STATIC_FUNCTION_P (tem))
X c = '?';
X else
X c = '.';
X fprintf (asmfile, ":%s;%c%c",
X IDENTIFIER_POINTER (DECL_NAME (t)),
X TREE_PRIVATE (tem) ? '0' : TREE_PROTECTED (tem) ? '1' : '2', c);
X CHARS (IDENTIFIER_LENGTH (DECL_NAME (t)) + 5);
X if (DECL_VIRTUAL_P (tem))
X {
X fprintf (asmfile, "%d;",
X TREE_INT_CST_LOW (DECL_VINDEX (tem)));
X CHARS (8);
X }
X if (tem == dtor)
X break;
X tem = TREE_CHAIN (tem);
X if (tem == NULL_TREE)
X tem = dtor;
X }
X putc (';', asmfile);
X }
X if (methods != end)
X methods++;
X }
X else if (method_vec != NULL_TREE)
X {
X methods = &TREE_VEC_ELT (method_vec, 1);
X end = TREE_VEC_END (method_vec);
X }
X
X for (; methods != end; methods++)
X {
X tem = *methods;
X
X if (tem)
X {
X if (TREE_OPERATOR (tem))
X {
X char *name1 = operator_name_string (DECL_NAME (tem));
X fprintf (asmfile, "op$::%s.", name1);
X CHARS (strlen (name1) + 6);
X }
X else
X {
X fprintf (asmfile, "%s::",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (tem)));
X CHARS (IDENTIFIER_LENGTH (DECL_ORIGINAL_NAME (tem)) + 3);
X }
X
X for (; tem; tem = TREE_CHAIN (tem))
X /* Output the name of the field (after overloading), as
X well as the name of the field before overloading, along
X with its parameter list */
X {
X /* @@ */
X tree t;
X char c;
X
X CONTIN;
X
X /* Get to the FUNCTION_DECL */
X t = tem;
X dbxout_type (TREE_TYPE (t), 0); /* METHOD_TYPE */
X if (DECL_VIRTUAL_P (tem))
X c = '*';
X else if (DECL_STATIC_FUNCTION_P (tem))
X c = '?';
X else
X c = '.';
X fprintf (asmfile, ":%s;%c%c",
X IDENTIFIER_POINTER (DECL_NAME (t)),
X TREE_PRIVATE (tem) ? '0' : TREE_PROTECTED (tem) ? '1' : '2', c);
X CHARS (IDENTIFIER_LENGTH (DECL_NAME (t)) + 6);
X if (DECL_VIRTUAL_P (tem))
X {
X fprintf (asmfile, "%d;",
X TREE_INT_CST_LOW (DECL_VINDEX (tem)));
X CHARS (8);
X }
X }
X putc (';', asmfile);
X CHARS (1);
X }
X }
X
X putc (';', asmfile);
X
X if (use_gdb_dbx_extensions && TREE_CODE (type) == RECORD_TYPE)
X {
X /* Tell GDB+ that it may keep reading. */
X putc ('~', asmfile);
X if (TYPE_HAS_DESTRUCTOR (type) && TYPE_HAS_CONSTRUCTOR (type))
X putc ('=', asmfile);
X else if (TYPE_HAS_DESTRUCTOR (type))
X putc ('-', asmfile);
X else if (TYPE_HAS_CONSTRUCTOR (type))
X putc ('+', asmfile);
X
X if (CLASSTYPE_VSIZE (type))
X {
X tree t, v = DECL_NAME (CLASSTYPE_VFIELD (type));
X t = type;
X while (CLASSTYPE_N_BASECLASSES (t) && TYPE_VIRTUAL_P (CLASSTYPE_BASECLASS (t, 1)))
X t = CLASSTYPE_BASECLASS (t, 1);
X putc ('%', asmfile);
X dbxout_type (t, 0);
X fprintf (asmfile, ",%s;", IDENTIFIER_POINTER (v));
X CHARS (IDENTIFIER_LENGTH (v) + 6);
X }
X else
X {
X putc (';', asmfile);
X CHARS (3);
X }
X }
X break;
X
X case ENUMERAL_TYPE:
X if ((TYPE_NAME (type) != 0
X && !full
X && ((TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
X && ! ANON_AGGRNAME_P (DECL_NAME (TYPE_NAME (type))))
X || (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE
X && ! ANON_AGGRNAME_P (TYPE_NAME (type)))))
X || TYPE_SIZE (type) == 0)
X {
X fprintf (asmfile, "xe");
X CHARS (3);
X dbxout_type_name (type);
X typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
X fprintf (asmfile, ":");
X return;
X }
X putc ('e', asmfile);
X CHARS (1);
X for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
X {
X fprintf (asmfile, "%s:%d,", IDENTIFIER_POINTER (TREE_PURPOSE (tem)),
X TREE_INT_CST_LOW (TREE_VALUE (tem)));
X CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem)));
X if (TREE_CHAIN (tem) != 0)
X CONTIN;
X }
X putc (';', asmfile);
X CHARS (1);
X break;
X
X case POINTER_TYPE:
X putc ('*', asmfile);
X CHARS (1);
X dbxout_type (TREE_TYPE (type), 0);
X break;
X
X case METHOD_TYPE:
X if (use_gdb_dbx_extensions)
X {
X putc ('#', asmfile);
X CHARS (1);
X dbxout_type (TYPE_METHOD_BASETYPE (type), 0);
X putc (',', asmfile);
X CHARS (1);
X dbxout_type (TREE_TYPE (type), 0);
X dbxout_args (TYPE_ARG_TYPES (type));
X putc (';', asmfile);
X CHARS (1);
X }
X else
X {
X /* Should print as an int, because it is really
X just an offset. */
X dbxout_type (integer_type_node, 0);
X }
X break;
X
X case OFFSET_TYPE:
X if (use_gdb_dbx_extensions)
X {
X putc ('@', asmfile);
X CHARS (1);
X dbxout_type (TYPE_OFFSET_BASETYPE (type), 0);
X putc (',', asmfile);
X CHARS (1);
X dbxout_type (TREE_TYPE (type), 0);
X }
X else
X {
X /* Should print as an int, because it is really
X just an offset. */
X dbxout_type (integer_type_node, 0);
X }
X break;
X
X case REFERENCE_TYPE:
X putc (use_gdb_dbx_extensions ? '&' : '*', asmfile);
X CHARS (1);
X dbxout_type (TREE_TYPE (type), 0);
X break;
X
X case FUNCTION_TYPE:
X putc ('f', asmfile);
X CHARS (1);
X dbxout_type (TREE_TYPE (type), 0);
X break;
X
X default:
X abort ();
X }
X}
X
X/* Output the name of type TYPE, with no punctuation.
X Such names can be set up either by typedef declarations
X or by struct, enum and union tags. */
X
Xstatic void
Xdbxout_type_name (type)
X register tree type;
X{
X tree t;
X if (TYPE_NAME (type) == 0)
X abort ();
X if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
X {
X t = TYPE_NAME (type);
X }
X else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
X {
X t = DECL_NAME (TYPE_NAME (type));
X }
X else
X abort ();
X
X fprintf (asmfile, "%s", IDENTIFIER_POINTER (t));
X CHARS (IDENTIFIER_LENGTH (t));
X}
X
X/* Output a .stabs for the symbol defined by DECL,
X which must be a ..._DECL node in the normal namespace.
X It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL.
X LOCAL is nonzero if the scope is less than the entire file. */
X
Xvoid
Xdbxout_symbol (decl, local)
X tree decl;
X int local;
X{
X int letter = 0;
X tree type = TREE_TYPE (decl);
X char *name;
X
X /* If global, first output all types and all
X struct, enum and union tags that have been created
X and not yet output. */
X
X if (local == 0)
X {
X /* Send out the types first. */
X dbxout_types (get_permanent_types ());
X dbxout_tags (gettags ());
X }
X
X current_sym_code = 0;
X current_sym_value = 0;
X current_sym_addr = 0;
X
X /* The output will always start with the symbol name,
X so count that always in the length-output-so-far. */
X
X if (DECL_NAME (decl) == 0)
X return;
X
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl));
X
X switch (TREE_CODE (decl))
X {
X case CONST_DECL:
X /* Enum values are defined by defining the enum type. */
X break;
X
X case FUNCTION_DECL:
X if (DECL_RTL (decl) == 0)
X return;
X if (TREE_EXTERNAL (decl))
X break;
X if (GET_CODE (DECL_RTL (decl)) != MEM
X || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
X break;
X FORCE_TEXT;
X fprintf (asmfile, ".stabs \"%s:%c",
X IDENTIFIER_POINTER (DECL_NAME (decl)),
X TREE_PUBLIC (decl) ? 'F' : 'f');
X
X current_sym_code = N_FUN;
X current_sym_addr = XEXP (DECL_RTL (decl), 0);
X
X if (TREE_TYPE (TREE_TYPE (decl)))
X dbxout_type (TREE_TYPE (TREE_TYPE (decl)), 0);
X else
X dbxout_type (void_type_node, 0);
X dbxout_finish_symbol ();
X break;
X
X case TYPE_DECL:
X#if 0
X /* This seems all wrong. Outputting most kinds of types gives no name
X at all. A true definition gives no name; a cross-ref for a
X structure can give the tag name, but not a type name.
X It seems that no typedef name is defined by outputting a type. */
X
X /* If this typedef name was defined by outputting the type,
X don't duplicate it. */
X if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED
X && TYPE_NAME (TREE_TYPE (decl)) == decl)
X return;
X#endif
X /* Don't output the same typedef twice.
X And don't output what language-specific stuff doesn't want output. */
X if (TREE_ASM_WRITTEN (decl)
X || lang_output_debug_info (TREE_TYPE (decl)) == 0)
X return;
X
X /* Output typedef name. */
X FORCE_TEXT;
X fprintf (asmfile, ".stabs \"%s:t",
X IDENTIFIER_POINTER (DECL_NAME (decl)));
X
X current_sym_code = N_LSYM;
X
X dbxout_type (TREE_TYPE (decl), 1);
X dbxout_finish_symbol ();
X
X /* Prevent duplicate output of a typedef. */
X TREE_ASM_WRITTEN (decl) = 1;
X break;
X
X case PARM_DECL:
X /* Parm decls go in their own separate chains
X and are output by dbxout_reg_parms and dbxout_parms. */
X abort ();
X
X case RESULT_DECL:
X /* Named return value, treat like a VAR_DECL. */
X case VAR_DECL:
X if (DECL_RTL (decl) == 0)
X return;
X /* Don't mention a variable that is external.
X Let the file that defines it describe it. */
X if (TREE_EXTERNAL (decl))
X break;
X
X /* If the variable is really a constant, inform dbx of such. */
X if (TREE_STATIC (decl) && TREE_READONLY (decl)
X && DECL_INITIAL (decl) != 0
X && (DECL_FIELD_CONTEXT (decl) == NULL_TREE
X || TREE_CODE (DECL_FIELD_CONTEXT (decl)) == LET_STMT))
X {
X if (TREE_PUBLIC (decl) == 0)
X {
X /* The sun4 assembler does not grok this. */
X name = IDENTIFIER_POINTER (DECL_NAME (decl));
X if (TREE_CODE (TREE_TYPE (decl)) == INTEGER_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
X {
X int ival = TREE_INT_CST_LOW (DECL_INITIAL (decl));
X fprintf (asmfile, ".stabs \"%s:c=i%d\",0x%x,0,0,0\n",
X name, ival, N_LSYM);
X return;
X }
X else if (TREE_CODE (TREE_TYPE (decl)) == REAL_TYPE)
X {
X /* don't know how to do this yet. */
X }
X break;
X }
X /* else it is something we handle like a normal variable. */
X }
X
X /* Don't mention a variable at all
X if it was completely optimized into nothingness. */
X if (GET_CODE (DECL_RTL (decl)) == REG
X && (REGNO (DECL_RTL (decl)) < 0
X || REGNO (DECL_RTL (decl)) >= FIRST_PSEUDO_REGISTER))
X break;
X
X /* The kind-of-variable letter depends on where
X the variable is and on the scope of its name:
X G and N_GSYM for static storage and global scope,
X S for static storage and file scope,
X V for static storage and local scope,
X for those two, use N_LCSYM if data is in bss segment,
X N_STSYM if in data segment, N_FUN otherwise.
X (We used N_FUN originally, then changed to N_STSYM
X to please GDB. However, it seems that confused ld.
X Now GDB has been fixed to like N_FUN, says Kingdon.)
X no letter at all, and N_LSYM, for auto variable,
X r and N_RSYM for register variable. */
X
X if (GET_CODE (DECL_RTL (decl)) == MEM
X && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF)
X {
X if (TREE_PUBLIC (decl))
X {
X letter = 'G';
X current_sym_code = N_GSYM;
X }
X else
X {
X current_sym_addr = XEXP (DECL_RTL (decl), 0);
X
X letter = TREE_PERMANENT (decl) ? 'S' : 'V';
X
X if (!DECL_INITIAL (decl))
X current_sym_code = N_LCSYM;
X else if (TREE_READONLY (decl) && ! TREE_VOLATILE (decl))
X /* This is not quite right, but it's the closest
X of all the codes that Unix defines. */
X current_sym_code = N_FUN;
X else
X current_sym_code = N_STSYM;
X }
X }
X else if (GET_CODE (DECL_RTL (decl)) == REG)
X {
X letter = 'r';
X current_sym_code = N_RSYM;
X current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (decl)));
X }
X else if (GET_CODE (DECL_RTL (decl)) == MEM
X && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
X || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG
X && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM)))
X /* If the value is indirect by memory or by a register
X that isn't the frame pointer
X then it means the object is variable-sized and address through
X that register or stack slot. DBX has no way to represent this
X so all we can do is output the variable as a pointer.
X If it's not a parameter, ignore it.
X (VAR_DECLs like this can be made by integrate.c.) */
X {
X if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG)
X {
X letter = 'r';
X current_sym_code = N_RSYM;
X current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (DECL_RTL (decl), 0)));
X }
X else
X {
X current_sym_code = N_LSYM;
X /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))).
X We want the value of that CONST_INT. */
X current_sym_value = INTVAL (XEXP (XEXP (XEXP (DECL_RTL (decl), 0), 0), 1));
X }
X
X /* Effectively do build_pointer_type, but don't cache this type,
X since it might be temporary whereas the type it points to
X might have been saved for inlining. */
X type = make_node (POINTER_TYPE);
X TREE_TYPE (type) = TREE_TYPE (decl);
X }
X else if (GET_CODE (DECL_RTL (decl)) == MEM
X && GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG)
X {
X current_sym_code = N_LSYM;
X current_sym_value = 0;
X }
X else if (GET_CODE (DECL_RTL (decl)) == MEM
X && GET_CODE (XEXP (DECL_RTL (decl), 0)) == PLUS
X && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 1)) == CONST_INT)
X {
X current_sym_code = N_LSYM;
X /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...)))
X We want the value of that CONST_INT. */
X current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (decl), 0), 1));
X }
X else
X /* Address might be a MEM, when DECL is a variable-sized object.
X Or it might be const0_rtx, meaning previous passes
X want us to ignore this variable. */
X break;
X
X /* Ok, start a symtab entry and output the variable name. */
X FORCE_TEXT;
X /* One slight hitch: if this is a VAR_DECL which is a static
X class member, we must put out the mangled name instead of the
X DECL_NAME. */
X /* Note also that static member (variable) names DO NOT begin
X with underscores in .stabs directives. */
X if (DECL_LANG_SPECIFIC (decl))
X {
X name = DECL_ASSEMBLER_NAME (decl);
X
X /* Adding 1 here only works on systems
X which flush an initial underscore. */
X if (name[0] == '_')
X name += 1;
X }
X else name = IDENTIFIER_POINTER (DECL_NAME (decl));
X
X fprintf (asmfile, ".stabs \"%s:", name);
X if (letter) putc (letter, asmfile);
X dbxout_type (type, 0);
X dbxout_finish_symbol ();
X break;
X }
X}
X
Xstatic void
Xdbxout_finish_symbol ()
X{
X fprintf (asmfile, "\",%d,0,0,", current_sym_code);
X if (current_sym_addr)
X output_addr_const (asmfile, current_sym_addr);
X else
X fprintf (asmfile, "%d", current_sym_value);
X putc ('\n', asmfile);
X}
X
X/* Output definitions of all the decls in a chain. */
X
Xstatic void
Xdbxout_syms (syms)
X tree syms;
X{
X while (syms)
X {
X dbxout_symbol (syms, 1);
X syms = TREE_CHAIN (syms);
X }
X}
X
X/* The following two functions output definitions of function parameters.
X Each parameter gets a definition locating it in the parameter list.
X Each parameter that is a register variable gets a second definition
X locating it in the register.
X
X Printing or argument lists in gdb uses the definitions that
X locate in the parameter list. But reference to the variable in
X expressions uses preferentially the definition as a register. */
X
X/* Output definitions, referring to storage in the parmlist,
X of all the parms in PARMS, which is a chain of PARM_DECL nodes. */
X
Xstatic void
Xdbxout_parms (parms)
X tree parms;
X{
X for (; parms; parms = TREE_CHAIN (parms))
X {
X if (DECL_OFFSET (parms) >= 0)
X {
X current_sym_code = N_PSYM;
X current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT;
X current_sym_addr = 0;
X
X FORCE_TEXT;
X if (DECL_NAME (parms))
X {
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
X
X fprintf (asmfile, ".stabs \"%s:p",
X IDENTIFIER_POINTER (DECL_NAME (parms)));
X }
X else
X {
X current_sym_nchars = 8;
X fprintf (asmfile, ".stabs \"(anon):p");
X }
X
X if (GET_CODE (DECL_RTL (parms)) == REG
X && REGNO (DECL_RTL (parms)) >= 0
X && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
X dbxout_type (DECL_ARG_TYPE (parms), 0);
X else
X {
X /* This is the case where the parm is passed as an int or double
X and it is converted to a char, short or float and stored back
X in the parmlist. In this case, describe the parm
X with the variable's declared type, and adjust the address
X if the least significant bytes (which we are using) are not
X the first ones. */
X#ifdef BYTES_BIG_ENDIAN
X if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
X current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
X - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
X#endif
X
X if (GET_CODE (DECL_RTL (parms)) == MEM
X && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
X && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
X && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value)
X dbxout_type (TREE_TYPE (parms), 0);
X else
X {
X current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT;
X dbxout_type (DECL_ARG_TYPE (parms), 0);
X }
X }
X dbxout_finish_symbol ();
X }
X /* Parm was passed in registers.
X If it lives in a hard register, output a "regparm" symbol
X for the register it lives in. */
X else if (GET_CODE (DECL_RTL (parms)) == REG
X && REGNO (DECL_RTL (parms)) >= 0
X && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
X {
X current_sym_code = N_RSYM;
X current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)));
X current_sym_addr = 0;
X
X FORCE_TEXT;
X if (DECL_NAME (parms))
X {
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
X fprintf (asmfile, ".stabs \"%s:P",
X IDENTIFIER_POINTER (DECL_NAME (parms)));
X }
X else
X {
X current_sym_nchars = 8;
X fprintf (asmfile, ".stabs \"(anon):P");
X }
X
X dbxout_type (DECL_ARG_TYPE (parms), 0);
X dbxout_finish_symbol ();
X }
X else if (GET_CODE (DECL_RTL (parms)) == MEM
X && XEXP (DECL_RTL (parms), 0) != const0_rtx)
X {
X current_sym_code = N_LSYM;
X /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))).
X We want the value of that CONST_INT. */
X current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
X current_sym_addr = 0;
X
X FORCE_TEXT;
X if (DECL_NAME (parms))
X {
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
X fprintf (asmfile, ".stabs \"%s:p",
X IDENTIFIER_POINTER (DECL_NAME (parms)));
X }
X else
X {
X current_sym_nchars = 8;
X fprintf (asmfile, ".stabs \"(anon):p");
X }
X
X#if 0 /* This is actually the case in which a parameter
X is passed in registers but lives on the stack in a local slot.
X The address we are using is already correct, so don't change it. */
X
X /* This is the case where the parm is passed as an int or double
X and it is converted to a char, short or float and stored back
X in the parmlist. In this case, describe the parm
X with the variable's declared type, and adjust the address
X if the least significant bytes (which we are using) are not
X the first ones. */
X#ifdef BYTES_BIG_ENDIAN
X if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
X current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
X - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
X#endif
X#endif /* 0 */
X
X dbxout_type (TREE_TYPE (parms), 0);
X dbxout_finish_symbol ();
X }
X }
X}
X
X/* Output definitions, referring to registers,
X of all the parms in PARMS which are stored in registers during the function.
X PARMS is a chain of PARM_DECL nodes. */
X
Xstatic void
Xdbxout_reg_parms (parms)
X tree parms;
X{
X while (parms)
X {
X /* Report parms that live in registers during the function. */
X if (GET_CODE (DECL_RTL (parms)) == REG
X && REGNO (DECL_RTL (parms)) >= 0
X && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER
X && DECL_OFFSET (parms) >= 0)
X {
X current_sym_code = N_RSYM;
X current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)));
X current_sym_addr = 0;
X
X FORCE_TEXT;
X if (DECL_NAME (parms))
X {
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
X fprintf (asmfile, ".stabs \"%s:r",
X IDENTIFIER_POINTER (DECL_NAME (parms)));
X }
X else
X {
X current_sym_nchars = 8;
X fprintf (asmfile, ".stabs \"(anon):r");
X }
X dbxout_type (TREE_TYPE (parms), 0);
X dbxout_finish_symbol ();
X }
X /* Report parms that live in memory but outside the parmlist. */
X else if (GET_CODE (DECL_RTL (parms)) == MEM
X && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
X && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT)
X {
X int offset = DECL_OFFSET (parms) / BITS_PER_UNIT;
X /* A parm declared char is really passed as an int,
X so it occupies the least significant bytes.
X On a big-endian machine those are not the low-numbered ones. */
X#ifdef BYTES_BIG_ENDIAN
X if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
X offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
X - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
X#endif
X if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset)
X {
X current_sym_code = N_LSYM;
X current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
X current_sym_addr = 0;
X FORCE_TEXT;
X if (DECL_NAME (parms))
X {
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
X fprintf (asmfile, ".stabs \"%s:",
X IDENTIFIER_POINTER (DECL_NAME (parms)));
X }
X else
X {
X current_sym_nchars = 8;
X fprintf (asmfile, ".stabs \"(anon):");
X }
X dbxout_type (TREE_TYPE (parms), 0);
X dbxout_finish_symbol ();
X }
X }
X parms = TREE_CHAIN (parms);
X }
X}
X
X/* Given a chain of ..._TYPE nodes (as come in a parameter list),
X output definitions of those names, in raw form */
X
Xvoid
Xdbxout_args (args)
X tree args;
X{
X while (args)
X {
X putc (',', asmfile);
X dbxout_type (TREE_VALUE (args), 0);
X CHARS (1);
X args = TREE_CHAIN (args);
X }
X}
X
X/* Given a chain of ..._TYPE nodes,
X find those which have typedef names and output those names.
X This is to ensure those types get output. */
X
Xvoid
Xdbxout_types (types)
X register tree types;
X{
X while (types)
X {
X if (TYPE_NAME (types)
X && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL
X && ! TREE_ASM_WRITTEN (TYPE_NAME (types)))
X dbxout_symbol (TYPE_NAME (types), 1);
X types = TREE_CHAIN (types);
X }
X}
X
X/* Output the tags (struct, union and enum definitions with names) for a block,
X given a list of them (a chain of TREE_LIST nodes) in TAGS.
X We must check to include those that have been mentioned already with
X only a cross-reference. */
X
Xvoid
Xdbxout_tags (tags)
X tree tags;
X{
X register tree link;
X for (link = tags; link; link = TREE_CHAIN (link))
X {
X register tree type = TYPE_MAIN_VARIANT (TREE_VALUE (link));
X if (TREE_PURPOSE (link) != 0
X && ! TREE_ASM_WRITTEN (link)
X && TYPE_SIZE (type) != 0
X && lang_output_debug_info (type))
X {
X TREE_ASM_WRITTEN (link) = 1;
X current_sym_code = N_LSYM;
X current_sym_value = 0;
X current_sym_addr = 0;
X current_sym_nchars = 2 + IDENTIFIER_LENGTH (TREE_PURPOSE (link));
X
X FORCE_TEXT;
X fprintf (asmfile, ".stabs \"%s:T",
X ANON_AGGRNAME_P (TREE_PURPOSE (link)) ? "" : IDENTIFIER_POINTER (TREE_PURPOSE (link)));
X dbxout_type (type, 1);
X dbxout_finish_symbol ();
X
X/* Change by Bryan Boreham, Kewill, Fri Sep 22 16:57:42 1989.
X Added to make sure all fully-output structs have typedefs. */
X
X if (!ANON_AGGRNAME_P (TREE_PURPOSE (link)))
X {
X fprintf (asmfile, ".stabs \"%s:t",
X IDENTIFIER_POINTER (TREE_PURPOSE (link)));
X
X current_sym_code = N_LSYM;
X
X dbxout_type (type, 1);
X dbxout_finish_symbol ();
X }
X }
X }
X}
X
X/* Output everything about a symbol block (that is to say, a LET_STMT node
X that represents a scope level),
X including recursive output of contained blocks.
X
X STMT is the LET_STMT node.
X DEPTH is its depth within containing symbol blocks.
X ARGS is usually zero; but for the outermost block of the
X body of a function, it is a chain of PARM_DECLs for the function parameters.
X We output definitions of all the register parms
X as if they were local variables of that block.
X
X Actually, STMT may be several statements chained together.
X We handle them all in sequence. */
X
Xstatic void
Xdbxout_block (stmt, depth, args)
X register tree stmt;
X int depth;
X tree args;
X{
X int blocknum;
X
X while (stmt)
X {
X switch (TREE_CODE (stmt))
X {
X case COMPOUND_STMT:
X case LOOP_STMT:
X dbxout_block (STMT_BODY (stmt), depth, 0);
X break;
X
X case IF_STMT:
X dbxout_block (STMT_THEN (stmt), depth, 0);
X dbxout_block (STMT_ELSE (stmt), depth, 0);
X break;
X
X case LET_STMT:
X /* Ignore LET_STMTs for blocks never really used to make RTL. */
X if (! TREE_USED (stmt))
X break;
X /* In dbx format, the syms of a block come before the N_LBRAC. */
X dbxout_tags (STMT_TYPE_TAGS (stmt));
X dbxout_syms (STMT_VARS (stmt));
X if (args)
X dbxout_reg_parms (args);
X
X /* Now output an N_LBRAC symbol to represent the beginning of
X the block. Use the block's tree-walk order to generate
X the assembler symbols LBBn and LBEn
X that final will define around the code in this block. */
X if (depth > 0)
X {
X char buf[20];
X blocknum = next_block_number++;
X ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum);
X fprintf (asmfile, ".stabn %d,0,0,", N_LBRAC);
X assemble_name (asmfile, buf);
X fprintf (asmfile, "\n");
X }
X
X /* Output the subblocks. */
X dbxout_block (STMT_SUBBLOCKS (stmt), depth + 1, 0);
X
X /* Refer to the marker for the end of the block. */
X if (depth > 0)
X {
X char buf[20];
X ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum);
X fprintf (asmfile, ".stabn %d,0,0,", N_RBRAC);
X assemble_name (asmfile, buf);
X fprintf (asmfile, "\n");
X }
X }
X stmt = TREE_CHAIN (stmt);
X }
X}
X
X/* Output dbx data for a function definition.
X This includes a definition of the function name itself (a symbol),
X definitions of the parameters (locating them in the parameter list)
X and then output the block that makes up the function's body
X (including all the auto variables of the function). */
X
Xvoid
Xdbxout_function (decl)
X tree decl;
X{
X extern tree value_identifier;
X
X dbxout_symbol (decl, 0);
X dbxout_parms (DECL_ARGUMENTS (decl));
X if (DECL_NAME (DECL_RESULT (decl)) != value_identifier)
X dbxout_symbol (DECL_RESULT (decl), 1);
X dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl));
X
X /* If we made any temporary types in this fn that weren't
X output, output them now. */
X dbxout_types (get_temporary_types ());
X}
X
X/* GNU C++ extensions. */
X
X/* At the start of the file, emit symbolic information to orient
X GDB for this particular file's exception handling implementation.
X EH_TYPE is the type name of the exception type.
X EH_DECL is the global root of the exception handler stack. */
X
Xvoid
Xdbxout_eh_init (eh_type, eh_decl)
X tree eh_type, eh_decl;
X{
X}
X
X#else /* not DBX_DEBUGGING_INFO */
X
Xvoid
Xdbxout_init (asm_file, input_file_name)
X FILE *asm_file;
X char *input_file_name;
X{}
X
Xvoid
Xdbxout_symbol (decl, local)
X tree decl;
X int local;
X{}
X
Xvoid
Xdbxout_types (types)
X register tree types;
X{}
X
Xvoid
Xdbxout_tags (tags)
X tree tags;
X{}
X
Xvoid
Xdbxout_function (decl)
X tree decl;
X{}
X
X#endif /* DBX_DEBUGGING_INFO */
!EOF
echo "Extracting lastfile.c..."
sed 's/^X//' >lastfile.c << '!EOF'
X/* Mark end of data space to dump as pure, for GNU Emacs.
X Copyright (C) 1985 Free Software Foundation, Inc.
X
XThis file is part of GNU Emacs.
X
XGNU Emacs is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY. No author or distributor
Xaccepts responsibility to anyone for the consequences of using it
Xor for whether it serves any particular purpose or works at all,
Xunless he says so in writing. Refer to the GNU Emacs General Public
XLicense for full details.
X
XEveryone is granted permission to copy, modify and redistribute
XGNU Emacs, but only under the conditions described in the
XGNU Emacs General Public License. A copy of this license is
Xsupposed to have been given to you along with GNU Emacs so you
Xcan know your rights and responsibilities. It should be in a
Xfile named COPYING. Among other things, the copyright notice
Xand this notice must be preserved on all copies. */
X
X
X/* How this works:
X
X Fdump_emacs dumps everything up to my_edata as text space (pure).
X
X The files of Emacs are written so as to have no initialized
X data that can ever need to be altered except at the first startup.
X This is so that those words can be dumped as sharable text.
X
X It is not possible to exercise such control over library files.
X So it is necessary to refrain from making their data areas shared.
X Therefore, this file is loaded following all the files of Emacs
X but before library files.
X As a result, the symbol my_edata indicates the point
X in data space between data coming from Emacs and data
X coming from libraries.
X*/
X
Xchar my_edata = 0;
!EOF
echo "Extracting version.c..."
sed 's/^X//' >version.c << '!EOF'
Xchar *version_string = "1.40.3 (based on GCC 1.40)";
!EOF
echo "Extracting cplus-dem.c..."
sed 's/^X//' >cplus-dem.c << '!EOF'
X/* Demangler for GNU C++
X Copyright (C) 1989 Free Software Foundation, Inc.
X written by James Clark ([email protected])
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* This is for g++ 1.36.1 (November 6 version). It will probably
X require changes for any other version.
X
X Modified for g++ 1.36.2 (November 18 version). */
X
X/* This file exports one function
X
X char *cplus_demangle (const char *name)
X
X If `name' is a mangled function name produced by g++, then
X a pointer to a malloced string giving a C++ representation
X of the name will be returned; otherwise NULL will be returned.
X It is the caller's responsibility to free the string which
X is returned.
X
X For example,
X
X cplus_demangle ("_foo__1Ai")
X
X returns
X
X "A::foo(int)"
X
X This file imports xmalloc and xrealloc, which are like malloc and
X realloc except that they generate a fatal error if there is no
X available memory. */
X
X/* #define nounderscore 1 /* define this is names don't start with _ */
X
X#include
X#include
X
X#ifdef USG
X#include
X#include
X#else
X#include
X#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n))
X#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n))
X#define strchr index
X#define strrchr rindex
X#endif
X
X#ifndef __STDC__
X#define const
X#endif
X
X#ifdef __STDC__
Xextern char *cplus_demangle (const char *type);
X#else
Xextern char *cplus_demangle ();
X#endif
X
X#ifdef __STDC__
Xextern char *xmalloc (int);
Xextern char *xrealloc (char *, int);
X#else
Xextern char *xmalloc ();
Xextern char *xrealloc ();
X#endif
X
Xstatic char **typevec = 0;
Xstatic int ntypes = 0;
Xstatic int typevec_size = 0;
X
Xstatic struct {
X const char *in;
X const char *out;
X} optable[] = {
X "new", " new",
X "delete", " delete",
X "ne", "!=",
X "eq", "==",
X "ge", ">=",
X "gt", ">",
X "le", "<=",
X "lt", "<",
X "plus", "+",
X "minus", "-",
X "mult", "*",
X "convert", "+", /* unary + */
X "negate", "-", /* unary - */
X "trunc_mod", "%",
X "trunc_div", "/",
X "truth_andif", "&&",
X "truth_orif", "||",

X "truth_not", "!",
X "postincrement", "++",
X "postdecrement", "--",
X "bit_ior", "|",
X "bit_xor", "^",
X "bit_and", "&",
X "bit_not", "~",
X "call", "()",
X "cond", "?:",
X "alshift", "<<",
X "arshift", ">>",
X "component", "->",
X "indirect", "*",
X "method_call", "->()",
X "addr", "&", /* unary & */
X "array", "[]",
X "nop", "", /* for operator= */
X};
X
X/* Beware: these aren't '\0' terminated. */
X
Xtypedef struct {
X char *b; /* pointer to start of string */
X char *p; /* pointer after last character */
X char *e; /* pointer after end of allocated space */
X} string;
X
X#ifdef __STDC__
Xstatic void string_need (string *s, int n);
Xstatic void string_delete (string *s);
Xstatic void string_init (string *s);
Xstatic void string_clear (string *s);
Xstatic int string_empty (string *s);
Xstatic void string_append (string *p, const char *s);
Xstatic void string_appends (string *p, string *s);
Xstatic void string_appendn (string *p, const char *s, int n);
Xstatic void string_prepend (string *p, const char *s);
X#if 0
Xstatic void string_prepends (string *p, string *s);
X#endif
Xstatic void string_prependn (string *p, const char *s, int n);
Xstatic int get_count (const char **type, int *count);
Xstatic int do_args (const char **type, string *decl);
Xstatic int do_type (const char **type, string *result);
Xstatic int do_arg (const char **type, string *result);
Xstatic int do_args (const char **type, string *decl);
Xstatic void munge_function_name (string *name);
Xstatic void remember_type (const char *type, int len);
X#else
Xstatic void string_need ();
Xstatic void string_delete ();
Xstatic void string_init ();
Xstatic void string_clear ();
Xstatic int string_empty ();
Xstatic void string_append ();
Xstatic void string_appends ();
Xstatic void string_appendn ();
Xstatic void string_prepend ();
Xstatic void string_prepends ();
Xstatic void string_prependn ();
Xstatic int get_count ();
Xstatic int do_args ();
Xstatic int do_type ();
Xstatic int do_arg ();
Xstatic int do_args ();
Xstatic void munge_function_name ();
Xstatic void remember_type ();
X#endif
X
Xchar *
Xcplus_demangle (type)
X const char *type;
X{
X string decl;
X int n;
X int success = 0;
X int constructor = 0;
X int const_flag = 0;
X int i;
X const char *p;
X#ifndef LONGERNAMES
X const char *premangle;
X#endif
X
X if (type == NULL || *type == '\0')
X return NULL;
X#ifndef nounderscore
X if (*type++ != '_')
X return NULL;
X#endif
X p = type;
X while (*p != '\0' && !(*p == '_' && p[1] == '_'))
X p++;
X if (*p == '\0')
X {
X /* destructor */
X if (type[0] == '_' && type[1] == '$' && type[2] == '_')
X {
X int n = (strlen (type) - 3)*2 + 3 + 2 + 1;
X char *tem = (char *) xmalloc (n);
X strcpy (tem, type + 3);
X strcat (tem, "::~");
X strcat (tem, type + 3);
X strcat (tem, "()");
X return tem;
X }
X /* static data member */
X if (*type != '_' && (p = strchr (type, '$')) != NULL)
X {
X int n = strlen (type) + 2;
X char *tem = (char *) xmalloc (n);
X memcpy (tem, type, p - type);
X strcpy (tem + (p - type), "::");
X strcpy (tem + (p - type) + 2, p + 1);
X return tem;
X }
X /* virtual table */
X if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$')
X {
X int n = strlen (type + 4) + 14 + 1;
X char *tem = (char *) xmalloc (n);
X strcpy (tem, type + 4);
X strcat (tem, " virtual table");
X return tem;
X }
X return NULL;
X }
X
X string_init (&decl);
X
X if (p == type)
X {
X if (!isdigit (p[2]))
X {
X string_delete (&decl);
X return NULL;
X }
X constructor = 1;
X }
X else
X {
X string_appendn (&decl, type, p - type);
X munge_function_name (&decl);
X }
X p += 2;
X
X#ifndef LONGERNAMES
X premangle = p;
X#endif
X switch (*p)
X {
X case 'C':
X /* a const member function */
X if (!isdigit (p[1]))
X {
X string_delete (&decl);
X return NULL;
X }
X p += 1;
X const_flag = 1;
X /* fall through */
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X n = 0;
X do
X {
X n *= 10;
X n += *p - '0';
X p += 1;
X }
X while (isdigit (*p));
X if (strlen (p) < n)
X {
X string_delete (&decl);
X return NULL;
X }
X if (constructor)
X {
X string_appendn (&decl, p, n);
X string_append (&decl, "::");
X string_appendn (&decl, p, n);
X }
X else
X {
X string_prepend (&decl, "::");
X string_prependn (&decl, p, n);
X }
X p += n;
X#ifndef LONGERNAMES
X remember_type (premangle, p - premangle);
X#endif
X success = do_args (&p, &decl);
X if (const_flag)
X string_append (&decl, " const");
X break;
X case 'F':
X p += 1;
X success = do_args (&p, &decl);
X break;
X }
X
X for (i = 0; i < ntypes; i++)
X if (typevec[i] != NULL)
X free (typevec[i]);
X ntypes = 0;
X if (typevec != NULL)
X {
X free ((char *)typevec);
X typevec = NULL;
X typevec_size = 0;
X }
X
X if (success)
X {
X string_appendn (&decl, "", 1);
X return decl.b;
X }
X else
X {
X string_delete (&decl);
X return NULL;
X }
X}
X
Xstatic int
Xget_count (type, count)
X const char **type;
X int *count;
X{
X if (!isdigit (**type))
X return 0;
X *count = **type - '0';
X *type += 1;
X /* see flush_repeats in cplus-method.c */
X if (isdigit (**type))
X {
X const char *p = *type;
X int n = *count;
X do
X {
X n *= 10;
X n += *p - '0';
X p += 1;
X }
X while (isdigit (*p));
X if (*p == '_')
X {
X *type = p + 1;
X *count = n;
X }
X }
X return 1;
X}
X
X/* result will be initialised here; it will be freed on failure */
X
Xstatic int
Xdo_type (type, result)
X const char **type;
X string *result;
X{
X int n;
X int done;
X int non_empty = 0;
X int success;
X string decl;
X const char *remembered_type;
X
X string_init (&decl);
X string_init (result);
X
X done = 0;
X success = 1;
X while (success && !done)
X {
X int member;
X switch (**type)
X {
X case 'P':
X *type += 1;
X string_prepend (&decl, "*");
X break;
X
X case 'R':
X *type += 1;
X string_prepend (&decl, "&");
X break;
X
X case 'T':
X *type += 1;
X if (!get_count (type, &n) || n >= ntypes)
X success = 0;
X else
X {
X remembered_type = typevec[n];
X type = &remembered_type;
X }
X break;
X
X case 'F':
X *type += 1;
X if (!string_empty (&decl) && decl.b[0] == '*')
X {
X string_prepend (&decl, "(");
X string_append (&decl, ")");
X }
X if (!do_args (type, &decl) || **type != '_')
X success = 0;
X else
X *type += 1;
X break;
X
X case 'M':
X case 'O':
X {
X int constp = 0;
X int volatilep = 0;
X
X member = **type == 'M';
X *type += 1;
X if (!isdigit (**type))
X {
X success = 0;
X break;
X }
X n = 0;
X do
X {
X n *= 10;
X n += **type - '0';
X *type += 1;
X }
X while (isdigit (**type));
X if (strlen (*type) < n)
X {
X success = 0;
X break;
X }
X string_append (&decl, ")");
X string_prepend (&decl, "::");
X string_prependn (&decl, *type, n);
X string_prepend (&decl, "(");
X *type += n;
X if (member)
X {
X if (**type == 'C')
X {
X *type += 1;
X constp = 1;
X }
X if (**type == 'V')
X {
X *type += 1;
X volatilep = 1;
X }
X if (*(*type)++ != 'F')
X {
X success = 0;
X break;
X }
X }
X if ((member && !do_args (type, &decl)) || **type != '_')
X {
X success = 0;
X break;
X }
X *type += 1;
X if (constp)
X {
X if (non_empty)
X string_append (&decl, " ");
X else
X non_empty = 1;
X string_append (&decl, "const");
X }
X if (volatilep)
X {
X if (non_empty)
X string_append (&decl, " ");
X else
X non_empty = 1;
X string_append (&decl, "volatilep");
X }
X break;
X }
X
X case 'C':
X if ((*type)[1] == 'P')
X {
X *type += 1;
X if (!string_empty (&decl))
X string_prepend (&decl, " ");
X string_prepend (&decl, "const");
X break;
X }
X
X /* fall through */
X default:
X done = 1;
X break;
X }
X }
X
X done = 0;
X non_empty = 0;
X while (success && !done)
X {
X switch (**type)
X {
X case 'C':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X else
X non_empty = 1;
X string_append (result, "const");
X break;
X case 'U':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X else
X non_empty = 1;
X string_append (result, "unsigned");
X break;
X case 'V':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X else
X non_empty = 1;
X string_append (result, "volatile");
X break;
X default:
X done = 1;
X break;
X }
X }
X
X if (success)
X switch (**type)
X {
X case '\0':
X case '_':
X break;
X case 'v':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "void");
X break;
X case 'x':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "long long");
X break;
X case 'l':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "long");
X break;
X case 'i':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "int");
X break;
X case 's':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "short");
X break;
X case 'c':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "char");
X break;
X case 'r':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "long double");
X break;
X case 'd':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "double");
X break;
X case 'f':
X *type += 1;
X if (non_empty)
X string_append (result, " ");
X string_append (result, "float");
X break;
X case 'G':
X *type += 1;
X if (!isdigit (**type))
X {
X success = 0;
X break;
X }
X /* fall through */
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X n = 0;
X do
X {
X n *= 10;
X n += **type - '0';
X *type += 1;
X }
X while (isdigit (**type));
X if (strlen (*type) < n)
X {
X success = 0;
X break;
X }
X if (non_empty)
X string_append (result, " ");
X string_appendn (result, *type, n);
X *type += n;
X break;
X default:
X success = 0;
X break;
X }
X
X if (success)
X {
X if (!string_empty (&decl))
X {
X string_append (result, " ");
X string_appends (result, &decl);
X }
X string_delete (&decl);
X return 1;
X }
X else
X {
X string_delete (&decl);
X string_delete (result);
X return 0;
X }
X}
X
X/* `result' will be initialised in do_type; it will be freed on failure */
X
Xstatic int
Xdo_arg (type, result)
X const char **type;
X string *result;
X{
X const char *start = *type;
X
X if (!do_type (type, result))
X return 0;
X remember_type (start, *type - start);
X return 1;
X}
X
Xstatic void
Xremember_type (start, len)
X const char *start;
X int len;
X{
X char *tem;
X
X if (ntypes >= typevec_size)
X {
X if (typevec_size == 0)
X {
X typevec_size = 3;
X typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
X }
X else
X {
X typevec_size *= 2;
X typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
X }
X }
X tem = (char *) xmalloc (len + 1);
X memcpy (tem, start, len);
X tem[len] = '\0';
X typevec[ntypes++] = tem;
X}
X
X/* `decl' must be already initialised, usually non-empty;
X it won't be freed on failure */
X
Xstatic int
Xdo_args (type, decl)
X const char **type;
X string *decl;
X{
X string arg;
X int need_comma = 0;
X
X string_append (decl, "(");
X
X while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
X {
X if (**type == 'N')
X {
X int r;
X int t;
X *type += 1;
X if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
X return 0;
X while (--r >= 0)
X {
X const char *tem = typevec[t];
X if (need_comma)
X string_append (decl, ", ");
X if (!do_arg (&tem, &arg))
X return 0;
X string_appends (decl, &arg);
X string_delete (&arg);
X need_comma = 1;
X }
X }
X else
X {
X if (need_comma)
X string_append (decl, ", ");
X if (!do_arg (type, &arg))
X return 0;
X string_appends (decl, &arg);
X string_delete (&arg);
X need_comma = 1;
X }
X }
X
X if (**type == 'v')
X *type += 1;
X else if (**type == 'e')
X {
X *type += 1;
X if (need_comma)
X string_append (decl, ",");
X string_append (decl, "...");
X }
X
X string_append (decl, ")");
X return 1;
X}
X
Xstatic void
Xmunge_function_name (name)
X string *name;
X{
X if (!string_empty (name) && name->p - name->b >= 3
X && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
X {
X int i;
X /* see if it's an assignment expression */
X if (name->p - name->b >= 10 /* op$assign_ */
X && memcmp (name->b + 3, "assign_", 7) == 0)
X {
X for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
X {
X int len = name->p - name->b - 10;
X if (strlen (optable[i].in) == len
X && memcmp (optable[i].in, name->b + 10, len) == 0)
X {
X string_clear (name);
X string_append (name, "operator");
X string_append (name, optable[i].out);
X string_append (name, "=");
X return;
X }
X }
X }
X else
X {
X for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
X {
X int len = name->p - name->b - 3;
X if (strlen (optable[i].in) == len
X && memcmp (optable[i].in, name->b + 3, len) == 0)
X {
X string_clear (name);
X string_append (name, "operator");
X string_append (name, optable[i].out);
X return;
X }
X }
X }
X return;
X }
X else if (!string_empty (name) && name->p - name->b >= 5
X && memcmp (name->b, "type$", 5) == 0)
X {
X /* type conversion operator */
X string type;
X const char *tem = name->b + 5;
X if (do_type (&tem, &type))
X {
X string_clear (name);
X string_append (name, "operator ");
X string_appends (name, &type);

X string_delete (&type);
X return;
X }
X }
X}
X
X/* a mini string-handling package */
X
Xstatic void
Xstring_need (s, n)
X string *s;
X int n;
X{
X if (s->b == NULL)
X {
X if (n < 32)
X n = 32;
X s->p = s->b = (char *) xmalloc (n);
X s->e = s->b + n;
X }
X else if (s->e - s->p < n)
X {
X int tem = s->p - s->b;
X n += tem;
X n *= 2;
X s->b = (char *) xrealloc (s->b, n);
X s->p = s->b + tem;
X s->e = s->b + n;
X }
X}
X
Xstatic void
Xstring_delete (s)
X string *s;
X{
X if (s->b != NULL)
X {
X free (s->b);
X s->b = s->e = s->p = NULL;
X }
X}
X
Xstatic void
Xstring_init (s)
X string *s;
X{
X s->b = s->p = s->e = NULL;
X}
X
Xstatic void
Xstring_clear (s)
X string *s;
X{
X s->p = s->b;
X}
X
Xstatic int
Xstring_empty (s)
X string *s;
X{
X return s->b == s->p;
X}
X
Xstatic void
Xstring_append (p, s)
X string *p;
X const char *s;
X{
X int n;
X if (s == NULL || *s == '\0')
X return;
X n = strlen (s);
X string_need (p, n);
X memcpy (p->p, s, n);
X p->p += n;
X}
X
Xstatic void
Xstring_appends (p, s)
X string *p, *s;
X{
X int n;
X if (s->b == s->p)
X return;
X n = s->p - s->b;
X string_need (p, n);
X memcpy (p->p, s->b, n);
X p->p += n;
X}
X
Xstatic void
Xstring_appendn (p, s, n)
X string *p;
X const char *s;
X int n;
X{
X if (n == 0)
X return;
X string_need (p, n);
X memcpy (p->p, s, n);
X p->p += n;
X}
X
Xstatic void
Xstring_prepend (p, s)
X string *p;
X const char *s;
X{
X if (s == NULL || *s == '\0')
X return;
X string_prependn (p, s, strlen (s));
X}
X
X#if 0
Xstatic void
Xstring_prepends (p, s)
X string *p, *s;
X{
X if (s->b == s->p)
X return;
X string_prependn (p, s->b, s->p - s->b);
X}
X#endif
X
Xstatic void
Xstring_prependn (p, s, n)
X string *p;
X const char *s;
X int n;
X{
X char *q;
X
X if (n == 0)
X return;
X string_need (p, n);
X for (q = p->p - 1; q >= p->b; q--)
X q[n] = q[0];
X memcpy (p->b, s, n);
X p->p += n;
X}
!EOF
echo "Extracting dummy.c..."
sed 's/^X//' >dummy.c << '!EOF'
X/* We need this file, because there is no static version of libdl */
X/* Copyright (C) 1991 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* As a special exception, if you link this file with files
X compiled with GCC to produce an executable, this does not cause
X the resulting executable to be covered by the GNU General Public License.
X This exception does not however invalidate any other reasons why
X the executable file might be covered by the GNU General Public License. */
X
X#include "init_main.h"
X
X/* dummy functions, which can be overwritten by the user */
X/* They are called before and after __init_main();
X i.e. the static initialization */
X
X#ifdef L_init_start
Xvoid INIT_START() {}
X#endif
X
X#ifdef L_init_end
Xvoid INIT_END() {}
X#endif
X
X
X/* in case that there are no global constructors we need these dummies */
X
X#ifdef L_ctor_list
Xep_fp * __CTOR_LIST__ = 0;
X#endif
X
X#ifdef L_dtor_list
Xep_fp * __DTOR_LIST__ = 0;
X#endif
X
X/* dymmy functions needed when using static and dynamic linking */
X
X#ifdef l_dl
X#if defined( sun) && !defined( NO_DYNAMIC_LIBS)
X
X#include
X
Xvoid __eprintf (); /* Defined in gnulib */
X
Xvoid *dlopen( char * path, int mode)
X{
X __eprintf( "ERROR: dlopen must never be called.\n");
X abort();
X}
X
Xchar *dlerror()
X{
X __eprintf( "ERROR: dlerror must never be called.\n");
X abort();
X}
X
Xvoid *dlsym( void * handle, char * symbol)
X{
X __eprintf( "ERROR: dlsym must never be called.\n");
X abort();
X}
X
X#endif
X#endif /* L_dl */
!EOF
echo "Extracting ld.c..."
sed 's/^X//' >ld.c << '!EOF'
X/* Linker `ld' for GNU
X Copyright (C) 1988 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* Written by Richard Stallman with some help from Eric Albert.
X Set, indirect, and warning symbol features added by Randy Smith. */
X
X/* Define how to initialize system-dependent header fields. */
X#ifdef sun
X/* Use Sun's TARGET convention. */
X#ifndef TARGET
X#define SUN2 2
X#define SUN3 3
X#define SUN4 4
X#if defined(sparc)
X#define TARGET SUN4
X#else
X#if defined(mc68020) || defined(m68020)
X#define TARGET SUN3
X#else
X#define TARGET SUN2
X#endif
X#endif
X#else
X#define _CROSS_TARGET_ARCH TARGET /* locate the correct a.out.h file */
X#endif
X#endif
X
X#include
X#include
X#include
X#include
X#include
X#include
X#include
X#ifndef sony_news
X#include
X#endif
X
X#ifdef COFF_ENCAPSULATE
X#include "a.out.encap.h"
X#else
X#include
X#endif
X
X#ifndef N_SET_MAGIC
X#define N_SET_MAGIC(exec, val) ((exec).a_magic = val)
X#endif
X
X/* If compiled with GNU C, use the built-in alloca */
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#endif
X
X/* Always use the GNU version of debugging symbol type codes, if possible. */
X
X#include "stab.h"
X#define CORE_ADDR unsigned long /* For symseg.h */
X#include "symseg.h"
X
X#ifdef USG
X#include
X#else
X#include
X#endif
X
X/* Determine whether we should attempt to handle (minimally)
X N_BINCL and N_EINCL. */
X
X#if defined (__GNU_STAB__) || defined (N_BINCL)
X#define HAVE_SUN_STABS
X#endif
X
X#define min(a,b) ((a) < (b) ? (a) : (b))
X
X/* Macro to control the number of undefined references printed */
X#define MAX_UREFS_PRINTED 10
X
X/* Size of a page; obtained from the operating system. */
X
Xint page_size;
X
X/* Name this program was invoked by. */
X
Xchar *progname;
X
X/* System dependencies */
X
X/* Define this if names etext, edata and end should not start with `_'. */
X/* #define nounderscore 1 */
X
X/* Define NON_NATIVE if using BSD or pseudo-BSD file format on a system
X whose native format is different. */
X/* #define NON_NATIVE */
X
X/* Define this to specify the default executable format. */
X
X#ifdef hpux
X#define DEFAULT_MAGIC NMAGIC /* hpux bugs screw ZMAGIC */
X#endif
X
X#ifndef DEFAULT_MAGIC
X#define DEFAULT_MAGIC ZMAGIC
X#endif
X
X/* Ordinary 4.3bsd lacks these macros in a.out.h. */
X
X#ifndef N_TXTADDR
X#if defined(vax) || defined(sony_news) || defined(hp300) || defined(pyr)
X#define N_TXTADDR(X) 0
X#endif
X#ifdef is68k
X#define N_TXTADDR(x) (sizeof (struct exec))
X#endif
X#ifdef sequent
X#define N_TXTADDR(x) (N_ADDRADJ(x))
X#endif
X#endif
X
X#ifndef N_DATADDR
X#if defined(vax) || defined(sony_news) || defined(hp300) || defined(pyr)
X#define N_DATADDR(x) \
X (((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \
X : (page_size+((N_TXTADDR(x)+(x).a_text-1) & ~(page_size-1))))
X#endif
X#ifdef is68k
X#define SEGMENT_SIZE 0x20000
X#define N_DATADDR(x) \
X(((x).a_magic==Omagic)? (N_TXTADDR(x)+(x).a_text) \
X : (SEGMENT_SIZE + ((N_TXTADDR(x)+(x).a_text-1) & ~(SEGMENT_SIZE-1))))
X#endif
X#ifdef sequent
X#define N_DATADDR(x) \
X (((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \
X : (page_size+(((x).a_text-1) & ~(page_size-1))))
X#endif
X#endif
X
X#ifdef sun
X#if TARGET == SUN4
X#define INITIALIZE_HEADER \
X{outheader.a_machtype = M_SPARC; outheader.a_toolversion = 1;}
X#endif
X#if TARGET == SUN2
X#define INITIALIZE_HEADER outheader.a_machtype = M_68010
X#endif
X#ifndef INITIALIZE_HEADER
X#define INITIALIZE_HEADER outheader.a_machtype = M_68020
X#endif
X#define TEXT_START(x) N_PAGSIZ(x)
X#endif
X#ifdef ALTOS
X#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, M_68020)
X#endif
X#ifdef is68k
X#ifdef M_68020
X/* ISI rel 4.0D doesn't use it, and rel 3.05 doesn't have an
X a_machtype field and so won't recognize the magic number. To keep
X binary compatibility for now, just ignore it */
X#define INITIALIZE_HEADER outheader.a_machtype = 0;
X#endif
X#endif
X#ifdef hpux
X#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, HP9000S200_ID)
X#endif
X#if defined(i386) && !defined(sequent)
X#define INITIALIZE_HEADER N_SET_MACHTYPE (outheader, M_386)
X#endif
X
X#ifdef is68k
X/* This enables code to take care of an ugly hack in the ISI OS.
X If a symbol beings with _$, then the object file is included only
X if the rest of the symbol name has been referenced. */
X#define DOLLAR_KLUDGE
X#endif
X
X/*
X * Alloca include.
X */
X#if defined(sun) && defined(sparc) && !defined(__GNUC__)
X#include "alloca.h"
X#endif
X
X#ifndef L_SET
X#define L_SET 0
X#endif
X
X/*
X * Ok. Following are the relocation information macros. If your
X * system should not be able to use the default set (below), you must
X * define the following:
X
X * relocation_info: This must be typedef'd (or #define'd) to the type
X * of structure that is stored in the relocation info section of your
X * a.out files. Often this is defined in the a.out.h for your system.
X *
X * RELOC_ADDRESS (rval): Offset into the current section of the
X * to be relocated. *Must be an lvalue*.
X *
X * RELOC_EXTERN_P (rval): Is this relocation entry based on an
X * external symbol (1), or was it fully resolved upon entering the
X * loader (0) in which case some combination of the value in memory
X * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains
X * what the value of the relocation actually was. *Must be an lvalue*.
X *
X * RELOC_TYPE (rval): If this entry was fully resolved upon
X * entering the loader, what type should it be relocated as?
X *
X * RELOC_SYMBOL (rval): If this entry was not fully resolved upon
X * entering the loader, what is the index of it's symbol in the symbol
X * table? *Must be a lvalue*.
X *
X * RELOC_MEMORY_ADD_P (rval): This should return true if the final
X * relocation value output here should be added to memory, or if the
X * section of memory described should simply be set to the relocation
X * value.
X *
X * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives
X * an extra value to be added to the relocation value based on the
X * individual relocation entry. *Must be an lvalue if defined*.
X *
X * RELOC_PCREL_P (rval): True if the relocation value described is
X * pc relative.
X *
X * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the
X * final relocation value before putting it where it belongs.
X *
X * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of
X * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes
X * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could
X * do everything in terms of the bit operators below), but having this
X * macro could end up producing better code on machines without fancy
X * bit twiddling. Also, it's easier to understand/code big/little
X * endian distinctions with this macro.
X *
X * RELOC_TARGET_BITPOS (rval): The starting bit position within the
X * object described in RELOC_TARGET_SIZE in which the relocation value
X * will go.
X *
X * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced
X * with the bits of the relocation value. It may be assumed by the
X * code that the relocation value will fit into this many bits. This
X * may be larger than RELOC_TARGET_SIZE if such be useful.
X *
X *
X * Things I haven't implemented
X * ----------------------------
X *
X * Values for RELOC_TARGET_SIZE other than 0, 1, or 2.
X *
X * Pc relative relocation for External references.
X *
X *
X */
X
X/* The following #if has been modifed for cross compilation */
X/* It originally read: #if defined(sun) && defined(sparc) */
X/* Marc Ullman, Stanford University Nov. 1 1989 */
X#if defined(sun) && (TARGET == SUN4)
X/* Sparc (Sun 4) macros */
X#undef relocation_info
X#define relocation_info reloc_info_sparc
X#define RELOC_ADDRESS(r) ((r)->r_address)
X#define RELOC_EXTERN_P(r) ((r)->r_extern)
X#define RELOC_TYPE(r) ((r)->r_index)
X#define RELOC_SYMBOL(r) ((r)->r_index)
X#define RELOC_MEMORY_SUB_P(r) 0
X#define RELOC_MEMORY_ADD_P(r) 0
X#define RELOC_ADD_EXTRA(r) ((r)->r_addend)
X#define RELOC_PCREL_P(r) \
X ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
X#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type])
X#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type])
X#define RELOC_TARGET_BITPOS(r) 0
X#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type])
X
X/* Note that these are very dependent on the order of the enums in
X enum reloc_type (in a.out.h); if they change the following must be
X changed */
X/* Also note that the last few may be incorrect; I have no information */
Xstatic int reloc_target_rightshift[] = {
X 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
X};
Xstatic int reloc_target_size[] = {
X 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X};
Xstatic int reloc_target_bitsize[] = {
X 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
X};
X
X#define MAX_ALIGNMENT (sizeof (double))
X#endif
X#ifdef sequent
X#define RELOC_ADDRESS(r) ((r)->r_address)
X#define RELOC_EXTERN_P(r) ((r)->r_extern)
X#define RELOC_TYPE(r) ((r)->r_symbolnum)
X#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
X#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr)
X#define RELOC_MEMORY_ADD_P(r) 1
X#undef RELOC_ADD_EXTRA
X#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr)
X#define RELOC_VALUE_RIGHTSHIFT(r) 0
X#define RELOC_TARGET_SIZE(r) ((r)->r_length)
X#define RELOC_TARGET_BITPOS(r) 0
X#define RELOC_TARGET_BITSIZE(r) 32
X#endif
X
X/* Default macros */
X#ifndef RELOC_ADDRESS
X#define RELOC_ADDRESS(r) ((r)->r_address)
X#define RELOC_EXTERN_P(r) ((r)->r_extern)
X#define RELOC_TYPE(r) ((r)->r_symbolnum)
X#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
X#define RELOC_MEMORY_SUB_P(r) 0
X#define RELOC_MEMORY_ADD_P(r) 1
X#undef RELOC_ADD_EXTRA
X#define RELOC_PCREL_P(r) ((r)->r_pcrel)
X#define RELOC_VALUE_RIGHTSHIFT(r) 0
X#define RELOC_TARGET_SIZE(r) ((r)->r_length)
X#define RELOC_TARGET_BITPOS(r) 0
X#define RELOC_TARGET_BITSIZE(r) 32
X#endif
X
X#ifndef MAX_ALIGNMENT
X#define MAX_ALIGNMENT (sizeof (int))
X#endif
X
X#ifdef nounderscore
X#define LPREFIX '.'
X#else
X#define LPREFIX 'L'
X#endif
X
X#ifndef TEXT_START
X#define TEXT_START(x) N_TXTADDR(x)
X#endif
X
X/* Special global symbol types understood by GNU LD. */
X
X/* The following type indicates the definition of a symbol as being
X an indirect reference to another symbol. The other symbol
X appears as an undefined reference, immediately following this symbol.
X
X Indirection is asymmetrical. The other symbol's value will be used
X to satisfy requests for the indirect symbol, but not vice versa.
X If the other symbol does not have a definition, libraries will
X be searched to find a definition.
X
X So, for example, the following two lines placed in an assembler
X input file would result in an object file which would direct gnu ld
X to resolve all references to symbol "foo" as references to symbol
X "bar".
X
X .stabs "_foo",11,0,0,0
X .stabs "_bar",1,0,0,0
X
X Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */
X
X#ifndef N_INDR
X#define N_INDR 0xa
X#endif
X
X/* The following symbols refer to set elements. These are expected
X only in input to the loader; they should not appear in loader
X output (unless relocatable output is requested). To be recognized
X by the loader, the input symbols must have their N_EXT bit set.
X All the N_SET[ATDB] symbols with the same name form one set. The
X loader collects all of these elements at load time and outputs a
X vector for each name.
X Space (an array of 32 bit words) is allocated for the set in the
X data section, and the n_value field of each set element value is
X stored into one word of the array.
X The first word of the array is the length of the set (number of
X elements). The last word of the vector is set to zero for possible
X use by incremental loaders. The array is ordered by the linkage
X order; the first symbols which the linker encounters will be first
X in the array.
X
X In C syntax this looks like:
X
X struct set_vector {
X unsigned int length;
X unsigned int vector[length];
X unsigned int always_zero;
X };
X
X Before being placed into the array, each element is relocated
X according to its type. This allows the loader to create an array
X of pointers to objects automatically. N_SETA type symbols will not
X be relocated.
X
X The address of the set is made into an N_SETV symbol
X whose name is the same as the name of the set.
X This symbol acts like a N_DATA global symbol
X in that it can satisfy undefined external references.
X
X For the purposes of determining whether or not to load in a library
X file, set element definitions are not considered "real
X definitions"; they will not cause the loading of a library
X member.
X
X If relocatable output is requested, none of this processing is
X done. The symbols are simply relocated and passed through to the
X output file.
X
X So, for example, the following three lines of assembler code
X (whether in one file or scattered between several different ones)
X will produce a three element vector (total length is five words;
X see above), referenced by the symbol "_xyzzy", which will have the
X addresses of the routines _init1, _init2, and _init3.
X
X *NOTE*: If symbolic addresses are used in the n_value field of the
X defining .stabs, those symbols must be defined in the same file as
X that containing the .stabs.
X
X .stabs "_xyzzy",23,0,0,_init1
X .stabs "_xyzzy",23,0,0,_init2
X .stabs "_xyzzy",23,0,0,_init3
X
X Note that (23 == (N_SETT | N_EXT)). */
X
X#ifndef N_SETA
X#define N_SETA 0x14 /* Absolute set element symbol */
X#endif /* This is input to LD, in a .o file. */
X
X#ifndef N_SETT
X#define N_SETT 0x16 /* Text set element symbol */
X#endif /* This is input to LD, in a .o file. */
X
X#ifndef N_SETD
X#define N_SETD 0x18 /* Data set element symbol */
X#endif /* This is input to LD, in a .o file. */
X
X#ifndef N_SETB
X#define N_SETB 0x1A /* Bss set element symbol */
X#endif /* This is input to LD, in a .o file. */
X
X/* Macros dealing with the set element symbols defined in a.out.h */
X#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT))
X#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS)
X
X#ifndef N_SETV
X#define N_SETV 0x1C /* Pointer to set vector in data area. */
X#endif /* This is output from LD. */
X
X/* If a this type of symbol is encountered, its name is a warning
X message to print each time the symbol referenced by the next symbol
X table entry is referenced.
X
X This feature may be used to allow backwards compatibility with
X certain functions (eg. gets) but to discourage programmers from
X their use.
X
X So if, for example, you wanted to have ld print a warning whenever
X the function "gets" was used in their C program, you would add the
X following to the assembler file in which gets is defined:
X
X .stabs "Obsolete function \"gets\" referenced",30,0,0,0
X .stabs "_gets",1,0,0,0
X
X These .stabs do not necessarily have to be in the same file as the
X gets function, they simply must exist somewhere in the compilation. */
X
X#ifndef N_WARNING
X#define N_WARNING 0x1E /* Warning message to print if symbol
X included */
X#endif /* This is input to ld */
X
X#ifndef __GNU_STAB__
X
X/* Line number for the data section. This is to be used to describe
X the source location of a variable declaration. */
X#ifndef N_DSLINE
X#define N_DSLINE (N_SLINE+N_DATA-N_TEXT)
X#endif
X
X/* Line number for the bss section. This is to be used to describe
X the source location of a variable declaration. */
X#ifndef N_BSLINE
X#define N_BSLINE (N_SLINE+N_BSS-N_TEXT)
X#endif
X
X#endif /* not __GNU_STAB__ */
X
X/* Symbol table */
X
X/* Global symbol data is recorded in these structures,
X one for each global symbol.
X They are found via hashing in 'symtab', which points to a vector of buckets.
X Each bucket is a chain of these structures through the link field. */
X
Xtypedef
X struct glosym
X {
X /* Pointer to next symbol in this symbol's hash bucket. */
X struct glosym *link;
X /* Name of this symbol. */
X char *name;
X /* Value of this symbol as a global symbol. */
X long value;
X /* Chain of external 'nlist's in files for this symbol, both defs
X and refs. */
X struct nlist *refs;
X /* Any warning message that might be associated with this symbol
X from an N_WARNING symbol encountered. */
X char *warning;
X /* Nonzero means definitions of this symbol as common have been seen,
X and the value here is the largest size specified by any of them. */
X int max_common_size;
X /* For relocatable_output, records the index of this global sym in the
X symbol table to be written, with the first global sym given index 0.*/
X int def_count;
X /* Nonzero means a definition of this global symbol is known to exist.
X Library members should not be loaded on its account. */
X char defined;
X /* Nonzero means a reference to this global symbol has been seen
X in a file that is surely being loaded.
X A value higher than 1 is the n_type code for the symbol's
X definition. */
X char referenced;
X /* A count of the number of undefined references printed for a
X specific symbol. If a symbol is unresolved at the end of
X digest_symbols (and the loading run is supposed to produce
X relocatable output) do_file_warnings keeps track of how many
X unresolved reference error messages have been printed for
X each symbol here. When the number hits MAX_UREFS_PRINTED,
X messages stop. */
X unsigned char undef_refs;
X /* 1 means that this symbol has multiple definitions. 2 means
X that it has multiple definitions, and some of them are set
X elements, one of which has been printed out already. */
X unsigned char multiply_defined;
X /* Nonzero means print a message at all refs or defs of this symbol */
X char trace;
X }
X symbol;
X
X/* Demangler for C++. */
Xextern char *cplus_demangle ();
X
X/* Demangler function to use. */
Xchar *(*demangler)() = NULL;
X
X/* Number of buckets in symbol hash table */
X#define TABSIZE 1009
X
X/* The symbol hash table: a vector of TABSIZE pointers to struct glosym. */
Xsymbol *symtab[TABSIZE];
X
X/* Number of symbols in symbol hash table. */
Xint num_hash_tab_syms = 0;
X
X/* Count the number of nlist entries that are for local symbols.
X This count and the three following counts
X are incremented as as symbols are entered in the symbol table. */
Xint local_sym_count;
X
X/* Count number of nlist entries that are for local symbols
X whose names don't start with L. */
Xint non_L_local_sym_count;
X
X/* Count the number of nlist entries for debugger info. */
Xint debugger_sym_count;
X
X/* Count the number of global symbols referenced and not defined. */
Xint undefined_global_sym_count;
X
X/* Count the number of global symbols multiply defined. */
Xint multiple_def_count;
X
X/* Count the number of defined global symbols.
X Each symbol is counted only once
X regardless of how many different nlist entries refer to it,
X since the output file will need only one nlist entry for it.
X This count is computed by `digest_symbols';
X it is undefined while symbols are being loaded. */
Xint defined_global_sym_count;
X
X/* Count the number of symbols defined through common declarations.
X This count is kept in symdef_library, linear_library, and
X enter_global_ref. It is incremented when the defined flag is set
X in a symbol because of a common definition, and decremented when
X the symbol is defined "for real" (ie. by something besides a common
X definition). */
Xint common_defined_global_count;
X
X/* Count the number of set element type symbols and the number of
X separate vectors which these symbols will fit into. See the
X GNU a.out.h for more info.
X This count is computed by 'enter_file_symbols' */
Xint set_symbol_count;
Xint set_vector_count;
X
X/* Define a linked list of strings which define symbols which should
X be treated as set elements even though they aren't. Any symbol
X with a prefix matching one of these should be treated as a set
X element.
X
X This is to make up for deficiencies in many assemblers which aren't
X willing to pass any stabs through to the loader which they don't
X understand. */
Xstruct string_list_element {
X char *str;
X struct string_list_element *next;
X};
X
Xstruct string_list_element *set_element_prefixes;
X
X/* Count the number of definitions done indirectly (ie. done relative
X to the value of some other symbol. */
Xint global_indirect_count;
X
X/* Count the number of warning symbols encountered. */
Xint warning_count;
X
X/* Total number of symbols to be written in the output file.
X Computed by digest_symbols from the variables above. */
Xint nsyms;
X
X
X/* Nonzero means ptr to symbol entry for symbol to use as start addr.
X -e sets this. */
Xsymbol *entry_symbol;
X
Xsymbol *edata_symbol; /* the symbol _edata */
Xsymbol *etext_symbol; /* the symbol _etext */
Xsymbol *end_symbol; /* the symbol _end */
X
X/* Each input file, and each library member ("subfile") being loaded,
X has a `file_entry' structure for it.
X
X For files specified by command args, these are contained in the vector
X which `file_table' points to.
X
X For library members, they are dynamically allocated,
X and chained through the `chain' field.
X The chain is found in the `subfiles' field of the `file_entry'.
X The `file_entry' objects for the members have `superfile' fields pointing
X to the one for the library. */
X
Xstruct file_entry {
X /* Name of this file. */
X char *filename;
X /* Name to use for the symbol giving address of text start */
X /* Usually the same as filename, but for a file spec'd with -l
X this is the -l switch itself rather than the filename. */
X char *local_sym_name;
X
X /* Describe the layout of the contents of the file */
X
X /* The file's a.out header. */
X struct exec header;
X /* Offset in file of GDB symbol segment, or 0 if there is none. */
X int symseg_offset;
X
X /* Describe data from the file loaded into core */
X
X /* Symbol table of the file. */
X struct nlist *symbols;
X /* Size in bytes of string table. */
X int string_size;
X /* Pointer to the string table.
X The string table is not kept in core all the time,
X but when it is in core, its address is here. */
X char *strings;
X
X /* Next two used only if `relocatable_output' or if needed for */
X /* output of undefined reference line numbers. */
X
X /* Text reloc info saved by `write_text' for `coptxtrel'. */
X struct relocation_info *textrel;
X /* Data reloc info saved by `write_data' for `copdatrel'. */
X struct relocation_info *datarel;
X
X /* Relation of this file's segments to the output file */
X
X /* Start of this file's text seg in the output file core image. */
X int text_start_address;
X /* Start of this file's data seg in the output file core image. */
X int data_start_address;
X /* Start of this file's bss seg in the output file core image. */
X int bss_start_address;
X /* Offset in bytes in the output file symbol table
X of the first local symbol for this file. Set by `write_file_symbols'. */
X int local_syms_offset;
X
X /* For library members only */
X
X /* For a library, points to chain of entries for the library members. */
X struct file_entry *subfiles;
X /* For a library member, offset of the member within the archive.
X Zero for files that are not library members. */
X int starting_offset;
X /* Size of contents of this file, if library member. */
X int total_size;
X /* For library member, points to the library's own entry. */
X struct file_entry *superfile;
X /* For library member, points to next entry for next member. */
X struct file_entry *chain;
X
X /* 1 if file is a library. */
X char library_flag;
X
X /* 1 if file's header has been read into this structure. */
X char header_read_flag;
X
X /* 1 means search a set of directories for this file. */
X char search_dirs_flag;
X
X /* 1 means this is base file of incremental load.
X Do not load this file's text or data.
X Also default text_start to after this file's bss. */
X char just_syms_flag;
X};
X
X/* Vector of entries for input files specified by arguments.
X These are all the input files except for members of specified libraries. */
Xstruct file_entry *file_table;
X
X/* Length of that vector. */
Xint number_of_files;
X
X/* When loading the text and data, we can avoid doing a close
X and another open between members of the same library.
X
X These two variables remember the file that is currently open.
X Both are zero if no file is open.
X
X See `each_file' and `file_close'. */
X
Xstruct file_entry *input_file;
Xint input_desc;
X
X/* The name of the file to write; "a.out" by default. */
X
Xchar *output_filename;
X
X/* Descriptor for writing that file with `mywrite'. */
X
Xint outdesc;
X
X/* Header for that file (filled in by `write_header'). */
X
Xstruct exec outheader;
X
X#ifdef COFF_ENCAPSULATE
Xstruct coffheader coffheader;
Xint need_coff_header;
X#endif
X
X/* The following are computed by `digest_symbols'. */
X
Xint text_size; /* total size of text of all input files. */
Xint data_size; /* total size of data of all input files. */
Xint bss_size; /* total size of bss of all input files. */
Xint text_reloc_size; /* total size of text relocation of all input files. */
Xint data_reloc_size; /* total size of data relocation of all input */
X /* files. */
X
X/* Specifications of start and length of the area reserved at the end
X of the text segment for the set vectors. Computed in 'digest_symbols' */
Xint set_sect_start;
Xint set_sect_size;
X
X/* Pointer for in core storage for the above vectors, before they are
X written. */
Xunsigned long *set_vectors;
X
X/* Amount of cleared space to leave between the text and data segments. */
X
Xint text_pad;
X
X/* Amount of bss segment to include as part of the data segment. */
X
Xint data_pad;
X
X/* Format of __.SYMDEF:
X First, a longword containing the size of the 'symdef' data that follows.
X Second, zero or more 'symdef' structures.
X Third, a longword containing the length of symbol name strings.
X Fourth, zero or more symbol name strings (each followed by a null). */
X
Xstruct symdef {
X int symbol_name_string_index;
X int library_member_offset;
X};
X
X/* Record most of the command options. */
X
X/* Address we assume the text section will be loaded at.
X We relocate symbols and text and data for this, but we do not
X write any padding in the output file for it. */
Xint text_start;
X
X/* Offset of default entry-pc within the text section. */
Xint entry_offset;
X
X/* Address we decide the data section will be loaded at. */
Xint data_start;
X
X/* `text-start' address is normally this much plus a page boundary.
X This is not a user option; it is fixed for each system. */
Xint text_start_alignment;
X
X/* Nonzero if -T was specified in the command line.
X This prevents text_start from being set later to default values. */
Xint T_flag_specified;
X
X/* Nonzero if -Tdata was specified in the command line.
X This prevents data_start from being set later to default values. */
Xint Tdata_flag_specified;
X
X/* Size to pad data section up to.
X We simply increase the size of the data section, padding with zeros,
X and reduce the size of the bss section to match. */
Xint specified_data_size;
X
X/* Magic number to use for the output file, set by switch. */
Xint magic;
X
X/* Nonzero means print names of input files as processed. */
Xint trace_files;
X
X/* Which symbols should be stripped (omitted from the output):
X none, all, or debugger symbols. */
Xenum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols;
X
X/* Which local symbols should be omitted:
X none, all, or those starting with L.
X This is irrelevant if STRIP_NONE. */
Xenum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals;
X
X/* 1 => write load map. */
Xint write_map;
X
X/* 1 => write relocation into output file so can re-input it later. */
Xint relocatable_output;
X
X/* 1 => assign space to common symbols even if `relocatable_output'. */
Xint force_common_definition;
X
X/* Standard directories to search for files specified by -l. */
Xchar *standard_search_dirs[] =
X#ifdef STANDARD_SEARCH_DIRS
X {STANDARD_SEARCH_DIRS};
X#else
X#ifdef NON_NATIVE
X {"/usr/local/lib/gnu"};
X#else
X {"/lib", "/usr/lib", "/usr/local/lib"};
X#endif
X#endif
X
X/* Actual vector of directories to search;
X this contains those specified with -L plus the standard ones. */
Xchar **search_dirs;
X
X/* Length of the vector `search_dirs'. */
Xint n_search_dirs;
X
X/* Non zero means to create the output executable. */
X/* Cleared by nonfatal errors. */
Xint make_executable;
X
X/* Force the executable to be output, even if there are non-fatal
X errors */
Xint force_executable;
X
X/* Keep a list of any symbols referenced from the command line (so
X that error messages for these guys can be generated). This list is
X zero terminated. */
Xstruct glosym **cmdline_references;
Xint cl_refs_allocated;
X
Xvoid bcopy (), bzero ();
Xint malloc (), realloc ();
X#ifndef alloca
Xint alloca ();
X#endif
Xint free ();
X
Xint xmalloc ();
Xint xrealloc ();
Xvoid fatal ();
Xvoid fatal_with_file ();
Xvoid perror_name ();
Xvoid perror_file ();
Xvoid error ();
X
Xvoid digest_symbols ();
Xvoid print_symbols ();
Xvoid load_symbols ();
Xvoid decode_command ();
Xvoid list_undefined_symbols ();
Xvoid list_unresolved_references ();
Xvoid write_output ();
Xvoid write_header ();
Xvoid write_text ();
Xvoid read_file_relocation ();
Xvoid write_data ();
Xvoid write_rel ();
Xvoid write_syms ();
Xvoid write_symsegs ();
Xvoid mywrite ();
Xvoid symtab_init ();
Xvoid padfile ();
Xchar *concat ();
Xchar *get_file_name ();
Xsymbol *getsym (), *getsym_soft ();
X
Xint
Xmain (argc, argv)
X char **argv;
X int argc;
X{
X/* Added this to stop ld core-dumping on very large .o files. */
X#ifdef RLIMIT_STACK
X /* Get rid of any avoidable limit on stack size. */
X {
X struct rlimit rlim;
X
X /* Set the stack limit huge so that alloca does not fail. */
X getrlimit (RLIMIT_STACK, &rlim);
X rlim.rlim_cur = rlim.rlim_max;
X setrlimit (RLIMIT_STACK, &rlim);
X }
X#endif /* RLIMIT_STACK */
X
X page_size = getpagesize ();
X progname = argv[0];
X
X /* Clear the cumulative info on the output file. */
X
X text_size = 0;
X data_size = 0;
X bss_size = 0;
X text_reloc_size = 0;
X data_reloc_size = 0;
X
X data_pad = 0;
X text_pad = 0;
X
X /* Initialize the data about options. */
X
X specified_data_size = 0;
X strip_symbols = STRIP_NONE;
X trace_files = 0;
X discard_locals = DISCARD_NONE;
X entry_symbol = 0;
X write_map = 0;
X relocatable_output = 0;
X force_common_definition = 0;
X T_flag_specified = 0;
X Tdata_flag_specified = 0;
X magic = DEFAULT_MAGIC;
X make_executable = 1;
X force_executable = 0;
X set_element_prefixes = 0;
X
X /* Initialize the cumulative counts of symbols. */
X
X local_sym_count = 0;
X non_L_local_sym_count = 0;
X debugger_sym_count = 0;
X undefined_global_sym_count = 0;
X set_symbol_count = 0;
X set_vector_count = 0;
X global_indirect_count = 0;
X warning_count = 0;
X multiple_def_count = 0;
X common_defined_global_count = 0;
X
X /* Keep a list of symbols referenced from the command line */
X cl_refs_allocated = 10;
X cmdline_references
X = (struct glosym **) xmalloc (cl_refs_allocated
X * sizeof(struct glosym *));
X *cmdline_references = 0;
X
X /* Completely decode ARGV. */
X
X decode_command (argc, argv);
X
X /* Create the symbols `etext', `edata' and `end'. */
X
X if (!relocatable_output)
X symtab_init ();
X
X /* Determine whether to count the header as part of
X the text size, and initialize the text size accordingly.
X This depends on the kind of system and on the output format selected. */
X
X N_SET_MAGIC (outheader, magic);
X#ifdef INITIALIZE_HEADER
X INITIALIZE_HEADER;
X#endif
X
X text_size = sizeof (struct exec);
X#ifdef COFF_ENCAPSULATE
X if (relocatable_output == 0 && file_table[0].just_syms_flag == 0)
X {
X need_coff_header = 1;
X /* set this flag now, since it will change the values of N_TXTOFF, etc */
X N_SET_FLAGS (outheader, N_FLAGS_COFF_ENCAPSULATE);
X text_size += sizeof (struct coffheader);
X }
X#endif
X
X text_size -= N_TXTOFF (outheader);
X
X if (text_size < 0)
X text_size = 0;
X entry_offset = text_size;
X
X if (!T_flag_specified && !relocatable_output)
X text_start = TEXT_START (outheader);
X
X /* The text-start address is normally this far past a page boundary. */
X text_start_alignment = text_start % page_size;
X
X /* Load symbols of all input files.
X Also search all libraries and decide which library members to load. */
X
X load_symbols ();
X
X /* Compute where each file's sections go, and relocate symbols. */
X
X digest_symbols ();
X
X /* Print error messages for any missing symbols, for any warning
X symbols, and possibly multiple definitions */
X
X do_warnings (stderr);
X
X /* Print a map, if requested. */
X
X if (write_map) print_symbols (stdout);
X
X /* Write the output file. */
X
X if (make_executable || force_executable)
X write_output ();
X
X exit (!make_executable);
X}
X
Xvoid decode_option ();
X
X/* Analyze a command line argument.
X Return 0 if the argument is a filename.
X Return 1 if the argument is a option complete in itself.
X Return 2 if the argument is a option which uses an argument.
X
X Thus, the value is the number of consecutive arguments
X that are part of options. */
X
Xint
Xclassify_arg (arg)
X register char *arg;
X{
X if (*arg != '-') return 0;
X switch (arg[1])
X {
X case 'A':
X case 'D':
X case 'e':
X case 'L':
X case 'l':
X case 'o':
X case 'u':
X case 'V':
X case 'y':
X if (arg[2])
X return 1;
X return 2;
X
X case 'B':
X if (! strcmp (&arg[2], "static"))
X return 1;
X
X case 'T':
X if (arg[2] == 0)
X return 2;
X if (! strcmp (&arg[2], "text"))
X return 2;
X if (! strcmp (&arg[2], "data"))
X return 2;
X return 1;
X }
X
X return 1;
X}
X
X/* Process the command arguments,
X setting up file_table with an entry for each input file,
X and setting variables according to the options. */
X
Xvoid
Xdecode_command (argc, argv)
X char **argv;
X int argc;
X{
X register int i;
X register struct file_entry *p;
X
X number_of_files = 0;
X output_filename = "a.out";
X
X n_search_dirs = 0;
X search_dirs = (char **) xmalloc (sizeof (char *));
X
X /* First compute number_of_files so we know how long to make file_table. */
X /* Also process most options completely. */
X
X for (i = 1; i < argc; i++)
X {
X register int code = classify_arg (argv[i]);
X if (code)
X {
X if (i + code > argc)
X fatal ("no argument following %s\n", argv[i]);
X
X decode_option (argv[i], argv[i+1]);
X
X if (argv[i][1] == 'l' || argv[i][1] == 'A')
X number_of_files++;
X
X i += code - 1;
X }
X else
X number_of_files++;
X }
X
X if (!number_of_files)
X fatal ("no input files", 0);
X
X p = file_table
X = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry));
X bzero (p, number_of_files * sizeof (struct file_entry));
X
X /* Now scan again and fill in file_table. */
X /* All options except -A and -l are ignored here. */
X
X for (i = 1; i < argc; i++)
X {
X register int code = classify_arg (argv[i]);
X
X if (code)
X {
X char *string;
X if (code == 2)
X string = argv[i+1];
X else
X string = &argv[i][2];
X
X if (argv[i][1] == 'A')
X {
X if (p != file_table)
X fatal ("-A specified before an input file other than the first");
X
X p->filename = string;
X p->local_sym_name = string;
X p->just_syms_flag = 1;
X p++;
X }
X if (argv[i][1] == 'l')
X {
X p->filename = concat ("lib", string, ".a");
X p->local_sym_name = concat ("-l", string, "");
X p->search_dirs_flag = 1;
X p++;
X }
X i += code - 1;
X }
X else
X {
X p->filename = argv[i];
X p->local_sym_name = argv[i];
X p++;
X }
X }
X
X /* Now check some option settings for consistency. */
X
X#ifdef NMAGIC
X if ((magic == ZMAGIC || magic == NMAGIC)
X#else
X if ((magic == ZMAGIC)
X#endif
X && (text_start - text_start_alignment) & (page_size - 1))
X fatal ("-T argument not multiple of page size, with sharable output", 0);
X
X /* Append the standard search directories to the user-specified ones. */
X {
X int n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
X n_search_dirs += n;
X search_dirs
X = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *));
X bcopy (standard_search_dirs, &search_dirs[n_search_dirs - n],
X n * sizeof (char *));
X }
X}
X
X
Xvoid
Xadd_cmdline_ref (sp)
X struct glosym *sp;
X{
X struct glosym **ptr;
X
X for (ptr = cmdline_references;
X ptr < cmdline_references + cl_refs_allocated && *ptr;
X ptr++)
X ;
X
X if (ptr >= cmdline_references + cl_refs_allocated - 1)
X {
X int diff = ptr - cmdline_references;
X
X cl_refs_allocated *= 2;
X cmdline_references = (struct glosym **)
X xrealloc (cmdline_references,
X cl_refs_allocated * sizeof (struct glosym *));
X ptr = cmdline_references + diff;
X }
X
X *ptr++ = sp;
X *ptr = (struct glosym *) 0;
X}
X
Xint
Xset_element_prefixed_p (name)
X char *name;
X{
X struct string_list_element *p;
X int i;
X
X for (p = set_element_prefixes; p; p = p->next)
X {
X for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++)
X ;
X
X if (p->str[i] == '\0')
X return 1;
X }
X return 0;
X}
X
Xint parse ();
X
X/* Record an option and arrange to act on it later.
X ARG should be the following command argument,
X which may or may not be used by this option.
X
X The `l' and `A' options are ignored here since they actually
X specify input files. */
X
Xvoid
Xdecode_option (swt, arg)
X register char *swt, *arg;
X{
X /* We get Bstatic from gcc on suns. */
X if (! strcmp (swt + 1, "Bstatic"))
X return;
X if (! strcmp (swt + 1, "Ttext"))
X {
X text_start = parse (arg, "%x", "invalid argument to -Ttext");
X T_flag_specified = 1;
X return;
X }
X if (! strcmp (swt + 1, "Tdata"))
X {
X data_start = parse (arg, "%x", "invalid argument to -Tdata");
X Tdata_flag_specified = 1;
X return;
X }
X if (! strcmp (swt + 1, "noinhibit-exec"))
X {
X force_executable = 1;
X return;
X }
X
X if (swt[2] != 0)
X arg = &swt[2];
X
X switch (swt[1])
X {
X case 'A':
X return;
X
X case 'D':
X specified_data_size = parse (arg, "%x", "invalid argument to -D");
X return;
X
X case 'd':
X force_common_definition = 1;
X return;
X
X case 'e':
X entry_symbol = getsym (arg);
X if (!entry_symbol->defined && !entry_symbol->referenced)
X undefined_global_sym_count++;
X entry_symbol->referenced = 1;
X add_cmdline_ref (entry_symbol);
X return;
X
X case 'l':
X /* If linking with libg++, use the C++ demangler. */
X if (arg != NULL && strcmp (arg, "g++") == 0)
X demangler = cplus_demangle;
X return;
X
X case 'L':
X n_search_dirs++;
X search_dirs
X = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *));
X search_dirs[n_search_dirs - 1] = arg;
X return;
X
X case 'M':
X write_map = 1;
X return;
X
X case 'N':
X magic = OMAGIC;
X return;
X
X#ifdef NMAGIC
X case 'n':
X magic = NMAGIC;
X return;
X#endif
X
X case 'o':
X output_filename = arg;
X return;
X
X case 'r':
X relocatable_output = 1;
X magic = OMAGIC;
X text_start = 0;
X return;
X
X case 'S':
X strip_symbols = STRIP_DEBUGGER;
X return;
X
X case 's':
X strip_symbols = STRIP_ALL;
X return;
X
X case 'T':
X text_start = parse (arg, "%x", "invalid argument to -T");
X T_flag_specified = 1;
X return;
X
X case 't':
X trace_files = 1;
X return;
X
X case 'u':
X {
X register symbol *sp = getsym (arg);
X if (!sp->defined && !sp->referenced)
X undefined_global_sym_count++;
X sp->referenced = 1;
X add_cmdline_ref (sp);
X }
X return;
X
X case 'V':
X {
X struct string_list_element *new
X = (struct string_list_element *)
X xmalloc (sizeof (struct string_list_element));
X
X new->str = arg;
X new->next = set_element_prefixes;
X set_element_prefixes = new;
X return;
X }
X
X case 'X':
X discard_locals = DISCARD_L;
X return;
X
X case 'x':
X discard_locals = DISCARD_ALL;
X return;
X
X case 'y':
X {
X register symbol *sp = getsym (&swt[2]);
X sp->trace = 1;
X }
X return;
X
X case 'z':
X magic = ZMAGIC;
X return;
X
X default:
X fatal ("invalid command option `%s'", swt);
X }
X}
X
X/** Convenient functions for operating on one or all files being */
X /** loaded. */
Xvoid print_file_name ();
X
X/* Call FUNCTION on each input file entry.
X Do not call for entries for libraries;
X instead, call once for each library member that is being loaded.
X
X FUNCTION receives two arguments: the entry, and ARG. */
X
Xvoid
Xeach_file (function, arg)
X register void (*function)();
X register int arg;
X{
X register int i;
X
X for (i = 0; i < number_of_files; i++)
X {
X register struct file_entry *entry = &file_table[i];
X if (entry->library_flag)
X {
X register struct file_entry *subentry = entry->subfiles;
X for (; subentry; subentry = subentry->chain)
X (*function) (subentry, arg);
X }
X else
X (*function) (entry, arg);
X }
X}
X
X/* Call FUNCTION on each input file entry until it returns a non-zero
X value. Return this value.
X Do not call for entries for libraries;
X instead, call once for each library member that is being loaded.
X
X FUNCTION receives two arguments: the entry, and ARG. It must be a
X function returning unsigned long (though this can probably be fudged). */
X
Xunsigned long
Xcheck_each_file (function, arg)
X register unsigned long (*function)();
X register int arg;
X{
X register int i;
X register unsigned long return_val;
X
X for (i = 0; i < number_of_files; i++)
X {
X register struct file_entry *entry = &file_table[i];
X if (entry->library_flag)
X {
X register struct file_entry *subentry = entry->subfiles;
X for (; subentry; subentry = subentry->chain)
X if (return_val = (*function) (subentry, arg))
X return return_val;
X }
X else
X if (return_val = (*function) (entry, arg))
X return return_val;
X }
X return 0;
X}
X
X/* Like `each_file' but ignore files that were just for symbol definitions. */
X
Xvoid
Xeach_full_file (function, arg)
X register void (*function)();
X register int arg;
X{
X register int i;
X
X for (i = 0; i < number_of_files; i++)
X {
X register struct file_entry *entry = &file_table[i];
X if (entry->just_syms_flag)
X continue;
X if (entry->library_flag)
X {
X register struct file_entry *subentry = entry->subfiles;
X for (; subentry; subentry = subentry->chain)
X (*function) (subentry, arg);
X }
X else
X (*function) (entry, arg);
X }
X}
X
X/* Close the input file that is now open. */
X
Xvoid
Xfile_close ()
X{
X close (input_desc);
X input_desc = 0;
X input_file = 0;
X}
X
X/* Open the input file specified by 'entry', and return a descriptor.
X The open file is remembered; if the same file is opened twice in a row,
X a new open is not actually done. */
X
Xint
Xfile_open (entry)
X register struct file_entry *entry;
X{
X register int desc;
X
X if (entry->superfile)
X return file_open (entry->superfile);
X
X if (entry == input_file)
X return input_desc;
X
X if (input_file) file_close ();
X
X if (entry->search_dirs_flag)
X {
X int i;
X
X for (i = 0; i < n_search_dirs; i++)
X {
X register char *string
X = concat (search_dirs[i], "/", entry->filename);
X desc = open (string, O_RDONLY, 0);
X if (desc > 0)
X {
X entry->filename = string;
X entry->search_dirs_flag = 0;
X break;
X }
X free (string);
X }
X }
X else
X desc = open (entry->filename, O_RDONLY, 0);
X
X if (desc > 0)
X {
X input_file = entry;
X input_desc = desc;
X return desc;
X }
X
X perror_file (entry);
X /* NOTREACHED */
X}
X
X/* Print the filename of ENTRY on OUTFILE (a stdio stream),
X and then a newline. */
X
Xvoid
Xprline_file_name (entry, outfile)
X struct file_entry *entry;
X FILE *outfile;
X{
X print_file_name (entry, outfile);
X fprintf (outfile, "\n");
X}
X
X/* Print the filename of ENTRY on OUTFILE (a stdio stream). */
X
Xvoid
Xprint_file_name (entry, outfile)
X struct file_entry *entry;
X FILE *outfile;
X{
X if (entry->superfile)
X {
X print_file_name (entry->superfile, outfile);
X fprintf (outfile, "(%s)", entry->filename);
X }
X else
X fprintf (outfile, "%s", entry->filename);
X}
X
X/* Return the filename of entry as a string (malloc'd for the purpose) */
X
Xchar *
Xget_file_name (entry)
X struct file_entry *entry;
X{
X char *result, *supfile;
X if (entry->superfile)
X {
X supfile = get_file_name (entry->superfile);
X result = (char *) xmalloc (strlen (supfile)
X + strlen (entry->filename) + 3);
X sprintf (result, "%s(%s)", supfile, entry->filename);
X free (supfile);
X }
X else
X {
X result = (char *) xmalloc (strlen (entry->filename) + 1);
X strcpy (result, entry->filename);
X }
X return result;
X}
X
X/* Medium-level input routines for rel files. */
X
X/* Read a file's header into the proper place in the file_entry.
X DESC is the descriptor on which the file is open.
X ENTRY is the file's entry. */
X
Xvoid
Xread_header (desc, entry)
X int desc;
X register struct file_entry *entry;
X{
X register int len;
X struct exec *loc = (struct exec *) &entry->header;
X
X lseek (desc, entry->starting_offset, 0);
X#ifdef COFF_ENCAPSULATE
X if (entry->just_syms_flag)
X lseek (desc, sizeof(coffheader), 1);
X#endif
X len = read (desc, loc, sizeof (struct exec));
X if (len != sizeof (struct exec))
X fatal_with_file ("failure reading header of ", entry);
X if (N_BADMAG (*loc))
X fatal_with_file ("bad magic number in ", entry);
X
X entry->header_read_flag = 1;
X}
X
X/* Read the symbols of file ENTRY into core.
X Assume it is already open, on descriptor DESC.
X Also read the length of the string table, which follows the symbol table,
X but don't read the contents of the string table. */
X
Xvoid
Xread_entry_symbols (desc, entry)
X struct file_entry *entry;
X int desc;
X{
X int str_size;
X
X if (!entry->header_read_flag)
X read_header (desc, entry);
X
X entry->symbols = (struct nlist *) xmalloc (entry->header.a_syms);
X
X lseek (desc, N_SYMOFF (entry->header) + entry->starting_offset, 0);
X if (entry->header.a_syms != read (desc, entry->symbols, entry->header.a_syms))
X fatal_with_file ("premature end of file in symbols of ", entry);
X
X lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0);
X if (sizeof str_size != read (desc, &str_size, sizeof str_size))
X fatal_with_file ("bad string table size in ", entry);
X
X entry->string_size = str_size;
X}
X
X/* Read the string table of file ENTRY into core.
X Assume it is already open, on descriptor DESC.
X Also record whether a GDB symbol segment follows the string table. */

X
Xvoid
Xread_entry_strings (desc, entry)
X struct file_entry *entry;
X int desc;
X{
X int buffer;
X
X if (!entry->header_read_flag)
X read_header (desc, entry);
X
X lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0);
X if (entry->string_size != read (desc, entry->strings, entry->string_size))
X fatal_with_file ("premature end of file in strings of ", entry);
X
X /* While we are here, see if the file has a symbol segment at the end.
X For a separate file, just try reading some more.
X For a library member, compare current pos against total size. */
X if (entry->superfile)
X {
X if (entry->total_size == N_STROFF (entry->header) + entry->string_size)
X return;
X }
X else
X {
X buffer = read (desc, &buffer, sizeof buffer);
X if (buffer == 0)
X return;
X if (buffer != sizeof buffer)
X fatal_with_file ("premature end of file in GDB symbol segment of ", entry);
X }
X /* Don't try to do anything with symsegs. */
X return;
X#if 0
X /* eliminate warning of `statement not reached'. */
X entry->symseg_offset = N_STROFF (entry->header) + entry->string_size;
X#endif
X}
X
X/* Read in the symbols of all input files. */
X
Xvoid read_file_symbols (), read_entry_symbols (), read_entry_strings ();
Xvoid enter_file_symbols (), enter_global_ref (), search_library ();
X
Xvoid
Xload_symbols ()
X{
X register int i;
X
X if (trace_files) fprintf (stderr, "Loading symbols:\n\n");
X
X for (i = 0; i < number_of_files; i++)
X {
X register struct file_entry *entry = &file_table[i];
X read_file_symbols (entry);
X }
X
X if (trace_files) fprintf (stderr, "\n");
X}
X
X/* If ENTRY is a rel file, read its symbol and string sections into core.
X If it is a library, search it and load the appropriate members
X (which means calling this function recursively on those members). */
X
Xvoid
Xread_file_symbols (entry)
X register struct file_entry *entry;
X{
X register int desc;
X register int len;
X struct exec hdr;
X
X desc = file_open (entry);
X
X#ifdef COFF_ENCAPSULATE
X if (entry->just_syms_flag)
X lseek (desc, sizeof(coffheader),0);
X#endif
X
X len = read (desc, &hdr, sizeof hdr);
X if (len != sizeof hdr)
X fatal_with_file ("failure reading header of ", entry);
X
X if (!N_BADMAG (hdr))
X {
X read_entry_symbols (desc, entry);
X entry->strings = (char *) alloca (entry->string_size);
X read_entry_strings (desc, entry);
X enter_file_symbols (entry);
X entry->strings = 0;
X }
X else
X {
X char armag[SARMAG];
X
X lseek (desc, 0, 0);
X if (SARMAG != read (desc, armag, SARMAG) || strncmp (armag, ARMAG, SARMAG))
X fatal_with_file ("malformed input file (not rel or archive) ", entry);
X entry->library_flag = 1;
X search_library (desc, entry);
X }
X
X file_close ();
X}
X
X/* Enter the external symbol defs and refs of ENTRY in the hash table. */
X
Xvoid
Xenter_file_symbols (entry)
X struct file_entry *entry;
X{
X register struct nlist
X *p,
X *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
X
X if (trace_files) prline_file_name (entry, stderr);
X
X for (p = entry->symbols; p < end; p++)
X {
X if (p->n_type == (N_SETV | N_EXT)) continue;
X if (set_element_prefixes
X && set_element_prefixed_p (p->n_un.n_strx + entry->strings))
X p->n_type += (N_SETA - N_ABS);
X
X if (SET_ELEMENT_P (p->n_type))
X {
X set_symbol_count++;
X if (!relocatable_output)
X enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
X }
X else if (p->n_type == N_WARNING)
X {
X char *name = p->n_un.n_strx + entry->strings;
X
X /* Grab the next entry. */
X p++;
X if (p->n_type != (N_UNDF | N_EXT))
X {
X fprintf (stderr, "%s: Warning symbol found in %s without external reference following.\n",
X progname, entry->filename);
X make_executable = 0;
X p--; /* Process normally. */
X }
X else
X {
X symbol *sp;
X char *sname = p->n_un.n_strx + entry->strings;
X /* Deal with the warning symbol. */
X enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
X sp = getsym (sname);
X sp->warning = (char *) xmalloc (strlen(name) + 1);
X strcpy (sp->warning, name);
X warning_count++;
X }
X }
X else if (p->n_type & N_EXT)
X enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
X else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT)))
X {
X if ((p->n_un.n_strx + entry->strings)[0] != LPREFIX)
X non_L_local_sym_count++;
X local_sym_count++;
X }
X else debugger_sym_count++;
X }
X
X /* Count one for the local symbol that we generate,
X whose name is the file's name (usually) and whose address
X is the start of the file's text. */
X
X local_sym_count++;
X non_L_local_sym_count++;
X}
X
X/* Enter one global symbol in the hash table.
X NLIST_P points to the `struct nlist' read from the file
X that describes the global symbol. NAME is the symbol's name.
X ENTRY is the file entry for the file the symbol comes from.
X
X The `struct nlist' is modified by placing it on a chain of
X all such structs that refer to the same global symbol.
X This chain starts in the `refs' field of the symbol table entry
X and is chained through the `n_name'. */
X
Xvoid
Xenter_global_ref (nlist_p, name, entry)
X register struct nlist *nlist_p;
X char *name;
X struct file_entry *entry;
X{
X register symbol *sp = getsym (name);
X register int type = nlist_p->n_type;
X int oldref = sp->referenced;
X int olddef = sp->defined;
X
X nlist_p->n_un.n_name = (char *) sp->refs;
X sp->refs = nlist_p;
X
X sp->referenced = 1;
X if (type != (N_UNDF | N_EXT) || nlist_p->n_value)
X {
X if (!sp->defined || sp->defined == (N_UNDF | N_EXT))
X sp->defined = type;
X
X if (oldref && !olddef)
X /* It used to be undefined and we're defining it. */
X undefined_global_sym_count--;
X
X if (!olddef && type == (N_UNDF | N_EXT) && nlist_p->n_value)
X {
X /* First definition and it's common. */
X common_defined_global_count++;
X sp->max_common_size = nlist_p->n_value;
X }
X else if (olddef && sp->max_common_size && type != (N_UNDF | N_EXT))
X {
X /* It used to be common and we're defining it as
X something else. */
X common_defined_global_count--;
X sp->max_common_size = 0;
X }
X else if (olddef && sp->max_common_size && type == (N_UNDF | N_EXT)
X && sp->max_common_size < nlist_p->n_value)
X /* It used to be common and this is a new common entry to
X which we need to pay attention. */
X sp->max_common_size = nlist_p->n_value;
X
X /* Are we defining it as a set element? */
X if (SET_ELEMENT_P (type)
X && (!olddef || (olddef && sp->max_common_size)))
X set_vector_count++;
X /* As an indirection? */
X else if (type == (N_INDR | N_EXT))
X {
X /* Indirect symbols value should be modified to point
X a symbol being equivalenced to. */
X nlist_p->n_value
X = (unsigned int) getsym ((nlist_p + 1)->n_un.n_strx
X + entry->strings);
X if ((symbol *) nlist_p->n_value == sp)
X {
X /* Somebody redefined a symbol to be itself. */
X fprintf (stderr, "%s: Symbol %s indirected to itself.\n",
X entry->filename, name);
X /* Rewrite this symbol as being a global text symbol
X with value 0. */
X nlist_p->n_type = sp->defined = N_TEXT | N_EXT;
X nlist_p->n_value = 0;
X /* Don't make the output executable. */
X make_executable = 0;
X }
X else
X global_indirect_count++;
X }
X }
X else
X if (!oldref)
X#ifndef DOLLAR_KLUDGE
X undefined_global_sym_count++;
X#else
X {
X if (entry->superfile && type == (N_UNDF | N_EXT) && name[1] == '$')
X {
X /* This is an (ISI?) $-conditional; skip it */
X sp->referenced = 0;
X if (sp->trace)
X {
X fprintf (stderr, "symbol %s is a $-conditional ignored in ", sp->name);
X print_file_name (entry, stderr);
X fprintf (stderr, "\n");
X }
X return;
X }
X else
X undefined_global_sym_count++;
X }
X#endif
X
X if (sp == end_symbol && entry->just_syms_flag && !T_flag_specified)
X text_start = nlist_p->n_value;
X
X if (sp->trace)
X {
X register char *reftype;
X switch (type & N_TYPE)
X {
X case N_UNDF:
X if (nlist_p->n_value)
X reftype = "defined as common";
X else reftype = "referenced";
X break;
X
X case N_ABS:
X reftype = "defined as absolute";
X break;
X
X case N_TEXT:
X reftype = "defined in text section";
X break;
X
X case N_DATA:
X reftype = "defined in data section";
X break;
X
X case N_BSS:
X reftype = "defined in BSS section";
X break;
X
X case N_SETT:
X reftype = "is a text set element";
X break;
X
X case N_SETD:
X reftype = "is a data set element";
X break;
X

X case N_SETB:
X reftype = "is a BSS set element";
X break;
X
X case N_SETA:
X reftype = "is an absolute set element";
X break;
X
X case N_SETV:
X reftype = "defined in data section as vector";
X break;
X
X case N_INDR:
X reftype = (char *) alloca (23
X + strlen ((nlist_p + 1)->n_un.n_strx
X + entry->strings));
X sprintf (reftype, "defined equivalent to %s",
X (nlist_p + 1)->n_un.n_strx + entry->strings);
X break;
X
X#ifdef sequent
X case N_SHUNDF:
X reftype = "shared undf";
X break;
X
X/* These conflict with cases above.
X case N_SHDATA:
X reftype = "shared data";
X break;
X
X case N_SHBSS:
X reftype = "shared BSS";
X break;
X*/
X default:
X reftype = "I don't know this type";
X break;
X#endif
X }
X
X fprintf (stderr, "symbol %s %s in ", sp->name, reftype);
X print_file_name (entry, stderr);
X fprintf (stderr, "\n");
X }
X}
X
X/* This return 0 if the given file entry's symbol table does *not*
X contain the nlist point entry, and it returns the files entry
X pointer (cast to unsigned long) if it does. */
X
Xunsigned long
Xcontains_symbol (entry, n_ptr)
X struct file_entry *entry;
X register struct nlist *n_ptr;
X{
X if (n_ptr >= entry->symbols &&
X n_ptr < (entry->symbols
X + (entry->header.a_syms / sizeof (struct nlist))))
X return (unsigned long) entry;
X return 0;
X}
X
X
X/* Searching libraries */
X
Xstruct file_entry *decode_library_subfile ();
Xvoid linear_library (), symdef_library ();
X
X/* Search the library ENTRY, already open on descriptor DESC.
X This means deciding which library members to load,
X making a chain of `struct file_entry' for those members,
X and entering their global symbols in the hash table. */
X
Xvoid
Xsearch_library (desc, entry)
X int desc;
X struct file_entry *entry;
X{
X int member_length;
X register char *name;
X register struct file_entry *subentry;
X
X if (!undefined_global_sym_count) return;
X
X /* Examine its first member, which starts SARMAG bytes in. */
X subentry = decode_library_subfile (desc, entry, SARMAG, &member_length);
X if (!subentry) return;
X
X name = subentry->filename;
X free (subentry);
X
X /* Search via __.SYMDEF if that exists, else linearly. */
X
X if (!strcmp (name, "__.SYMDEF"))
X symdef_library (desc, entry, member_length);
X else
X linear_library (desc, entry);
X}
X
X/* Construct and return a file_entry for a library member.
X The library's file_entry is library_entry, and the library is open on DESC.
X SUBFILE_OFFSET is the byte index in the library of this member's header.
X We store the length of the member into *LENGTH_LOC. */
X
Xstruct file_entry *
Xdecode_library_subfile (desc, library_entry, subfile_offset, length_loc)
X int desc;
X struct file_entry *library_entry;
X int subfile_offset;
X int *length_loc;
X{
X int bytes_read;
X register int namelen;
X int member_length;
X register char *name;
X struct ar_hdr hdr1;
X register struct file_entry *subentry;
X
X lseek (desc, subfile_offset, 0);
X
X bytes_read = read (desc, &hdr1, sizeof hdr1);
X if (!bytes_read)
X return 0; /* end of archive */
X
X if (sizeof hdr1 != bytes_read)
X fatal_with_file ("malformed library archive ", library_entry);
X
X if (sscanf (hdr1.ar_size, "%d", &member_length) != 1)
X fatal_with_file ("malformatted header of archive member in ", library_entry);
X
X subentry = (struct file_entry *) xmalloc (sizeof (struct file_entry));
X bzero (subentry, sizeof (struct file_entry));
X
X for (namelen = 0;
X namelen < sizeof hdr1.ar_name
X && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' '
X && hdr1.ar_name[namelen] != '/';
X namelen++);
X
X name = (char *) xmalloc (namelen+1);
X strncpy (name, hdr1.ar_name, namelen);
X name[namelen] = 0;
X
X subentry->filename = name;
X subentry->local_sym_name = name;
X subentry->symbols = 0;
X subentry->strings = 0;
X subentry->subfiles = 0;
X subentry->starting_offset = subfile_offset + sizeof hdr1;
X subentry->superfile = library_entry;
X subentry->library_flag = 0;
X subentry->header_read_flag = 0;
X subentry->just_syms_flag = 0;
X subentry->chain = 0;
X subentry->total_size = member_length;
X
X (*length_loc) = member_length;
X
X return subentry;
X}
X
Xint subfile_wanted_p ();
X
X/* Search a library that has a __.SYMDEF member.
X DESC is a descriptor on which the library is open.
X The file pointer is assumed to point at the __.SYMDEF data.
X ENTRY is the library's file_entry.
X MEMBER_LENGTH is the length of the __.SYMDEF data. */
X
Xvoid
Xsymdef_library (desc, entry, member_length)
X int desc;
X struct file_entry *entry;
X int member_length;
X{
X int *symdef_data = (int *) xmalloc (member_length);
X register struct symdef *symdef_base;
X char *sym_name_base;
X int number_of_symdefs;
X int length_of_strings;
X int not_finished;
X int bytes_read;
X register int i;
X struct file_entry *prev = 0;
X int prev_offset = 0;
X
X bytes_read = read (desc, symdef_data, member_length);
X if (bytes_read != member_length)
X fatal_with_file ("malformatted __.SYMDEF in ", entry);
X
X number_of_symdefs = *symdef_data / sizeof (struct symdef);
X if (number_of_symdefs < 0 ||
X number_of_symdefs * sizeof (struct symdef) + 2 * sizeof (int) > member_length)
X fatal_with_file ("malformatted __.SYMDEF in ", entry);
X
X symdef_base = (struct symdef *) (symdef_data + 1);
X length_of_strings = *(int *) (symdef_base + number_of_symdefs);
X
X if (length_of_strings < 0
X || number_of_symdefs * sizeof (struct symdef) + length_of_strings
X + 2 * sizeof (int) != member_length)
X fatal_with_file ("malformatted __.SYMDEF in ", entry);
X
X sym_name_base = sizeof (int) + (char *) (symdef_base + number_of_symdefs);
X
X /* Check all the string indexes for validity. */
X
X for (i = 0; i < number_of_symdefs; i++)
X {
X register int index = symdef_base[i].symbol_name_string_index;
X if (index < 0 || index >= length_of_strings
X || (index && *(sym_name_base + index - 1)))
X fatal_with_file ("malformatted __.SYMDEF in ", entry);
X }
X
X /* Search the symdef data for members to load.
X Do this until one whole pass finds nothing to load. */
X
X not_finished = 1;
X while (not_finished)
X {
X not_finished = 0;
X
X /* Scan all the symbols mentioned in the symdef for ones that we need.
X Load the library members that contain such symbols. */
X
X for (i = 0;
X (i < number_of_symdefs
X && (undefined_global_sym_count || common_defined_global_count));
X i++)
X if (symdef_base[i].symbol_name_string_index >= 0)
X {
X register symbol *sp;
X
X sp = getsym_soft (sym_name_base
X + symdef_base[i].symbol_name_string_index);
X
X /* If we find a symbol that appears to be needed, think carefully
X about the archive member that the symbol is in. */
X
X if (sp && ((sp->referenced && !sp->defined)
X || (sp->defined && sp->max_common_size)))
X {
X int junk;
X register int j;
X register int offset = symdef_base[i].library_member_offset;
X struct file_entry *subentry;
X
X /* Don't think carefully about any archive member
X more than once in a given pass. */
X
X if (prev_offset == offset)
X continue;
X prev_offset = offset;
X
X /* Read the symbol table of the archive member. */
X
X subentry = decode_library_subfile (desc, entry, offset, &junk);
X if (subentry == 0)
X fatal ("invalid offset for %s in symbol table of %s",
X sym_name_base
X + symdef_base[i].symbol_name_string_index,
X entry->filename);
X read_entry_symbols (desc, subentry);
X subentry->strings = (char *) malloc (subentry->string_size);
X read_entry_strings (desc, subentry);
X
X /* Now scan the symbol table and decide whether to load. */
X
X if (!subfile_wanted_p (subentry))

X {
X free (subentry->symbols);
X free (subentry);
X }
X else
X {
X /* This member is needed; load it.
X Since we are loading something on this pass,
X we must make another pass through the symdef data. */
X
X not_finished = 1;
X
X enter_file_symbols (subentry);
X
X if (prev)
X prev->chain = subentry;
X else entry->subfiles = subentry;
X prev = subentry;
X
X /* Clear out this member's symbols from the symdef data
X so that following passes won't waste time on them. */
X
X for (j = 0; j < number_of_symdefs; j++)
X {
X if (symdef_base[j].library_member_offset == offset)
X symdef_base[j].symbol_name_string_index = -1;
X }
X }
X
X /* We'll read the strings again if we need them again. */
X free (subentry->strings);
X subentry->strings = 0;
X }
X }
X }
X
X free (symdef_data);
X}
X
X/* Search a library that has no __.SYMDEF.
X ENTRY is the library's file_entry.
X DESC is the descriptor it is open on. */
X
Xvoid
Xlinear_library (desc, entry)
X int desc;
X struct file_entry *entry;
X{
X register struct file_entry *prev = 0;
X register int this_subfile_offset = SARMAG;
X
X while (undefined_global_sym_count || common_defined_global_count)
X {
X int member_length;
X register struct file_entry *subentry;
X
X subentry = decode_library_subfile (desc, entry, this_subfile_offset,
X &member_length);
X
X if (!subentry) return;
X
X read_entry_symbols (desc, subentry);
X subentry->strings = (char *) alloca (subentry->string_size);
X read_entry_strings (desc, subentry);
X
X if (!subfile_wanted_p (subentry))
X {
X free (subentry->symbols);
X free (subentry);
X }
X else
X {
X enter_file_symbols (subentry);
X
X if (prev)
X prev->chain = subentry;
X else entry->subfiles = subentry;
X prev = subentry;
X subentry->strings = 0; /* Since space will dissapear on return */
X }
X
X this_subfile_offset += member_length + sizeof (struct ar_hdr);
X if (this_subfile_offset & 1) this_subfile_offset++;
X }
X}
X
X/* ENTRY is an entry for a library member.
X Its symbols have been read into core, but not entered.
X Return nonzero if we ought to load this member. */
X
Xint
Xsubfile_wanted_p (entry)
X struct file_entry *entry;
X{
X register struct nlist *p;
X register struct nlist *end
X = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
X#ifdef DOLLAR_KLUDGE
X register int dollar_cond = 0;
X#endif
X
X for (p = entry->symbols; p < end; p++)
X {
X register int type = p->n_type;
X register char *name = p->n_un.n_strx + entry->strings;
X
X /* If the symbol has an interesting definition, we could
X potentially want it. */
X if (type & N_EXT
X && (type != (N_UNDF | N_EXT) || p->n_value
X
X#ifdef DOLLAR_KLUDGE
X || name[1] == '$'
X#endif
X )
X && !SET_ELEMENT_P (type)
X && !set_element_prefixed_p (name))
X {
X register symbol *sp = getsym_soft (name);
X
X#ifdef DOLLAR_KLUDGE
X if (name[1] == '$')
X {
X sp = getsym_soft (&name[2]);
X dollar_cond = 1;
X if (!sp) continue;
X if (sp->referenced)
X {
X if (write_map)
X {
X print_file_name (entry, stdout);
X fprintf (stdout, " needed due to $-conditional %s\n", name);
X }
X return 1;
X }
X continue;
X }
X#endif
X
X /* If this symbol has not been hashed, we can't be looking for it. */
X
X if (!sp) continue;
X
X if ((sp->referenced && !sp->defined)
X || (sp->defined && sp->max_common_size))
X {
X /* This is a symbol we are looking for. It is either
X not yet defined or defined as a common. */
X#ifdef DOLLAR_KLUDGE
X if (dollar_cond) continue;
X#endif
X if (type == (N_UNDF | N_EXT))
X {
X /* Symbol being defined as common.
X Remember this, but don't load subfile just for this. */
X
X /* If it didn't used to be common, up the count of
X common symbols. */
X if (!sp->max_common_size)
X common_defined_global_count++;
X
X if (sp->max_common_size < p->n_value)
X sp->max_common_size = p->n_value;
X if (!sp->defined)
X undefined_global_sym_count--;
X sp->defined = 1;
X continue;
X }
X
X if (write_map)
X {
X print_file_name (entry, stdout);
X fprintf (stdout, " needed due to %s\n", sp->name);
X }
X return 1;
X }
X }
X }
X
X return 0;
X}
X
Xvoid consider_file_section_lengths (), relocate_file_addresses ();
X
X/* Having entered all the global symbols and found the sizes of sections
X of all files to be linked, make all appropriate deductions from this data.
X
X We propagate global symbol values from definitions to references.
X We compute the layout of the output file and where each input file's
X contents fit into it. */
X
Xvoid
Xdigest_symbols ()
X{
X register int i;
X int setv_fill_count;
X
X if (trace_files)
X fprintf (stderr, "Digesting symbol information:\n\n");
X
X /* Compute total size of sections */
X
X each_file (consider_file_section_lengths, 0);
X
X /* If necessary, pad text section to full page in the file.
X Include the padding in the text segment size. */
X
X#ifdef NMAGIC
X if (magic == ZMAGIC || magic == NMAGIC)
X#else
X if (magic == ZMAGIC)
X#endif
X {
X int text_end = text_size + N_TXTOFF (outheader);
X text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end;
X text_size += text_pad;
X }
X
X#ifdef _N_BASEADDR
X /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */
X outheader.a_entry = N_PAGSIZ (outheader);
X#endif
X
X outheader.a_text = text_size;
X#ifdef sequent
X outheader.a_text += N_ADDRADJ (outheader);
X#endif
X
X /* Make the data segment address start in memory on a suitable boundary. */
X
X if (! Tdata_flag_specified)
X data_start = N_DATADDR (outheader) + text_start - TEXT_START (outheader);
X
X /* Set up the set element vector */
X
X if (!relocatable_output)
X {
X /* The set sector size is the number of set elements + a word
X for each symbol for the length word at the beginning of the
X vector, plus a word for each symbol for a zero at the end of
X the vector (for incremental linking). */
X set_sect_size
X = (2 * set_symbol_count + set_vector_count) * sizeof (unsigned long);
X set_sect_start = data_start + data_size;
X data_size += set_sect_size;
X set_vectors = (unsigned long *) xmalloc (set_sect_size);
X setv_fill_count = 0;
X }
X
X /* Compute start addresses of each file's sections and symbols. */
X
X each_full_file (relocate_file_addresses, 0);
X
X /* Now, for each symbol, verify that it is defined globally at most once.
X Put the global value into the symbol entry.
X Common symbols are allocated here, in the BSS section.
X Each defined symbol is given a '->defined' field
X which is the correct N_ code for its definition,
X except in the case of common symbols with -r.
X Then make all the references point at the symbol entry
X instead of being chained together. */
X
X defined_global_sym_count = 0;
X
X for (i = 0; i < TABSIZE; i++)
X {
X register symbol *sp;
X for (sp = symtab[i]; sp; sp = sp->link)
X {
X /* For each symbol */
X register struct nlist *p, *next;
X int defs = 0, com = sp->max_common_size;
X struct nlist *first_definition;
X for (p = sp->refs; p; p = next)
X {
X register int type = p->n_type;
X
X if (SET_ELEMENT_P (type))
X {
X if (relocatable_output)
X fatal ("internal: global ref to set element with -r");
X if (!defs++)
X {
X sp->value = set_sect_start
X + setv_fill_count++ * sizeof (unsigned long);
X sp->defined = N_SETV | N_EXT;
X first_definition = p;
X }
X else if ((sp->defined & ~N_EXT) != N_SETV)
X {
X sp->multiply_defined = 1;
X multiple_def_count++;
X }
X set_vectors[setv_fill_count++] = p->n_value;
X }
X else if ((type & N_EXT) && type != (N_UNDF | N_EXT))
X {
X /* non-common definition */
X if (defs++ && sp->value != p->n_value)
X {
X sp->multiply_defined = 1;
X multiple_def_count++;
X }
X sp->value = p->n_value;
X sp->defined = type;
X first_definition = p;
X }
X next = (struct nlist *) p->n_un.n_name;
X p->n_un.n_name = (char *) sp;
X }
X /* Allocate as common if defined as common and not defined for real */
X if (com && !defs)
X {
X if (!relocatable_output || force_common_definition)
X {
X int align = sizeof (int);
X
X /* Round up to nearest sizeof (int). I don't know
X whether this is necessary or not (given that
X alignment is taken care of later), but it's
X traditional, so I'll leave it in. Note that if
X this size alignment is ever removed, ALIGN above
X will have to be initialized to 1 instead of
X sizeof (int). */
X
X com = (com + sizeof (int) - 1) & (- sizeof (int));
X
X while (!(com & align))
X align <<= 1;
X
X align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align;
X
X bss_size = ((((bss_size + data_size + data_start)
X + (align - 1)) & (- align))
X - data_size - data_start);
X
X sp->value = data_start + data_size + bss_size;
X sp->defined = N_BSS | N_EXT;
X bss_size += com;
X if (write_map)
X printf ("Allocating common %s: %x at %x\n",
X sp->name, com, sp->value);
X }
X else
X {
X sp->defined = 0;
X undefined_global_sym_count++;
X }
X }
X /* Set length word at front of vector and zero byte at end.
X Reverse the vector itself to put it in file order. */
X if ((sp->defined & ~N_EXT) == N_SETV)
X {
X unsigned long length_word_index
X = (sp->value - set_sect_start) / sizeof (unsigned long);
X unsigned long i, tmp;
X
X set_vectors[length_word_index]
X = setv_fill_count - 1 - length_word_index;
X
X /* Reverse the vector. */
X for (i = 1;
X i < (setv_fill_count - length_word_index - 1) / 2 + 1;
X i++)
X {
X tmp = set_vectors[length_word_index + i];
X set_vectors[length_word_index + i]
X = set_vectors[setv_fill_count - i];
X set_vectors[setv_fill_count - i] = tmp;
X }
X
X set_vectors[setv_fill_count++] = 0;
X }
X if (sp->defined)
X defined_global_sym_count++;
X }
X }
X
X if (end_symbol) /* These are null if -r. */
X {
X etext_symbol->value = text_size + text_start;
X edata_symbol->value = data_start + data_size;
X end_symbol->value = data_start + data_size + bss_size;
X }
X
X /* Figure the data_pad now, so that it overlaps with the bss addresses. */
X
X if (specified_data_size && specified_data_size > data_size)
X data_pad = specified_data_size - data_size;
X
X if (magic == ZMAGIC)
X data_pad = ((data_pad + data_size + page_size - 1) & (- page_size))
X - data_size;
X
X bss_size -= data_pad;
X if (bss_size < 0) bss_size = 0;
X
X data_size += data_pad;
X}
X
X/* Accumulate the section sizes of input file ENTRY
X into the section sizes of the output file. */
X
Xvoid
Xconsider_file_section_lengths (entry)
X register struct file_entry *entry;
X{
X if (entry->just_syms_flag)
X return;
X
X entry->text_start_address = text_size;
X /* If there were any vectors, we need to chop them off */
X text_size += entry->header.a_text;
X entry->data_start_address = data_size;
X data_size += entry->header.a_data;
X entry->bss_start_address = bss_size;
X bss_size += entry->header.a_bss;
X
X text_reloc_size += entry->header.a_trsize;
X data_reloc_size += entry->header.a_drsize;
X}
X
X/* Determine where the sections of ENTRY go into the output file,
X whose total section sizes are already known.
X Also relocate the addresses of the file's local and debugger symbols. */
X
Xvoid
Xrelocate_file_addresses (entry)
X register struct file_entry *entry;
X{
X entry->text_start_address += text_start;
X /* Note that `data_start' and `data_size' have not yet been
X adjusted for `data_pad'. If they had been, we would get the wrong
X results here. */
X entry->data_start_address += data_start;
X entry->bss_start_address += data_start + data_size;
X
X {
X register struct nlist *p;
X register struct nlist *end
X = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
X
X for (p = entry->symbols; p < end; p++)
X {
X /* If this belongs to a section, update it by the section's start address */
X register int type = p->n_type & N_TYPE;
X
X switch (type)
X {
X case N_TEXT:
X case N_SETT:
X p->n_value += entry->text_start_address;
X break;
X case N_DATA:
X case N_SETV:
X case N_SETD:
X /* A symbol whose value is in the data section
X is present in the input file as if the data section
X started at an address equal to the length of the file's text. */
X p->n_value += entry->data_start_address - entry->header.a_text;
X break;
X case N_BSS:
X case N_SETB:
X /* likewise for symbols with value in BSS. */
X p->n_value += entry->bss_start_address
X - entry->header.a_text - entry->header.a_data;
X break;
X }
X }
X }
X}
X
Xvoid describe_file_sections (), list_file_locals ();
X
X/* Print a complete or partial map of the output file. */
X
Xvoid
Xprint_symbols (outfile)
X FILE *outfile;
X{
X register int i;
X
X fprintf (outfile, "\nFiles:\n\n");
X
X each_file (describe_file_sections, outfile);
X
X fprintf (outfile, "\nGlobal symbols:\n\n");
X
X for (i = 0; i < TABSIZE; i++)
X {
X register symbol *sp;
X for (sp = symtab[i]; sp; sp = sp->link)
X {
X if (sp->defined == 1)
X fprintf (outfile, " %s: common, length 0x%x\n", sp->name, sp->max_common_size);
X if (sp->defined)
X fprintf (outfile, " %s: 0x%x\n", sp->name, sp->value);
X else if (sp->referenced)
X fprintf (outfile, " %s: undefined\n", sp->name);
X }
X }
X
X each_file (list_file_locals, outfile);
X}
X
Xvoid
Xdescribe_file_sections (entry, outfile)
X struct file_entry *entry;
X FILE *outfile;
X{
X fprintf (outfile, " ");
X print_file_name (entry, outfile);
X if (entry->just_syms_flag)
X fprintf (outfile, " symbols only\n", 0);
X else
X fprintf (outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n",
X entry->text_start_address, entry->header.a_text,
X entry->data_start_address, entry->header.a_data,
X entry->bss_start_address, entry->header.a_bss);
X}
X
Xvoid
Xlist_file_locals (entry, outfile)
X struct file_entry *entry;
X FILE *outfile;
X{
X register struct nlist
X *p,
X *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
X
X entry->strings = (char *) alloca (entry->string_size);
X read_entry_strings (file_open (entry), entry);
X
X fprintf (outfile, "\nLocal symbols of ");
X print_file_name (entry, outfile);
X fprintf (outfile, ":\n\n");
X
X for (p = entry->symbols; p < end; p++)
X /* If this is a definition,
X update it if necessary by this file's start address. */
X if (!(p->n_type & (N_STAB | N_EXT)))
X fprintf (outfile, " %s: 0x%x\n",
X entry->strings + p->n_un.n_strx, p->n_value);
X
X entry->strings = 0; /* All done with them. */
X}
X
X
X/* Static vars for do_warnings and subroutines of it */
Xint list_unresolved_refs; /* List unresolved refs */
Xint list_warning_symbols; /* List warning syms */
Xint list_multiple_defs; /* List multiple definitions */
X
X/*
X * Structure for communication between do_file_warnings and it's
X * helper routines. Will in practice be an array of three of these:
X * 0) Current line, 1) Next line, 2) Source file info.
X */
Xstruct line_debug_entry
X{
X int line;
X char *filename;
X struct nlist *sym;
X};
X
Xvoid qsort ();
X/*
X * Helper routines for do_file_warnings.
X */
X
X/* Return an integer less than, equal to, or greater than 0 as per the
X relation between the two relocation entries. Used by qsort. */
X
Xint
Xrelocation_entries_relation (rel1, rel2)
X struct relocation_info *rel1, *rel2;
X{
X return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2);
X}
X
X/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS
X determines the type of the debugging symbol to look for (DSLINE or
X SLINE). STATE_POINTER keeps track of the old and new locatiosn in
X the file. It assumes that state_pointer[1] is valid; ie
X that it.sym points into some entry in the symbol table. If
X state_pointer[1].sym == 0, this routine should not be called. */
X
Xint
Xnext_debug_entry (use_data_symbols, state_pointer)
X register int use_data_symbols;
X /* Next must be passed by reference! */
X struct line_debug_entry state_pointer[3];
X{
X register struct line_debug_entry
X *current = state_pointer,
X *next = state_pointer + 1,
X /* Used to store source file */
X *source = state_pointer + 2;
X struct file_entry *entry = (struct file_entry *) source->sym;
X
X current->sym = next->sym;
X current->line = next->line;
X current->filename = next->filename;
X
X while (++(next->sym) < (entry->symbols
X + entry->header.a_syms/sizeof (struct nlist)))
X {
X /* n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, so
X * may look negative...therefore, must mask to low bits
X */
X switch (next->sym->n_type & 0xff)
X {
X case N_SLINE:
X if (use_data_symbols) continue;
X next->line = next->sym->n_desc;
X return 1;
X case N_DSLINE:
X if (!use_data_symbols) continue;
X next->line = next->sym->n_desc;
X return 1;
X#ifdef HAVE_SUN_STABS
X case N_EINCL:
X next->filename = source->filename;
X continue;
X#endif
X case N_SO:
X source->filename = next->sym->n_un.n_strx + entry->strings;
X source->line++;
X#ifdef HAVE_SUN_STABS
X case N_BINCL:
X#endif
X case N_SOL:
X next->filename
X = next->sym->n_un.n_strx + entry->strings;
X default:
X continue;
X }
X }
X next->sym = (struct nlist *) 0;
X return 0;
X}
X
X/* Create a structure to save the state of a scan through the debug
X symbols. USE_DATA_SYMBOLS is set if we should be scanning for
X DSLINE's instead of SLINE's. entry is the file entry which points
X at the symbols to use. */
X
Xstruct line_debug_entry *
Xinit_debug_scan (use_data_symbols, entry)
X int use_data_symbols;
X struct file_entry *entry;
X{
X struct line_debug_entry
X *state_pointer
X = (struct line_debug_entry *)
X xmalloc (3 * sizeof (struct line_debug_entry));
X register struct line_debug_entry
X *current = state_pointer,
X *next = state_pointer + 1,
X *source = state_pointer + 2; /* Used to store source file */
X
X struct nlist *tmp;
X
X for (tmp = entry->symbols;
X tmp < (entry->symbols
X + entry->header.a_syms/sizeof (struct nlist));
X tmp++)
X if (tmp->n_type == (int) N_SO)
X break;
X
X if (tmp >= (entry->symbols
X + entry->header.a_syms/sizeof (struct nlist)))
X {
X /* I believe this translates to "We lose" */
X current->filename = next->filename = entry->filename;
X current->line = next->line = -1;
X current->sym = next->sym = (struct nlist *) 0;
X return state_pointer;
X }
X
X next->line = source->line = 0;
X next->filename = source->filename
X = (tmp->n_un.n_strx + entry->strings);
X source->sym = (struct nlist *) entry;
X next->sym = tmp;
X
X next_debug_entry (use_data_symbols, state_pointer); /* To setup next */
X
X if (!next->sym) /* No line numbers for this section; */
X /* setup output results as appropriate */
X {
X if (source->line)
X {
X current->filename = source->filename = entry->filename;
X current->line = -1; /* Don't print lineno */
X }
X else
X {
X current->filename = source->filename;
X current->line = 0;
X }
X return state_pointer;
X }
X
X
X next_debug_entry (use_data_symbols, state_pointer); /* To setup current */
X
X return state_pointer;
X}
X
X/* Takes an ADDRESS (in either text or data space) and a STATE_POINTER
X which describes the current location in the implied scan through
X the debug symbols within the file which ADDRESS is within, and
X returns the source line number which corresponds to ADDRESS. */
X
Xint
Xaddress_to_line (address, state_pointer)
X unsigned long address;
X /* Next must be passed by reference! */
X struct line_debug_entry state_pointer[3];
X{
X struct line_debug_entry
X *current = state_pointer,
X *next = state_pointer + 1;
X struct line_debug_entry *tmp_pointer;
X
X int use_data_symbols;
X
X if (next->sym)
X use_data_symbols = (next->sym->n_type & N_TYPE) == N_DATA;
X else
X return current->line;
X
X /* Go back to the beginning if we've already passed it. */
X if (current->sym->n_value > address)
X {
X tmp_pointer = init_debug_scan (use_data_symbols,
X (struct file_entry *)
X ((state_pointer + 2)->sym));
X state_pointer[0] = tmp_pointer[0];
X state_pointer[1] = tmp_pointer[1];
X state_pointer[2] = tmp_pointer[2];
X free (tmp_pointer);
X }
X
X /* If we're still in a bad way, return -1, meaning invalid line. */
X if (current->sym->n_value > address)
X return -1;
X
X while (next->sym
X && next->sym->n_value <= address
X && next_debug_entry (use_data_symbols, state_pointer))
X ;
X return current->line;
X}
X
X
X/* Macros for manipulating bitvectors. */
X#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7))
X#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7))
X
X/* This routine will scan through the relocation data of file ENTRY,
X printing out references to undefined symbols and references to
X symbols defined in files with N_WARNING symbols. If DATA_SEGMENT
X is non-zero, it will scan the data relocation segment (and use
X N_DSLINE symbols to track line number); otherwise it will scan the
X text relocation segment. Warnings will be printed on the output
X stream OUTFILE. Eventually, every nlist symbol mapped through will
X be marked in the NLIST_BITVECTOR, so we don't repeat ourselves when
X we scan the nlists themselves. */
X
Xdo_relocation_warnings (entry, data_segment, outfile, nlist_bitvector)
X struct file_entry *entry;
X int data_segment;
X FILE *outfile;
X unsigned char *nlist_bitvector;
X{
X struct relocation_info
X *reloc_start = data_segment ? entry->datarel : entry->textrel,
X *reloc;
X int reloc_size
X = ((data_segment ? entry->header.a_drsize : entry->header.a_trsize)
X / sizeof (struct relocation_info));
X int start_of_segment
X = (data_segment ? entry->data_start_address : entry->text_start_address);
X struct nlist *start_of_syms = entry->symbols;
X struct line_debug_entry *state_pointer
X = init_debug_scan (data_segment != 0, entry);
X register struct line_debug_entry
X *current = state_pointer;
X /* Assigned to generally static values; should not be written into. */
X char *errfmt;
X /* Assigned to alloca'd values cand copied into; should be freed
X when done. */
X char *errmsg;
X int invalidate_line_number;
X
X /* We need to sort the relocation info here. Sheesh, so much effort
X for one lousy error optimization. */
X
X qsort (reloc_start, reloc_size, sizeof (struct relocation_info),
X relocation_entries_relation);
X
X for (reloc = reloc_start;
X reloc < (reloc_start + reloc_size);
X reloc++)
X {
X register struct nlist *s;
X register symbol *g;
X
X /* If the relocation isn't resolved through a symbol, continue */
X if (!RELOC_EXTERN_P(reloc))
X continue;
X
X s = &(entry->symbols[RELOC_SYMBOL(reloc)]);
X
X /* Local symbols shouldn't ever be used by relocation info, so
X the next should be safe.
X This is, of course, wrong. References to local BSS symbols can be
X the targets of relocation info, and they can (must) be
X resolved through symbols. However, these must be defined properly,
X (the assembler would have caught it otherwise), so we can
X ignore these cases. */
X if (!(s->n_type & N_EXT))
X continue;
X
X g = (symbol *) s->n_un.n_name;
X errmsg = 0;
X
X if (!g->defined && list_unresolved_refs) /* Reference */
X {
X /* Mark as being noted by relocation warning pass. */
X SET_BIT (nlist_bitvector, s - start_of_syms);
X
X if (g->undef_refs >= MAX_UREFS_PRINTED) /* Listed too many */
X continue;
X
X /* Undefined symbol which we should mention */
X
X if (++(g->undef_refs) == MAX_UREFS_PRINTED)
X {
X errfmt = "More undefined symbol %s refs follow";
X invalidate_line_number = 1;
X }
X else
X {
X errfmt = "Undefined symbol %s referenced from %s segment";
X invalidate_line_number = 0;
X }
X }
X else /* Defined */
X {
X /* Potential symbol warning here */
X if (!g->warning) continue;
X
X /* Mark as being noted by relocation warning pass. */
X SET_BIT (nlist_bitvector, s - start_of_syms);
X
X errfmt = 0;
X errmsg = g->warning;
X invalidate_line_number = 0;
X }
X
X
X /* If errfmt == 0, errmsg has already been defined. */
X if (errfmt != 0)
X {
X char *nm;
X
X if (demangler == NULL || (nm = (*demangler)(g->name)) == NULL)
X nm = g->name;
X errmsg = (char *) xmalloc (strlen (errfmt) + strlen (nm) + 1);
X sprintf (errmsg, errfmt, nm, data_segment ? "data" : "text");
X if (nm != g->name)
X free (nm);
X }
X
X address_to_line (RELOC_ADDRESS (reloc) + start_of_segment,
X state_pointer);
X
X if (current->line >=0)
X fprintf (outfile, "%s:%d: %s\n", current->filename,
X invalidate_line_number ? 0 : current->line, errmsg);
X else
X fprintf (outfile, "%s: %s\n", current->filename, errmsg);
X
X if (errfmt != 0)
X free (errmsg);
X }
X
X free (state_pointer);
X}
X
X/* Print on OUTFILE a list of all warnings generated by references
X and/or definitions in the file ENTRY. List source file and line
X number if possible, just the .o file if not. */
X
Xvoid
Xdo_file_warnings (entry, outfile)
X struct file_entry *entry;
X FILE *outfile;
X{
X int number_of_syms = entry->header.a_syms / sizeof (struct nlist);
X unsigned char *nlist_bitvector
X = (unsigned char *) alloca ((number_of_syms >> 3) + 1);
X struct line_debug_entry *text_scan, *data_scan;
X int i;
X char *errfmt, *file_name;
X int line_number;
X int dont_allow_symbol_name;
X
X bzero (nlist_bitvector, (number_of_syms >> 3) + 1);
X
X /* Read in the files strings if they aren't available */
X if (!entry->strings)
X {
X int desc;
X
X entry->strings = (char *) alloca (entry->string_size);
X desc = file_open (entry);
X read_entry_strings (desc, entry);
X }
X
X read_file_relocation (entry);
X
X /* Do text warnings based on a scan through the relocation info. */
X do_relocation_warnings (entry, 0, outfile, nlist_bitvector);
X
X /* Do data warnings based on a scan through the relocation info. */
X do_relocation_warnings (entry, 1, outfile, nlist_bitvector);
X
X /* Scan through all of the nlist entries in this file and pick up
X anything that the scan through the relocation stuff didn't. */
X
X text_scan = init_debug_scan (0, entry);
X data_scan = init_debug_scan (1, entry);
X
X for (i = 0; i < number_of_syms; i++)
X {
X struct nlist *s;
X struct glosym *g;
X
X s = entry->symbols + i;
X
X if (!(s->n_type & N_EXT))
X continue;
X
X g = (symbol *) s->n_un.n_name;
X dont_allow_symbol_name = 0;
X
X if (list_multiple_defs && g->multiply_defined)
X {
X errfmt = "Definition of symbol %s (multiply defined)";
X switch (s->n_type)
X {
X case N_TEXT | N_EXT:
X line_number = address_to_line (s->n_value, text_scan);
X file_name = text_scan[0].filename;
X break;
X case N_DATA | N_EXT:
X line_number = address_to_line (s->n_value, data_scan);
X file_name = data_scan[0].filename;
X break;
X case N_SETA | N_EXT:
X case N_SETT | N_EXT:
X case N_SETD | N_EXT:
X case N_SETB | N_EXT:
X if (g->multiply_defined == 2)
X continue;
X errfmt = "First set element definition of symbol %s (multiply defined)";
X break;
X default:
X continue; /* Don't print out multiple defs
X at references. */
X }
X }
X else if (BIT_SET_P (nlist_bitvector, i))
X continue;
X else if (list_unresolved_refs && !g->defined)
X {
X if (g->undef_refs >= MAX_UREFS_PRINTED)
X continue;
X
X if (++(g->undef_refs) == MAX_UREFS_PRINTED)
X errfmt = "More undefined \"%s\" refs follow";
X else
X errfmt = "Undefined symbol \"%s\" referenced";
X line_number = -1;
X }
X else if (g->warning)
X {
X /* There are two cases in which we don't want to
X do this. The first is if this is a definition instead of
X a reference. The second is if it's the reference used by
X the warning stabs itself. */
X if (s->n_type != (N_EXT | N_UNDF)
X || (i && (s-1)->n_type == N_WARNING))
X continue;
X
X errfmt = g->warning;
X line_number = -1;
X dont_allow_symbol_name = 1;
X }
X else
X continue;
X
X if (line_number == -1)
X fprintf (outfile, "%s: ", entry->filename);
X else
X fprintf (outfile, "%s:%d: ", file_name, line_number);
X
X if (dont_allow_symbol_name)
X fprintf (outfile, "%s", errfmt);
X else
X {
X char *nm;
X if (demangler != NULL && (nm = (*demangler)(g->name)) != NULL)
X {
X fprintf (outfile, errfmt, nm);
X free (nm);
X }
X else
X fprintf (outfile, errfmt, g->name);
X }
X
X fputc ('\n', outfile);
X }
X free (text_scan);
X free (data_scan);
X entry->strings = 0; /* Since it will dissapear anyway. */
X}
X
Xdo_warnings (outfile)
X FILE *outfile;
X{
X list_unresolved_refs = !relocatable_output && undefined_global_sym_count;
X list_warning_symbols = warning_count;
X list_multiple_defs = multiple_def_count != 0;
X
X if (!(list_unresolved_refs ||
X list_warning_symbols ||
X list_multiple_defs ))
X /* No need to run this routine */
X return;
X
X each_file (do_file_warnings, outfile);
X
X if (list_unresolved_refs || list_multiple_defs)
X make_executable = 0;
X}
X
X/* Write the output file */
X
Xvoid
Xwrite_output ()
X{
X struct stat statbuf;
X int filemode;
X
X (void) unlink (output_filename);
X outdesc = open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
X if (outdesc < 0) perror_name (output_filename);
X
X if (fstat (outdesc, &statbuf) < 0)
X perror_name (output_filename);
X
X filemode = statbuf.st_mode;
X
X chmod (output_filename, filemode & ~0111);
X
X /* Output the a.out header. */
X write_header ();
X
X /* Output the text and data segments, relocating as we go. */
X write_text ();
X write_data ();
X
X /* Output the merged relocation info, if requested with `-r'. */
X if (relocatable_output)
X write_rel ();
X
X /* Output the symbol table (both globals and locals). */
X write_syms ();
X
X /* Copy any GDB symbol segments from input files. */
X write_symsegs ();
X
X close (outdesc);
X
X if (chmod (output_filename, filemode | 0111) == -1)
X perror_name (output_filename);
X}
X
Xvoid modify_location (), perform_relocation (), copy_text (), copy_data ();
X
Xvoid
Xwrite_header ()
X{
X N_SET_MAGIC (outheader, magic);
X outheader.a_text = text_size;
X#ifdef sequent
X outheader.a_text += N_ADDRADJ (outheader);
X if (entry_symbol == 0)
X entry_symbol = getsym("start");
X#endif
X outheader.a_data = data_size;
X outheader.a_bss = bss_size;
X outheader.a_entry = (entry_symbol ? entry_symbol->value
X : text_start + entry_offset);
X#ifdef COFF_ENCAPSULATE
X if (need_coff_header)
X {
X /* We are encapsulating BSD format within COFF format. */
X struct coffscn *tp, *dp, *bp;
X
X tp = &coffheader.scns[0];
X dp = &coffheader.scns[1];
X bp = &coffheader.scns[2];
X
X strcpy (tp->s_name, ".text");
X tp->s_paddr = text_start;
X tp->s_vaddr = text_start;
X tp->s_size = text_size;
X tp->s_scnptr = sizeof (struct coffheader) + sizeof (struct exec);
X tp->s_relptr = 0;
X tp->s_lnnoptr = 0;
X tp->s_nreloc = 0;
X tp->s_nlnno = 0;
X tp->s_flags = 0x20;
X strcpy (dp->s_name, ".data");
X dp->s_paddr = data_start;
X dp->s_vaddr = data_start;
X dp->s_size = data_size;
X dp->s_scnptr = tp->s_scnptr + tp->s_size;
X dp->s_relptr = 0;
X dp->s_lnnoptr = 0;
X dp->s_nreloc = 0;
X dp->s_nlnno = 0;
X dp->s_flags = 0x40;
X strcpy (bp->s_name, ".bss");
X bp->s_paddr = dp->s_vaddr + dp->s_size;
X bp->s_vaddr = bp->s_paddr;
X bp->s_size = bss_size;
X bp->s_scnptr = 0;
X bp->s_relptr = 0;
X bp->s_lnnoptr = 0;
X bp->s_nreloc = 0;
X bp->s_nlnno = 0;
X bp->s_flags = 0x80;
X
X coffheader.f_magic = COFF_MAGIC;
X coffheader.f_nscns = 3;
X /* store an unlikely time so programs can
X * tell that there is a bsd header
X */
X coffheader.f_timdat = 1;
X coffheader.f_symptr = 0;
X coffheader.f_nsyms = 0;
X coffheader.f_opthdr = 28;
X coffheader.f_flags = 0x103;
X /* aouthdr */
X coffheader.magic = ZMAGIC;
X coffheader.vstamp = 0;
X coffheader.tsize = tp->s_size;
X coffheader.dsize = dp->s_size;
X coffheader.bsize = bp->s_size;
X coffheader.entry = outheader.a_entry;
X coffheader.text_start = tp->s_vaddr;
X coffheader.data_start = dp->s_vaddr;
X }
X#endif
X
X#ifdef INITIALIZE_HEADER
X INITIALIZE_HEADER;
X#endif
X
X if (strip_symbols == STRIP_ALL)
X nsyms = 0;
X else
X {
X nsyms = (defined_global_sym_count
X + undefined_global_sym_count);
X if (discard_locals == DISCARD_L)
X nsyms += non_L_local_sym_count;
X else if (discard_locals == DISCARD_NONE)
X nsyms += local_sym_count;
X /* One extra for following reference on indirects */
X if (relocatable_output)
X nsyms += set_symbol_count + global_indirect_count;
X }
X
X if (strip_symbols == STRIP_NONE)
X nsyms += debugger_sym_count;
X
X outheader.a_syms = nsyms * sizeof (struct nlist);
X
X if (relocatable_output)
X {
X outheader.a_trsize = text_reloc_size;
X outheader.a_drsize = data_reloc_size;
X }
X else
X {
X outheader.a_trsize = 0;
X outheader.a_drsize = 0;
X }
X
X#ifdef COFF_ENCAPSULATE
X if (need_coff_header)
X mywrite (&coffheader, sizeof coffheader, 1, outdesc);
X#endif
X mywrite (&outheader, sizeof (struct exec), 1, outdesc);
X
X /* Output whatever padding is required in the executable file
X between the header and the start of the text. */
X
X#ifndef COFF_ENCAPSULATE
X padfile (N_TXTOFF (outheader) - sizeof outheader, outdesc);
X#endif
X}
X
X/* Relocate the text segment of each input file
X and write to the output file. */
X
Xvoid
Xwrite_text ()
X{
X if (trace_files)
X fprintf (stderr, "Copying and relocating text:\n\n");
X
X each_full_file (copy_text, 0);
X file_close ();
X
X if (trace_files)
X fprintf (stderr, "\n");
X
X padfile (text_pad, outdesc);
X}
X
Xint
Xtext_offset (entry)
X struct file_entry *entry;
X{
X return entry->starting_offset + N_TXTOFF (entry->header);
X}
X
X/* Read in all of the relocation information */
X
Xvoid
Xread_relocation ()
X{
X each_full_file (read_file_relocation, 0);
X}
X
X/* Read in the relocation sections of ENTRY if necessary */
X
Xvoid
Xread_file_relocation (entry)
X struct file_entry *entry;
X{
X register struct relocation_info *reloc;
X int desc;
X int read_return;
X
X desc = -1;
X if (!entry->textrel)
X {
X reloc = (struct relocation_info *) xmalloc (entry->header.a_trsize);
X desc = file_open (entry);
X lseek (desc,
X text_offset (entry) + entry->header.a_text + entry->header.a_data,
X L_SET);
X if (entry->header.a_trsize != (read_return = read (desc, reloc, entry->header.a_trsize)))
X {
X fprintf (stderr, "Return from read: %d\n", read_return);
X fatal_with_file ("premature eof in text relocation of ", entry);
X }
X entry->textrel = reloc;
X }
X
X if (!entry->datarel)
X {
X reloc = (struct relocation_info *) xmalloc (entry->header.a_drsize);
X if (desc == -1) desc = file_open (entry);
X lseek (desc,
X text_offset (entry) + entry->header.a_text
X + entry->header.a_data + entry->header.a_trsize,
X L_SET);
X if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize))
X fatal_with_file ("premature eof in data relocation of ", entry);
X entry->datarel = reloc;
X }
X}
X
X/* Read the text segment contents of ENTRY, relocate them,
X and write the result to the output file.
X If `-r', save the text relocation for later reuse. */
X
Xvoid
Xcopy_text (entry)
X struct file_entry *entry;
X{
X register char *bytes;
X register int desc;
X register struct relocation_info *reloc;
X
X if (trace_files)
X prline_file_name (entry, stderr);
X
X desc = file_open (entry);
X
X /* Allocate space for the file's text section */
X
X bytes = (char *) alloca (entry->header.a_text);
X
X /* Deal with relocation information however is appropriate */
X
X if (entry->textrel) reloc = entry->textrel;
X else if (relocatable_output)
X {
X read_file_relocation (entry);
X reloc = entry->textrel;
X }
X else
X {
X reloc = (struct relocation_info *) alloca (entry->header.a_trsize);
X lseek (desc, text_offset (entry) + entry->header.a_text + entry->header.a_data, 0);
X if (entry->header.a_trsize != read (desc, reloc, entry->header.a_trsize))
X fatal_with_file ("premature eof in text relocation of ", entry);
X }
X
X /* Read the text section into core. */
X
X lseek (desc, text_offset (entry), 0);
X if (entry->header.a_text != read (desc, bytes, entry->header.a_text))
X fatal_with_file ("premature eof in text section of ", entry);
X
X
X /* Relocate the text according to the text relocation. */
X
X perform_relocation (bytes, entry->text_start_address, entry->header.a_text,
X reloc, entry->header.a_trsize, entry);
X
X /* Write the relocated text to the output file. */
X
X mywrite (bytes, 1, entry->header.a_text, outdesc);
X}
X
X/* Relocate the data segment of each input file
X and write to the output file. */
X
Xvoid
Xwrite_data ()
X{
X if (trace_files)
X fprintf (stderr, "Copying and relocating data:\n\n");
X
X each_full_file (copy_data, 0);
X file_close ();
X
X /* Write out the set element vectors. See digest symbols for
X description of length of the set vector section. */
X
X if (set_vector_count)
X mywrite (set_vectors, 2 * set_symbol_count + set_vector_count,
X sizeof (unsigned long), outdesc);
X
X if (trace_files)
X fprintf (stderr, "\n");
X
X padfile (data_pad, outdesc);
X}
X
X/* Read the data segment contents of ENTRY, relocate them,
X and write the result to the output file.
X If `-r', save the data relocation for later reuse.
X See comments in `copy_text'. */
X
Xvoid
Xcopy_data (entry)
X struct file_entry *entry;
X{
X register struct relocation_info *reloc;
X register char *bytes;
X register int desc;
X
X if (trace_files)
X prline_file_name (entry, stderr);
X
X desc = file_open (entry);
X
X bytes = (char *) alloca (entry->header.a_data);
X
X if (entry->datarel) reloc = entry->datarel;
X else if (relocatable_output) /* Will need this again */
X {
X read_file_relocation (entry);
X reloc = entry->datarel;
X }
X else
X {
X reloc = (struct relocation_info *) alloca (entry->header.a_drsize);
X lseek (desc, text_offset (entry) + entry->header.a_text
X + entry->header.a_data + entry->header.a_trsize,
X 0);
X if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize))
X fatal_with_file ("premature eof in data relocation of ", entry);
X }
X
X lseek (desc, text_offset (entry) + entry->header.a_text, 0);
X if (entry->header.a_data != read (desc, bytes, entry->header.a_data))
X fatal_with_file ("premature eof in data section of ", entry);
X
X perform_relocation (bytes, entry->data_start_address - entry->header.a_text,
X entry->header.a_data, reloc, entry->header.a_drsize, entry);
X
X mywrite (bytes, 1, entry->header.a_data, outdesc);
X}
X
X/* Relocate ENTRY's text or data section contents.
X DATA is the address of the contents, in core.
X DATA_SIZE is the length of the contents.
X PC_RELOCATION is the difference between the address of the contents
X in the output file and its address in the input file.
X RELOC_INFO is the address of the relocation info, in core.
X RELOC_SIZE is its length in bytes. */
X/* This version is about to be severly hacked by Randy. Hope it
X works afterwards. */
Xvoid
Xperform_relocation (data, pc_relocation, data_size, reloc_info, reloc_size, entry)
X char *data;
X struct relocation_info *reloc_info;
X struct file_entry *entry;
X int pc_relocation;
X int data_size;
X int reloc_size;
X{
X register struct relocation_info *p = reloc_info;
X struct relocation_info *end
X = reloc_info + reloc_size / sizeof (struct relocation_info);
X int text_relocation = entry->text_start_address;
X int data_relocation = entry->data_start_address - entry->header.a_text;
X int bss_relocation
X = entry->bss_start_address - entry->header.a_text - entry->header.a_data;
X
X for (; p < end; p++)
X {
X register int relocation = 0;
X register int addr = RELOC_ADDRESS(p);
X register unsigned int mask = 0;
X
X if (addr >= data_size)
X fatal_with_file ("relocation address out of range in ", entry);
X
X if (RELOC_EXTERN_P(p))
X {
X int symindex = RELOC_SYMBOL (p) * sizeof (struct nlist);
X symbol *sp = ((symbol *)
X (((struct nlist *)
X (((char *)entry->symbols) + symindex))
X ->n_un.n_name));
X
X#ifdef N_INDR
X /* Resolve indirection */
X if ((sp->defined & ~N_EXT) == N_INDR)
X sp = (symbol *) sp->value;
X#endif
X
X if (symindex >= entry->header.a_syms)
X fatal_with_file ("relocation symbolnum out of range in ", entry);
X
X /* If the symbol is undefined, leave it at zero. */
X if (! sp->defined)
X relocation = 0;
X else
X relocation = sp->value;
X }
X else switch (RELOC_TYPE(p))
X {
X case N_TEXT:
X case N_TEXT | N_EXT:
X relocation = text_relocation;
X break;
X
X case N_DATA:
X case N_DATA | N_EXT:
X /* A word that points to beginning of the the data section
X initially contains not 0 but rather the "address" of that section
X in the input file, which is the length of the file's text. */
X relocation = data_relocation;
X break;
X
X case N_BSS:
X case N_BSS | N_EXT:
X /* Similarly, an input word pointing to the beginning of the bss
X initially contains the length of text plus data of the file. */
X relocation = bss_relocation;
X break;
X
X case N_ABS:
X case N_ABS | N_EXT:
X /* Don't know why this code would occur, but apparently it does. */
X break;
X
X default:
X fatal_with_file ("nonexternal relocation code invalid in ", entry);
X }
X
X#ifdef RELOC_ADD_EXTRA
X relocation += RELOC_ADD_EXTRA(p);
X if (relocatable_output)
X {
X /* Non-PC relative relocations which are absolute
X or which have become non-external now have fixed
X relocations. Set the ADD_EXTRA of this relocation
X to be the relocation we have now determined. */
X if (! RELOC_PCREL_P (p))
X {
X if ((int)p->r_type <= RELOC_32
X || RELOC_EXTERN_P (p) == 0)
X RELOC_ADD_EXTRA (p) = relocation;
X }
X /* External PC-relative relocations continue to move around;
X update their relocations by the amount they have moved
X so far. */
X else if (RELOC_EXTERN_P (p))
X RELOC_ADD_EXTRA (p) -= pc_relocation;
X continue;
X }
X#endif
X
X if (RELOC_PCREL_P(p))
X relocation -= pc_relocation;
X
X relocation >>= RELOC_VALUE_RIGHTSHIFT(p);
X
X /* Unshifted mask for relocation */
X mask = 1 << RELOC_TARGET_BITSIZE(p) - 1;
X mask |= mask - 1;
X relocation &= mask;
X
X /* Shift everything up to where it's going to be used */
X relocation <<= RELOC_TARGET_BITPOS(p);
X mask <<= RELOC_TARGET_BITPOS(p);
X
X switch (RELOC_TARGET_SIZE(p))
X {
X case 0:
X if (RELOC_MEMORY_SUB_P(p))
X relocation -= mask & *(char *) (data + addr);
X else if (RELOC_MEMORY_ADD_P(p))
X relocation += mask & *(char *) (data + addr);
X *(char *) (data + addr) &= ~mask;
X *(char *) (data + addr) |= relocation;
X break;
X
X case 1:
X if (RELOC_MEMORY_SUB_P(p))
X relocation -= mask & *(short *) (data + addr);
X else if (RELOC_MEMORY_ADD_P(p))
X relocation += mask & *(short *) (data + addr);
X *(short *) (data + addr) &= ~mask;
X *(short *) (data + addr) |= relocation;
X break;
X
X case 2:
X#ifndef _CROSS_TARGET_ARCH
X if (RELOC_MEMORY_SUB_P(p))
X relocation -= mask & *(long *) (data + addr);
X else if (RELOC_MEMORY_ADD_P(p))
X relocation += mask & *(long *) (data + addr);
X *(long *) (data + addr) &= ~mask;
X *(long *) (data + addr) |= relocation;
X#else
X /* Handle long word alignment requirements of SPARC architecture */
X /* WARNING: This fix makes an assumption on byte ordering */
X /* Marc Ullman, Stanford University Nov. 1 1989 */
X if (RELOC_MEMORY_SUB_P(p)) {
X relocation -= mask &
X ((*(unsigned short *) (data + addr) << 16) |
X *(unsigned short *) (data + addr + 2));
X } else if (RELOC_MEMORY_ADD_P(p)) {
X relocation += mask &
X ((*(unsigned short *) (data + addr) << 16) |
X *(unsigned short *) (data + addr + 2));
X }
X *(unsigned short *) (data + addr) &= (~mask >> 16);
X *(unsigned short *) (data + addr + 2) &= (~mask & 0xffff);
X *(unsigned short *) (data + addr) |= (relocation >> 16);
X *(unsigned short *) (data + addr + 2) |= (relocation & 0xffff);
X#endif
X break;
X
X default:
X fatal_with_file ("Unimplemented relocation field length in ", entry);
X }
X }
X}
X
X/* For relocatable_output only: write out the relocation,
X relocating the addresses-to-be-relocated. */
X
Xvoid coptxtrel (), copdatrel ();
X
Xvoid
Xwrite_rel ()
X{
X register int i;
X register int count = 0;
X
X if (trace_files)
X fprintf (stderr, "Writing text relocation:\n\n");
X
X /* Assign each global symbol a sequence number, giving the order
X in which `write_syms' will write it.
X This is so we can store the proper symbolnum fields
X in relocation entries we write. */
X
X for (i = 0; i < TABSIZE; i++)
X {
X symbol *sp;
X for (sp = symtab[i]; sp; sp = sp->link)
X if (sp->referenced || sp->defined)
X {
X sp->def_count = count++;
X /* Leave room for the reference required by N_INDR, if
X necessary. */
X if ((sp->defined & ~N_EXT) == N_INDR)
X count++;
X }
X }
X /* Correct, because if (relocatable_output), we will also be writing
X whatever indirect blocks we have. */
X if (count != defined_global_sym_count
X + undefined_global_sym_count + global_indirect_count)
X fatal ("internal error");
X
X /* Write out the relocations of all files, remembered from copy_text. */
X
X each_full_file (coptxtrel, 0);
X
X if (trace_files)
X fprintf (stderr, "\nWriting data relocation:\n\n");
X
X each_full_file (copdatrel, 0);
X
X if (trace_files)
X fprintf (stderr, "\n");
X}
X
Xvoid
Xcoptxtrel (entry)
X struct file_entry *entry;
X{
X register struct relocation_info *p, *end;
X register int reloc = entry->text_start_address;
X
X p = entry->textrel;
X end = (struct relocation_info *) (entry->header.a_trsize + (char *) p);
X while (p < end)
X {
X RELOC_ADDRESS(p) += reloc;
X if (RELOC_EXTERN_P(p))
X {
X register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist);
X symbol *symptr = ((symbol *)
X (((struct nlist *)
X (((char *)entry->symbols) + symindex))
X ->n_un.n_name));
X
X if (symindex >= entry->header.a_syms)
X fatal_with_file ("relocation symbolnum out of range in ", entry);
X
X#ifdef N_INDR
X /* Resolve indirection. */
X if ((symptr->defined & ~N_EXT) == N_INDR)
X symptr = (symbol *) symptr->value;
X#endif
X
X /* If the symbol is now defined, change the external relocation
X to an internal one. */
X
X if (symptr->defined)
X {
X RELOC_EXTERN_P(p) = 0;
X RELOC_SYMBOL(p) = (symptr->defined & N_TYPE);
X#ifdef RELOC_ADD_EXTRA
X /* If we aren't going to be adding in the value in
X memory on the next pass of the loader, then we need
X to add it in from the relocation entry. Otherwise
X the work we did in this pass is lost. */
X if (!RELOC_MEMORY_ADD_P(p))
X RELOC_ADD_EXTRA (p) += symptr->value;
X#endif
X }
X else
X /* Debugger symbols come first, so have to start this
X after them. */
X RELOC_SYMBOL(p) = (symptr->def_count + nsyms
X - defined_global_sym_count
X - undefined_global_sym_count
X - global_indirect_count);
X }
X p++;
X }
X mywrite (entry->textrel, 1, entry->header.a_trsize, outdesc);
X}
X
Xvoid
Xcopdatrel (entry)
X struct file_entry *entry;
X{
X register struct relocation_info *p, *end;
X /* Relocate the address of the relocation.
X Old address is relative to start of the input file's data section.
X New address is relative to start of the output file's data section. */
X register int reloc = entry->data_start_address - text_size;
X
X p = entry->datarel;
X end = (struct relocation_info *) (entry->header.a_drsize + (char *) p);
X while (p < end)
X {
X RELOC_ADDRESS(p) += reloc;
X if (RELOC_EXTERN_P(p))
X {
X register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist);
X symbol *symptr = ((symbol *)
X (((struct nlist *)
X (((char *)entry->symbols) + symindex))
X ->n_un.n_name));
X int symtype;
X
X if (symindex >= entry->header.a_syms)
X fatal_with_file ("relocation symbolnum out of range in ", entry);
X
X#ifdef N_INDR
X /* Resolve indirection. */
X if ((symptr->defined & ~N_EXT) == N_INDR)
X symptr = (symbol *) symptr->value;
X#endif
X
X symtype = symptr->defined & N_TYPE;
X
X if (force_common_definition
X || symtype == N_DATA || symtype == N_TEXT || symtype == N_ABS)
X {
X RELOC_EXTERN_P(p) = 0;
X RELOC_SYMBOL(p) = symtype;
X }
X else
X /* Debugger symbols come first, so have to start this
X after them. */
X RELOC_SYMBOL(p)
X = (((symbol *)
X (((struct nlist *)
X (((char *)entry->symbols) + symindex))
X ->n_un.n_name))
X ->def_count
X + nsyms - defined_global_sym_count
X - undefined_global_sym_count
X - global_indirect_count);
X }
X p++;
X }
X mywrite (entry->datarel, 1, entry->header.a_drsize, outdesc);
X}
X
Xvoid write_file_syms ();
Xvoid write_string_table ();
X
X/* Offsets and current lengths of symbol and string tables in output file. */
X
Xint symbol_table_offset;
Xint symbol_table_len;
X
X/* Address in output file where string table starts. */
Xint string_table_offset;
X
X/* Offset within string table
X where the strings in `strtab_vector' should be written. */
Xint string_table_len;
X
X/* Total size of string table strings allocated so far,
X including strings in `strtab_vector'. */
Xint strtab_size;
X
X/* Vector whose elements are strings to be added to the string table. */
Xchar **strtab_vector;
X
X/* Vector whose elements are the lengths of those strings. */
Xint *strtab_lens;
X
X/* Index in `strtab_vector' at which the next string will be stored. */
Xint strtab_index;
X
X/* Add the string NAME to the output file string table.
X Record it in `strtab_vector' to be output later.
X Return the index within the string table that this string will have. */
X
Xint
Xassign_string_table_index (name)
X char *name;
X{
X register int index = strtab_size;
X register int len = strlen (name) + 1;
X
X strtab_size += len;
X strtab_vector[strtab_index] = name;
X strtab_lens[strtab_index++] = len;
X
X return index;
X}
X
XFILE *outstream = (FILE *) 0;
X
X/* Write the contents of `strtab_vector' into the string table.
X This is done once for each file's local&debugger symbols
X and once for the global symbols. */
X
Xvoid
Xwrite_string_table ()
X{
X register int i;
X
X lseek (outdesc, string_table_offset + string_table_len, 0);
X
X if (!outstream)
X outstream = fdopen (outdesc, "w");
X
X for (i = 0; i < strtab_index; i++)
X {
X fwrite (strtab_vector[i], 1, strtab_lens[i], outstream);
X string_table_len += strtab_lens[i];
X }
X
X fflush (outstream);
X
X /* Report I/O error such as disk full. */
X if (ferror (outstream))
X perror_name (output_filename);
X}
X
X/* Write the symbol table and string table of the output file. */
X
Xvoid
Xwrite_syms ()
X{
X /* Number of symbols written so far. */
X int syms_written = 0;
X register int i;
X register symbol *sp;
X
X /* Buffer big enough for all the global symbols. One
X extra struct for each indirect symbol to hold the extra reference
X following. */
X struct nlist *buf
X = (struct nlist *) alloca ((defined_global_sym_count
X + undefined_global_sym_count
X + global_indirect_count)
X * sizeof (struct nlist));
X /* Pointer for storing into BUF. */
X register struct nlist *bufp = buf;
X
X /* Size of string table includes the bytes that store the size. */
X strtab_size = sizeof strtab_size;
X
X symbol_table_offset = N_SYMOFF (outheader);
X symbol_table_len = 0;
X string_table_offset = N_STROFF (outheader);
X string_table_len = strtab_size;
X
X if (strip_symbols == STRIP_ALL)
X return;
X
X /* Write the local symbols defined by the various files. */
X
X each_file (write_file_syms, &syms_written);
X file_close ();
X
X /* Now write out the global symbols. */
X
X /* Allocate two vectors that record the data to generate the string
X table from the global symbols written so far. This must include
X extra space for the references following indirect outputs. */
X
X strtab_vector = (char **) alloca ((num_hash_tab_syms
X + global_indirect_count) * sizeof (char *));
X strtab_lens = (int *) alloca ((num_hash_tab_syms
X + global_indirect_count) * sizeof (int));
X strtab_index = 0;
X
X /* Scan the symbol hash table, bucket by bucket. */
X
X for (i = 0; i < TABSIZE; i++)
X for (sp = symtab[i]; sp; sp = sp->link)
X {
X struct nlist nl;
X
X nl.n_other = 0;
X nl.n_desc = 0;
X
X /* Compute a `struct nlist' for the symbol. */
X
X if (sp->defined || sp->referenced)
X {
X /* common condition needs to be before undefined condition */
X /* because unallocated commons are set undefined in */
X /* digest_symbols */
X if (sp->defined > 1) /* defined with known type */
X {
X /* If the target of an indirect symbol has been
X defined and we are outputting an executable,
X resolve the indirection; it's no longer needed */
X if (!relocatable_output
X && ((sp->defined & N_TYPE) == N_INDR)
X && (((symbol *) sp->value)->defined > 1))
X {
X symbol *newsp = (symbol *) sp->value;
X nl.n_type = newsp->defined;
X nl.n_value = newsp->value;
X }
X else
X {
X nl.n_type = sp->defined;
X if (sp->defined != (N_INDR | N_EXT))
X nl.n_value = sp->value;
X else
X nl.n_value = 0;
X }
X }
X else if (sp->max_common_size) /* defined as common but not allocated. */
X {
X /* happens only with -r and not -d */
X /* write out a common definition */
X nl.n_type = N_UNDF | N_EXT;
X nl.n_value = sp->max_common_size;
X }
X else if (!sp->defined) /* undefined -- legit only if -r */
X {
X nl.n_type = N_UNDF | N_EXT;
X nl.n_value = 0;
X }
X else
X fatal ("internal error: %s defined in mysterious way", sp->name);
X
X /* Allocate string table space for the symbol name. */
X
X nl.n_un.n_strx = assign_string_table_index (sp->name);
X
X /* Output to the buffer and count it. */
X
X *bufp++ = nl;
X syms_written++;
X if (nl.n_type == (N_INDR | N_EXT))
X {
X struct nlist xtra_ref;
X xtra_ref.n_type == N_EXT | N_UNDF;
X xtra_ref.n_un.n_strx
X = assign_string_table_index (((symbol *) sp->value)->name);
X xtra_ref.n_other = 0;
X xtra_ref.n_desc = 0;
X xtra_ref.n_value = 0;
X *bufp++ = xtra_ref;
X syms_written++;
X }
X }
X }
X
X /* Output the buffer full of `struct nlist's. */
X
X lseek (outdesc, symbol_table_offset + symbol_table_len, 0);
X mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc);
X symbol_table_len += sizeof (struct nlist) * (bufp - buf);
X
X if (syms_written != nsyms)
X fatal ("internal error: wrong number of symbols written into output file", 0);
X
X if (symbol_table_offset + symbol_table_len != string_table_offset)
X fatal ("internal error: inconsistent symbol table length", 0);
X
X /* Now the total string table size is known, so write it.
X We are already positioned at the right place in the file. */
X
X mywrite (&strtab_size, sizeof (int), 1, outdesc); /* we're at right place */
X
X /* Write the strings for the global symbols. */
X
X write_string_table ();
X}
X
X/* Write the local and debugger symbols of file ENTRY.
X Increment *SYMS_WRITTEN_ADDR for each symbol that is written. */
X
X/* Note that we do not combine identical names of local symbols.
X dbx or gdb would be confused if we did that. */
X
Xvoid
Xwrite_file_syms (entry, syms_written_addr)
X struct file_entry *entry;
X int *syms_written_addr;
X{
X register struct nlist *p = entry->symbols;
X register struct nlist *end = p + entry->header.a_syms / sizeof (struct nlist);
X
X /* Buffer to accumulate all the syms before writing them.
X It has one extra slot for the local symbol we generate here. */
X struct nlist *buf
X = (struct nlist *) alloca (entry->header.a_syms + sizeof (struct nlist));
X register struct nlist *bufp = buf;
X
X /* Upper bound on number of syms to be written here. */
X int max_syms = (entry->header.a_syms / sizeof (struct nlist)) + 1;
X
X /* Make tables that record, for each symbol, its name and its name's length.
X The elements are filled in by `assign_string_table_index'. */
X
X strtab_vector = (char **) alloca (max_syms * sizeof (char *));
X strtab_lens = (int *) alloca (max_syms * sizeof (int));
X strtab_index = 0;
X
X /* Generate a local symbol for the start of this file's text. */
X
X if (discard_locals != DISCARD_ALL)
X {
X struct nlist nl;
X
X nl.n_type = N_TEXT;
X nl.n_un.n_strx = assign_string_table_index (entry->local_sym_name);
X nl.n_value = entry->text_start_address;
X nl.n_desc = 0;
X nl.n_other = 0;
X *bufp++ = nl;
X (*syms_written_addr)++;
X entry->local_syms_offset = *syms_written_addr * sizeof (struct nlist);
X }
X
X /* Read the file's string table. */
X
X entry->strings = (char *) alloca (entry->string_size);
X read_entry_strings (file_open (entry), entry);
X
X for (; p < end; p++)
X {
X register int type = p->n_type;
X register int write = 0;
X
X /* WRITE gets 1 for a non-global symbol that should be written. */
X
X
X if (SET_ELEMENT_P (type)) /* This occurs even if global. These */
X /* types of symbols are never written */
X /* globally, though they are stored */
X /* globally. */
X write = relocatable_output;
X else if (!(type & (N_STAB | N_EXT)))
X /* ordinary local symbol */
X write = ((discard_locals != DISCARD_ALL)
X && !(discard_locals == DISCARD_L &&
X (p->n_un.n_strx + entry->strings)[0] == LPREFIX)
X && type != N_WARNING);
X else if (!(type & N_EXT))
X /* debugger symbol */
X write = (strip_symbols == STRIP_NONE);
X
X if (write)
X {
X /* If this symbol has a name,
X allocate space for it in the output string table. */
X
X if (p->n_un.n_strx)
X p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx
X + entry->strings);
X
X /* Output this symbol to the buffer and count it. */
X
X *bufp++ = *p;
X (*syms_written_addr)++;
X }
X }
X
X /* All the symbols are now in BUF; write them. */
X
X lseek (outdesc, symbol_table_offset + symbol_table_len, 0);
X mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc);
X symbol_table_len += sizeof (struct nlist) * (bufp - buf);
X
X /* Write the string-table data for the symbols just written,
X using the data in vectors `strtab_vector' and `strtab_lens'. */
X
X write_string_table ();
X entry->strings = 0; /* Since it will dissapear anyway. */
X}
X
X/* Copy any GDB symbol segments from the input files to the output file.
X The contents of the symbol segment is copied without change
X except that we store some information into the beginning of it. */
X
Xvoid write_file_symseg ();
X
Xvoid
Xwrite_symsegs ()
X{
X each_file (write_file_symseg, 0);
X}
X
Xvoid
Xwrite_file_symseg (entry)
X struct file_entry *entry;
X{
X char buffer[4096];
X struct symbol_root root;
X int indesc;
X int len;
X
X if (entry->symseg_offset == 0)
X return;
X
X /* This entry has a symbol segment. Read the root of the segment. */
X
X indesc = file_open (entry);
X lseek (indesc, entry->symseg_offset + entry->starting_offset, 0);
X if (sizeof root != read (indesc, &root, sizeof root))
X fatal_with_file ("premature end of file in symbol segment of ", entry);
X
X /* Store some relocation info into the root. */
X
X root.ldsymoff = entry->local_syms_offset;
X root.textrel = entry->text_start_address;
X root.datarel = entry->data_start_address - entry->header.a_text;
X root.bssrel = entry->bss_start_address
X - entry->header.a_text - entry->header.a_data;
X root.databeg = entry->data_start_address - root.datarel;
X root.bssbeg = entry->bss_start_address - root.bssrel;
X
X /* Write the modified root into the output file. */
X
X mywrite (&root, sizeof root, 1, outdesc);
X
X /* Copy the rest of the symbol segment unchanged. */
X
X if (entry->superfile)
X {
X /* Library member: number of bytes to copy is determined
X from the member's total size. */
X
X int total = entry->total_size - entry->symseg_offset - sizeof root;
X
X while (total > 0)
X {
X len = read (indesc, buffer, min (sizeof buffer, total));
X
X if (len != min (sizeof buffer, total))
X fatal_with_file ("premature end of file in symbol segment of ", entry);
X total -= len;
X mywrite (buffer, len, 1, outdesc);
X }
X }
X else
X {
X /* A separate file: copy until end of file. */
X
X while (len = read (indesc, buffer, sizeof buffer))
X {
X mywrite (buffer, len, 1, outdesc);
X if (len < sizeof buffer)
X break;
X }
X }
X
X file_close ();
X}
X
X/* Create the symbol table entries for `etext', `edata' and `end'. */
X
Xvoid
Xsymtab_init ()
X{
X#ifndef nounderscore
X edata_symbol = getsym ("_edata");
X etext_symbol = getsym ("_etext");
X end_symbol = getsym ("_end");
X#else
X edata_symbol = getsym ("edata");
X etext_symbol = getsym ("etext");
X end_symbol = getsym ("end");
X#endif
X
X#ifdef sun
X {
X symbol *dynamic_symbol = getsym ("__DYNAMIC");
X dynamic_symbol->defined = N_ABS | N_EXT;
X dynamic_symbol->referenced = 1;
X dynamic_symbol->value = 0;
X }
X#endif
X
X#ifdef sequent
X {
X symbol *_387_flt_symbol = getsym ("_387_flt");
X _387_flt_symbol->defined = N_ABS | N_EXT;
X _387_flt_symbol->referenced = 1;
X _387_flt_symbol->value = 0;
X }
X#endif
X
X edata_symbol->defined = N_DATA | N_EXT;
X etext_symbol->defined = N_TEXT | N_EXT;
X end_symbol->defined = N_BSS | N_EXT;
X
X edata_symbol->referenced = 1;
X etext_symbol->referenced = 1;
X end_symbol->referenced = 1;
X}
X
X/* Compute the hash code for symbol name KEY. */
X
Xint
Xhash_string (key)
X char *key;
X{
X register char *cp;
X register int k;
X
X cp = key;
X k = 0;
X while (*cp)
X k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
X
X return k;
X}
X
X/* Get the symbol table entry for the global symbol named KEY.
X Create one if there is none. */
X
Xsymbol *
Xgetsym (key)
X char *key;
X{
X register int hashval;
X register symbol *bp;
X
X /* Determine the proper bucket. */
X
X hashval = hash_string (key) % TABSIZE;
X
X /* Search the bucket. */
X
X for (bp = symtab[hashval]; bp; bp = bp->link)
X if (! strcmp (key, bp->name))
X return bp;
X
X /* Nothing was found; create a new symbol table entry. */
X
X bp = (symbol *) xmalloc (sizeof (symbol));
X bp->refs = 0;
X bp->name = (char *) xmalloc (strlen (key) + 1);
X strcpy (bp->name, key);
X bp->defined = 0;
X bp->referenced = 0;
X bp->trace = 0;
X bp->value = 0;
X bp->max_common_size = 0;
X bp->warning = 0;
X bp->undef_refs = 0;
X bp->multiply_defined = 0;
X
X /* Add the entry to the bucket. */
X
X bp->link = symtab[hashval];
X symtab[hashval] = bp;
X
X ++num_hash_tab_syms;
X
X return bp;
X}
X
X/* Like `getsym' but return 0 if the symbol is not already known. */
X
Xsymbol *
Xgetsym_soft (key)
X char *key;
X{
X register int hashval;
X register symbol *bp;
X
X /* Determine which bucket. */
X
X hashval = hash_string (key) % TABSIZE;
X
X /* Search the bucket. */
X
X for (bp = symtab[hashval]; bp; bp = bp->link)
X if (! strcmp (key, bp->name))
X return bp;
X
X return 0;
X}
X
X/* Report a fatal error.
X STRING is a printf format string and ARG is one arg for it. */
X
Xvoid
Xfatal (string, arg)
X char *string, *arg;
X{
X fprintf (stderr, "ld: ");
X fprintf (stderr, string, arg);
X fprintf (stderr, "\n");
X exit (1);
X}
X
X/* Report a fatal error. The error message is STRING
X followed by the filename of ENTRY. */
X
Xvoid
Xfatal_with_file (string, entry)
X char *string;
X struct file_entry *entry;
X{
X fprintf (stderr, "ld: ");
X fprintf (stderr, string);
X print_file_name (entry, stderr);
X fprintf (stderr, "\n");
X exit (1);
X}
X
X/* Report a fatal error using the message for the last failed system call,
X followed by the string NAME. */
X
Xvoid
Xperror_name (name)
X char *name;
X{
X extern int errno, sys_nerr;
X extern char *sys_errlist[];
X char *s;
X
X if (errno < sys_nerr)
X s = concat ("", sys_errlist[errno], " for %s");
X else
X s = "cannot open %s";
X fatal (s, name);
X}
X
X/* Report a fatal error using the message for the last failed system call,
X followed by the name of file ENTRY. */
X
Xvoid
Xperror_file (entry)
X struct file_entry *entry;
X{
X extern int errno, sys_nerr;
X extern char *sys_errlist[];
X char *s;
X
X if (errno < sys_nerr)
X s = concat ("", sys_errlist[errno], " for ");
X else
X s = "cannot open ";
X fatal_with_file (s, entry);
X}
X
X/* Report a nonfatal error.
X STRING is a format for printf, and ARG1 ... ARG3 are args for it. */
X
Xvoid
Xerror (string, arg1, arg2, arg3)
X char *string, *arg1, *arg2, *arg3;
X{
X fprintf (stderr, "%s: ", progname);
X fprintf (stderr, string, arg1, arg2, arg3);
X fprintf (stderr, "\n");
X}
X
X
X/* Output COUNT*ELTSIZE bytes of data at BUF
X to the descriptor DESC. */
X
Xvoid
Xmywrite (buf, count, eltsize, desc)
X char *buf;
X int count;
X int eltsize;
X int desc;
X{
X register int val;
X register int bytes = count * eltsize;
X
X while (bytes > 0)
X {
X val = write (desc, buf, bytes);
X if (val <= 0)
X perror_name (output_filename);
X buf += val;
X bytes -= val;
X }
X}
X
X/* Output PADDING zero-bytes to descriptor OUTDESC.
X PADDING may be negative; in that case, do nothing. */
X
Xvoid
Xpadfile (padding, outdesc)
X int padding;
X int outdesc;
X{
X register char *buf;
X if (padding <= 0)
X return;
X
X buf = (char *) alloca (padding);
X bzero (buf, padding);
X mywrite (buf, padding, 1, outdesc);
X}
X
X/* Return a newly-allocated string
X whose contents concatenate the strings S1, S2, S3. */
X
Xchar *
Xconcat (s1, s2, s3)
X char *s1, *s2, *s3;
X{
X register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
X register char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
X
X strcpy (result, s1);
X strcpy (result + len1, s2);
X strcpy (result + len1 + len2, s3);
X result[len1 + len2 + len3] = 0;
X
X return result;
X}
X
X/* Parse the string ARG using scanf format FORMAT, and return the result.
X If it does not parse, report fatal error
X generating the error message using format string ERROR and ARG as arg. */
X
Xint
Xparse (arg, format, error)
X char *arg, *format;
X{
X int x;
X if (1 != sscanf (arg, format, &x))
X fatal (error, arg);
X return x;
X}
X
X/* Like malloc but get fatal error if memory is exhausted. */
X
Xint
Xxmalloc (size)
X int size;
X{
X register int result = malloc (size);
X if (!result)
X fatal ("virtual memory exhausted", 0);
X return result;
X}
X
X/* Like realloc but get fatal error if memory is exhausted. */
X
Xint
Xxrealloc (ptr, size)
X char *ptr;
X int size;
X{
X register int result = realloc (ptr, size);
X if (!result)
X fatal ("virtual memory exhausted", 0);
X return result;
X}
X
X#ifdef USG
X
Xvoid
Xbzero (p, n)
X char *p;
X{
X memset (p, 0, n);
X}
X
Xvoid
Xbcopy (from, to, n)
X char *from, *to;
X{
X memcpy (to, from, n);
X}
X
Xgetpagesize ()
X{
X return (4096);
X}
X
X#endif
X
X#if TARGET == SUN4
X
X/* Don't use local pagesize to build for Sparc. */
X
Xgetpagesize ()
X{
X return (8192);
X}
X#endif
!EOF
exit 0


  3 Responses to “Category : UNIX Files
Archive   : GPLUS6.ZIP
Filename : G6

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/