diff options
-rw-r--r-- | contrib/gcc/libgcc2.c | 2249 |
1 files changed, 1790 insertions, 459 deletions
diff --git a/contrib/gcc/libgcc2.c b/contrib/gcc/libgcc2.c index 99693bb..d1854ed 100644 --- a/contrib/gcc/libgcc2.c +++ b/contrib/gcc/libgcc2.c @@ -1,6 +1,6 @@ /* More subroutines needed by GCC output code on some machines. */ /* Compile this one with gcc. */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +/* Copyright (C) 1989, 92-97, 1998 Free Software Foundation, Inc. This file is part of GNU CC. @@ -31,6 +31,17 @@ Boston, MA 02111-1307, USA. */ do not apply. */ #include "tconfig.h" + +/* We disable this when inhibit_libc, so that gcc can still be built without + needing header files first. */ +/* ??? This is not a good solution, since prototypes may be required in + some cases for correct code. See also frame.c. */ +#ifndef inhibit_libc +/* fixproto guarantees these system headers exist. */ +#include <stdlib.h> +#include <unistd.h> +#endif + #include "machmode.h" #include "defaults.h" #ifndef L_trampoline @@ -42,10 +53,17 @@ Boston, MA 02111-1307, USA. */ #undef abort #endif -#if (SUPPORTS_WEAK == 1) && defined (ASM_OUTPUT_DEF) +#if (SUPPORTS_WEAK == 1) && (defined (ASM_OUTPUT_DEF) || defined (ASM_OUTPUT_WEAK_ALIAS)) #define WEAK_ALIAS #endif +/* In a cross-compilation situation, default to inhibiting compilation + of routines that use libc. */ + +#if defined(CROSS_COMPILE) && !defined(inhibit_libc) +#define inhibit_libc +#endif + /* Permit the tm.h file to select the endianness to use just for this file. This is used when the endianness is determined when the compiler is run. */ @@ -138,8 +156,7 @@ extern DItype __fixunstfdi (TFtype a); static inline #endif DItype -__negdi2 (u) - DItype u; +__negdi2 (DItype u) { DIunion w; DIunion uu; @@ -153,11 +170,11 @@ __negdi2 (u) } #endif +/* Unless shift functions are defined whith full ANSI prototypes, + parameter b will be promoted to int if word_type is smaller than an int. */ #ifdef L_lshrdi3 DItype -__lshrdi3 (u, b) - DItype u; - word_type b; +__lshrdi3 (DItype u, word_type b) { DIunion w; word_type bm; @@ -187,9 +204,7 @@ __lshrdi3 (u, b) #ifdef L_ashldi3 DItype -__ashldi3 (u, b) - DItype u; - word_type b; +__ashldi3 (DItype u, word_type b) { DIunion w; word_type bm; @@ -219,9 +234,7 @@ __ashldi3 (u, b) #ifdef L_ashrdi3 DItype -__ashrdi3 (u, b) - DItype u; - word_type b; +__ashrdi3 (DItype u, word_type b) { DIunion w; word_type bm; @@ -252,8 +265,7 @@ __ashrdi3 (u, b) #ifdef L_ffsdi2 DItype -__ffsdi2 (u) - DItype u; +__ffsdi2 (DItype u) { DIunion uu, w; uu.ll = u; @@ -273,8 +285,7 @@ __ffsdi2 (u) #ifdef L_muldi3 DItype -__muldi3 (u, v) - DItype u, v; +__muldi3 (DItype u, DItype v) { DIunion w; DIunion uu, vv; @@ -293,8 +304,7 @@ __muldi3 (u, v) #ifdef L_udiv_w_sdiv #if defined (sdiv_qrnnd) USItype -__udiv_w_sdiv (rp, a1, a0, d) - USItype *rp, a1, a0, d; +__udiv_w_sdiv (USItype *rp, USItype a1, USItype a0, USItype d) { USItype q, r; USItype c0, c1, b1; @@ -392,9 +402,13 @@ __udiv_w_sdiv (rp, a1, a0, d) #else /* If sdiv_qrnnd doesn't exist, define dummy __udiv_w_sdiv. */ USItype -__udiv_w_sdiv (rp, a1, a0, d) - USItype *rp, a1, a0, d; -{} +__udiv_w_sdiv (USItype *rp __attribute__ ((__unused__)), + USItype a1 __attribute__ ((__unused__)), + USItype a0 __attribute__ ((__unused__)), + USItype d __attribute__ ((__unused__))) +{ + return 0; +} #endif #endif @@ -421,9 +435,7 @@ static const UQItype __clz_tab[] = static inline #endif UDItype -__udivmoddi4 (n, d, rp) - UDItype n, d; - UDItype *rp; +__udivmoddi4 (UDItype n, UDItype d, UDItype *rp) { DIunion ww; DIunion nn, dd; @@ -533,7 +545,7 @@ __udivmoddi4 (n, d, rp) udiv_qrnnd (q1, n1, n2, n1, d0); } - /* n1 != d0... */ + /* n1 != d0... */ udiv_qrnnd (q0, n0, n1, n0, d0); @@ -644,8 +656,7 @@ __udivmoddi4 (n, d, rp) UDItype __udivmoddi4 (); DItype -__divdi3 (u, v) - DItype u, v; +__divdi3 (DItype u, DItype v) { word_type c = 0; DIunion uu, vv; @@ -672,8 +683,7 @@ __divdi3 (u, v) #ifdef L_moddi3 UDItype __udivmoddi4 (); DItype -__moddi3 (u, v) - DItype u, v; +__moddi3 (DItype u, DItype v) { word_type c = 0; DIunion uu, vv; @@ -699,8 +709,7 @@ __moddi3 (u, v) #ifdef L_umoddi3 UDItype __udivmoddi4 (); UDItype -__umoddi3 (u, v) - UDItype u, v; +__umoddi3 (UDItype u, UDItype v) { UDItype w; @@ -713,8 +722,7 @@ __umoddi3 (u, v) #ifdef L_udivdi3 UDItype __udivmoddi4 (); UDItype -__udivdi3 (n, d) - UDItype n, d; +__udivdi3 (UDItype n, UDItype d) { return __udivmoddi4 (n, d, (UDItype *) 0); } @@ -722,8 +730,7 @@ __udivdi3 (n, d) #ifdef L_cmpdi2 word_type -__cmpdi2 (a, b) - DItype a, b; +__cmpdi2 (DItype a, DItype b) { DIunion au, bu; @@ -743,8 +750,7 @@ __cmpdi2 (a, b) #ifdef L_ucmpdi2 word_type -__ucmpdi2 (a, b) - DItype a, b; +__ucmpdi2 (DItype a, DItype b) { DIunion au, bu; @@ -767,8 +773,7 @@ __ucmpdi2 (a, b) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) DItype -__fixunstfdi (a) - TFtype a; +__fixunstfdi (TFtype a) { TFtype b; UDItype v; @@ -797,8 +802,7 @@ __fixunstfdi (a) #if defined(L_fixtfdi) && (LONG_DOUBLE_TYPE_SIZE == 128) DItype -__fixtfdi (a) - TFtype a; +__fixtfdi (TFtype a) { if (a < 0) return - __fixunstfdi (-a); @@ -811,8 +815,7 @@ __fixtfdi (a) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) DItype -__fixunsxfdi (a) - XFtype a; +__fixunsxfdi (XFtype a) { XFtype b; UDItype v; @@ -841,8 +844,7 @@ __fixunsxfdi (a) #if defined(L_fixxfdi) && (LONG_DOUBLE_TYPE_SIZE == 96) DItype -__fixxfdi (a) - XFtype a; +__fixxfdi (XFtype a) { if (a < 0) return - __fixunsxfdi (-a); @@ -855,8 +857,7 @@ __fixxfdi (a) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) DItype -__fixunsdfdi (a) - DFtype a; +__fixunsdfdi (DFtype a) { DFtype b; UDItype v; @@ -885,8 +886,7 @@ __fixunsdfdi (a) #ifdef L_fixdfdi DItype -__fixdfdi (a) - DFtype a; +__fixdfdi (DFtype a) { if (a < 0) return - __fixunsdfdi (-a); @@ -946,21 +946,16 @@ __fixsfdi (SFtype a) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) XFtype -__floatdixf (u) - DItype u; +__floatdixf (DItype u) { XFtype d; - SItype negate = 0; - if (u < 0) - u = -u, negate = 1; - - d = (USItype) (u >> WORD_SIZE); + d = (SItype) (u >> WORD_SIZE); d *= HIGH_HALFWORD_COEFF; d *= HIGH_HALFWORD_COEFF; d += (USItype) (u & (HIGH_WORD_COEFF - 1)); - return (negate ? -d : d); + return d; } #endif @@ -970,21 +965,16 @@ __floatdixf (u) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) TFtype -__floatditf (u) - DItype u; +__floatditf (DItype u) { TFtype d; - SItype negate = 0; - - if (u < 0) - u = -u, negate = 1; - d = (USItype) (u >> WORD_SIZE); + d = (SItype) (u >> WORD_SIZE); d *= HIGH_HALFWORD_COEFF; d *= HIGH_HALFWORD_COEFF; d += (USItype) (u & (HIGH_WORD_COEFF - 1)); - return (negate ? -d : d); + return d; } #endif @@ -994,21 +984,16 @@ __floatditf (u) #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE) DFtype -__floatdidf (u) - DItype u; +__floatdidf (DItype u) { DFtype d; - SItype negate = 0; - - if (u < 0) - u = -u, negate = 1; - d = (USItype) (u >> WORD_SIZE); + d = (SItype) (u >> WORD_SIZE); d *= HIGH_HALFWORD_COEFF; d *= HIGH_HALFWORD_COEFF; d += (USItype) (u & (HIGH_WORD_COEFF - 1)); - return (negate ? -d : d); + return d; } #endif @@ -1047,17 +1032,12 @@ __floatdidf (u) #endif SFtype -__floatdisf (u) - DItype u; +__floatdisf (DItype u) { /* Do the calculation in DFmode so that we don't lose any of the precision of the high word while multiplying it. */ DFtype f; - SItype negate = 0; - - if (u < 0) - u = -u, negate = 1; /* Protect against double-rounding error. Represent any low-order bits, that might be truncated in DFmode, @@ -1069,18 +1049,19 @@ __floatdisf (u) && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE)) { #define REP_BIT ((USItype) 1 << (DI_SIZE - DF_SIZE)) - if (u >= ((UDItype) 1 << DF_SIZE)) + if (! (- ((DItype) 1 << DF_SIZE) < u + && u < ((DItype) 1 << DF_SIZE))) { if ((USItype) u & (REP_BIT - 1)) u |= REP_BIT; } } - f = (USItype) (u >> WORD_SIZE); + f = (SItype) (u >> WORD_SIZE); f *= HIGH_HALFWORD_COEFF; f *= HIGH_HALFWORD_COEFF; f += (USItype) (u & (HIGH_WORD_COEFF - 1)); - return (SFtype) (negate ? -f : f); + return (SFtype) f; } #endif @@ -1098,8 +1079,7 @@ __floatdisf (u) #include <limits.h> USItype -__fixunsxfsi (a) - XFtype a; +__fixunsxfsi (XFtype a) { if (a >= - (DFtype) LONG_MIN) return (SItype) (a + LONG_MIN) - LONG_MIN; @@ -1121,8 +1101,7 @@ __fixunsxfsi (a) #include <limits.h> USItype -__fixunsdfsi (a) - DFtype a; +__fixunsdfsi (DFtype a) { if (a >= - (DFtype) LONG_MIN) return (SItype) (a + LONG_MIN) - LONG_MIN; @@ -1176,9 +1155,7 @@ __fixunssfsi (SFtype a) positive if S1 is greater, 0 if S1 and S2 are equal. */ int -__gcc_bcmp (s1, s2, size) - unsigned char *s1, *s2; - size_t size; +__gcc_bcmp (unsigned char *s1, unsigned char *s2, size_t size) { while (size > 0) { @@ -1192,6 +1169,11 @@ __gcc_bcmp (s1, s2, size) #endif +#ifdef L__dummy +void +__dummy () {} +#endif + #ifdef L_varargs #ifdef __i860__ #if defined(__svr4__) || defined(__alliant__) @@ -1390,6 +1372,9 @@ asm ("___builtin_saveregs:"); #if defined(__MIPSEL__) | defined(__R3000__) | defined(__R2000__) | defined(__mips__) asm (" .text"); +#ifdef __mips16 + asm (" .set nomips16"); +#endif asm (" .ent __builtin_saveregs"); asm (" .globl __builtin_saveregs"); asm ("__builtin_saveregs:"); @@ -1399,7 +1384,7 @@ asm ("___builtin_saveregs:"); asm (" sw $7,12($30)"); asm (" j $31"); asm (" .end __builtin_saveregs"); -#else /* not __mips__, etc. */ +#else /* not __mips__, etc. */ void * __builtin_saveregs () @@ -1419,11 +1404,8 @@ __builtin_saveregs () #include <stdio.h> /* This is used by the `assert' macro. */ void -__eprintf (string, expression, line, filename) - const char *string; - const char *expression; - int line; - const char *filename; +__eprintf (const char *string, const char *expression, + unsigned int line, const char *filename) { fprintf (stderr, string, expression, line, filename); fflush (stderr); @@ -1450,6 +1432,7 @@ struct bb const char **functions; const long *line_nums; const char **filenames; + char *flags; }; #ifdef BLOCK_PROFILER_CODE @@ -1465,19 +1448,9 @@ BLOCK_PROFILER_CODE #include <stdio.h> char *ctime (); -#ifdef HAVE_ATEXIT -#ifdef WINNT -extern int atexit (void (*) (void)); -#else -extern void atexit (void (*) (void)); -#endif -#define ON_EXIT(FUNC,ARG) atexit ((FUNC)) -#else -#ifdef sun -extern void on_exit (void*, void*); -#define ON_EXIT(FUNC,ARG) on_exit ((FUNC), (ARG)) -#endif -#endif +#include "gbl-ctors.h" +#include "gcov-io.h" +#include <string.h> static struct bb *bb_head; @@ -1501,8 +1474,111 @@ static struct bb *bb_head; void __bb_exit_func (void) { - FILE *file = fopen ("bb.out", "a"); + FILE *da_file, *file; long time_value; + int i; + + if (bb_head == 0) + return; + + i = strlen (bb_head->filename) - 3; + + if (!strcmp (bb_head->filename+i, ".da")) + { + /* Must be -fprofile-arcs not -a. + Dump data in a form that gcov expects. */ + + struct bb *ptr; + + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + /* If the file exists, and the number of counts in it is the same, + then merge them in. */ + + if ((da_file = fopen (ptr->filename, "r")) != 0) + { + long n_counts = 0; + + if (__read_long (&n_counts, da_file, 8) != 0) + { + fprintf (stderr, "arc profiling: Can't read output file %s.\n", + ptr->filename); + continue; + } + + if (n_counts == ptr->ncounts) + { + int i; + + for (i = 0; i < n_counts; i++) + { + long v = 0; + + if (__read_long (&v, da_file, 8) != 0) + { + fprintf (stderr, "arc profiling: Can't read output file %s.\n", + ptr->filename); + break; + } + ptr->counts[i] += v; + } + } + + if (fclose (da_file) == EOF) + fprintf (stderr, "arc profiling: Error closing output file %s.\n", + ptr->filename); + } + if ((da_file = fopen (ptr->filename, "w")) == 0) + { + fprintf (stderr, "arc profiling: Can't open output file %s.\n", + ptr->filename); + continue; + } + + /* ??? Should first write a header to the file. Preferably, a 4 byte + magic number, 4 bytes containing the time the program was + compiled, 4 bytes containing the last modification time of the + source file, and 4 bytes indicating the compiler options used. + + That way we can easily verify that the proper source/executable/ + data file combination is being used from gcov. */ + + if (__write_long (ptr->ncounts, da_file, 8) != 0) + { + + fprintf (stderr, "arc profiling: Error writing output file %s.\n", + ptr->filename); + } + else + { + int j; + long *count_ptr = ptr->counts; + int ret = 0; + for (j = ptr->ncounts; j > 0; j--) + { + if (__write_long (*count_ptr, da_file, 8) != 0) + { + ret=1; + break; + } + count_ptr++; + } + if (ret) + fprintf (stderr, "arc profiling: Error writing output file %s.\n", + ptr->filename); + } + + if (fclose (da_file) == EOF) + fprintf (stderr, "arc profiling: Error closing output file %s.\n", + ptr->filename); + } + + return; + } + + /* Must be basic block profiling. Emit a human readable output file. */ + + file = fopen ("bb.out", "a"); if (!file) perror ("bb.out"); @@ -1513,22 +1589,25 @@ __bb_exit_func (void) /* This is somewhat type incorrect, but it avoids worrying about exactly where time.h is included from. It should be ok unless - a void * differs from other pointer formats, or if sizeof(long) + a void * differs from other pointer formats, or if sizeof (long) is < sizeof (time_t). It would be nice if we could assume the use of rationale standards here. */ - time((void *) &time_value); + time ((void *) &time_value); fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value)); /* We check the length field explicitly in order to allow compatibility with older GCC's which did not provide it. */ - for (ptr = bb_head; ptr != (struct bb *)0; ptr = ptr->next) + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) { int i; - int func_p = (ptr->nwords >= sizeof (struct bb) && ptr->nwords <= 1000); + int func_p = (ptr->nwords >= sizeof (struct bb) + && ptr->nwords <= 1000 + && ptr->functions); int line_p = (func_p && ptr->line_nums); int file_p = (func_p && ptr->filenames); + int addr_p = (ptr->addresses != 0); long ncounts = ptr->ncounts; long cnt_max = 0; long line_max = 0; @@ -1552,7 +1631,7 @@ __bb_exit_func (void) if (cnt_max < ptr->counts[i]) cnt_max = ptr->counts[i]; - if (addr_max < ptr->addresses[i]) + if (addr_p && addr_max < ptr->addresses[i]) addr_max = ptr->addresses[i]; if (line_p && line_max < ptr->line_nums[i]) @@ -1583,10 +1662,13 @@ __bb_exit_func (void) for (i = 0; i < ncounts; i++) { fprintf (file, - " Block #%*d: executed %*ld time(s) address= 0x%.*lx", + " Block #%*d: executed %*ld time(s)", blk_len, i+1, - cnt_len, ptr->counts[i], - addr_len, ptr->addresses[i]); + cnt_len, ptr->counts[i]); + + if (addr_p) + fprintf (file, " address= 0x%.*lx", addr_len, + ptr->addresses[i]); if (func_p) fprintf (file, " function= %-*s", func_len, @@ -1615,7 +1697,7 @@ void __bb_init_func (struct bb *blocks) { /* User is supposed to check whether the first word is non-0, - but just in case.... */ + but just in case.... */ if (blocks->zero_word) return; @@ -1632,160 +1714,729 @@ __bb_init_func (struct bb *blocks) bb_head = blocks; } -#endif /* not inhibit_libc */ -#endif /* not BLOCK_PROFILER_CODE */ -#endif /* L_bb */ - -/* Default free-store management functions for C++, per sections 12.5 and - 17.3.3 of the Working Paper. */ +#ifndef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) +#endif +#ifndef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) +#endif -#ifdef L_op_new -/* operator new (size_t), described in 17.3.3.5. This function is used by - C++ programs to allocate a block of memory to hold a single object. */ +/* Number of buckets in hashtable of basic block addresses. */ -typedef void (*vfp)(void); -extern vfp __new_handler; -extern void __default_new_handler (void); +#define BB_BUCKETS 311 -#ifdef WEAK_ALIAS -void * __builtin_new (size_t sz) - __attribute__ ((weak, alias ("___builtin_new"))); -void * -___builtin_new (size_t sz) -#else -void * -__builtin_new (size_t sz) -#endif +/* Maximum length of string in file bb.in. */ + +#define BBINBUFSIZE 500 + +/* BBINBUFSIZE-1 with double quotes. We could use #BBINBUFSIZE or + "BBINBUFSIZE" but want to avoid trouble with preprocessors. */ + +#define BBINBUFSIZESTR "499" + +struct bb_edge { - void *p; - vfp handler = (__new_handler) ? __new_handler : __default_new_handler; + struct bb_edge *next; + unsigned long src_addr; + unsigned long dst_addr; + unsigned long count; +}; - /* malloc (0) is unpredictable; avoid it. */ - if (sz == 0) - sz = 1; - p = (void *) malloc (sz); - while (p == 0) +enum bb_func_mode +{ + TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2 +}; + +struct bb_func +{ + struct bb_func *next; + char *funcname; + char *filename; + enum bb_func_mode mode; +}; + +/* This is the connection to the outside world. + The BLOCK_PROFILER macro must set __bb.blocks + and __bb.blockno. */ + +struct { + unsigned long blockno; + struct bb *blocks; +} __bb; + +/* Vars to store addrs of source and destination basic blocks + of a jump. */ + +static unsigned long bb_src = 0; +static unsigned long bb_dst = 0; + +static FILE *bb_tracefile = (FILE *) 0; +static struct bb_edge **bb_hashbuckets = (struct bb_edge **) 0; +static struct bb_func *bb_func_head = (struct bb_func *) 0; +static unsigned long bb_callcount = 0; +static int bb_mode = 0; + +static unsigned long *bb_stack = (unsigned long *) 0; +static size_t bb_stacksize = 0; + +static int reported = 0; + +/* Trace modes: +Always : Print execution frequencies of basic blocks + to file bb.out. +bb_mode & 1 != 0 : Dump trace of basic blocks to file bbtrace[.gz] +bb_mode & 2 != 0 : Print jump frequencies to file bb.out. +bb_mode & 4 != 0 : Cut call instructions from basic block flow. +bb_mode & 8 != 0 : Insert return instructions in basic block flow. +*/ + +#ifdef HAVE_POPEN + +/*#include <sys/types.h>*/ +#include <sys/stat.h> +/*#include <malloc.h>*/ + +/* Commands executed by gopen. */ + +#define GOPENDECOMPRESS "gzip -cd " +#define GOPENCOMPRESS "gzip -c >" + +/* Like fopen but pipes through gzip. mode may only be "r" or "w". + If it does not compile, simply replace gopen by fopen and delete + '.gz' from any first parameter to gopen. */ + +static FILE * +gopen (char *fn, char *mode) +{ + int use_gzip; + char *p; + + if (mode[1]) + return (FILE *) 0; + + if (mode[0] != 'r' && mode[0] != 'w') + return (FILE *) 0; + + p = fn + strlen (fn)-1; + use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z')) + || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z')); + + if (use_gzip) { - (*handler) (); - p = (void *) malloc (sz); + if (mode[0]=='r') + { + FILE *f; + char *s = (char *) malloc (sizeof (char) * strlen (fn) + + sizeof (GOPENDECOMPRESS)); + strcpy (s, GOPENDECOMPRESS); + strcpy (s + (sizeof (GOPENDECOMPRESS)-1), fn); + f = popen (s, mode); + free (s); + return f; + } + + else + { + FILE *f; + char *s = (char *) malloc (sizeof (char) * strlen (fn) + + sizeof (GOPENCOMPRESS)); + strcpy (s, GOPENCOMPRESS); + strcpy (s + (sizeof (GOPENCOMPRESS)-1), fn); + if (!(f = popen (s, mode))) + f = fopen (s, mode); + free (s); + return f; + } } - - return p; + + else + return fopen (fn, mode); } -#endif /* L_op_new */ -#ifdef L_op_vnew -/* void * operator new [] (size_t), described in 17.3.3.6. This function - is used by C++ programs to allocate a block of memory for an array. */ +static int +gclose (FILE *f) +{ + struct stat buf; -extern void * __builtin_new (size_t); + if (f != 0) + { + if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode)) + return pclose (f); -#ifdef WEAK_ALIAS -void * __builtin_vec_new (size_t sz) - __attribute__ ((weak, alias ("___builtin_vec_new"))); -void * -___builtin_vec_new (size_t sz) -#else -void * -__builtin_vec_new (size_t sz) -#endif + return fclose (f); + } + return 0; +} + +#endif /* HAVE_POPEN */ + +/* Called once per program. */ + +static void +__bb_exit_trace_func () { - return __builtin_new (sz); + FILE *file = fopen ("bb.out", "a"); + struct bb_func *f; + struct bb *b; + + if (!file) + perror ("bb.out"); + + if (bb_mode & 1) + { + if (!bb_tracefile) + perror ("bbtrace"); + else +#ifdef HAVE_POPEN + gclose (bb_tracefile); +#else + fclose (bb_tracefile); +#endif /* HAVE_POPEN */ + } + + /* Check functions in `bb.in'. */ + + if (file) + { + long time_value; + const struct bb_func *p; + int printed_something = 0; + struct bb *ptr; + long blk; + + /* This is somewhat type incorrect. */ + time ((void *) &time_value); + + for (p = bb_func_head; p != (struct bb_func *) 0; p = p->next) + { + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + if (!ptr->filename || (p->filename != (char *) 0 && strcmp (p->filename, ptr->filename))) + continue; + for (blk = 0; blk < ptr->ncounts; blk++) + { + if (!strcmp (p->funcname, ptr->functions[blk])) + goto found; + } + } + + if (!printed_something) + { + fprintf (file, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value)); + printed_something = 1; + } + + fprintf (file, "\tFunction %s", p->funcname); + if (p->filename) + fprintf (file, " of file %s", p->filename); + fprintf (file, "\n" ); + +found: ; + } + + if (printed_something) + fprintf (file, "\n"); + + } + + if (bb_mode & 2) + { + if (!bb_hashbuckets) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + return; + } + + else if (file) + { + long time_value; + int i; + unsigned long addr_max = 0; + unsigned long cnt_max = 0; + int cnt_len; + int addr_len; + + /* This is somewhat type incorrect, but it avoids worrying about + exactly where time.h is included from. It should be ok unless + a void * differs from other pointer formats, or if sizeof (long) + is < sizeof (time_t). It would be nice if we could assume the + use of rationale standards here. */ + + time ((void *) &time_value); + fprintf (file, "Basic block jump tracing"); + + switch (bb_mode & 12) + { + case 0: + fprintf (file, " (with call)"); + break; + + case 4: + /* Print nothing. */ + break; + + case 8: + fprintf (file, " (with call & ret)"); + break; + + case 12: + fprintf (file, " (with ret)"); + break; + } + + fprintf (file, " finished on %s\n", ctime ((void *) &time_value)); + + for (i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *bucket = bb_hashbuckets[i]; + for ( ; bucket; bucket = bucket->next ) + { + if (addr_max < bucket->src_addr) + addr_max = bucket->src_addr; + if (addr_max < bucket->dst_addr) + addr_max = bucket->dst_addr; + if (cnt_max < bucket->count) + cnt_max = bucket->count; + } + } + addr_len = num_digits (addr_max, 16); + cnt_len = num_digits (cnt_max, 10); + + for ( i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *bucket = bb_hashbuckets[i]; + for ( ; bucket; bucket = bucket->next ) + { + fprintf (file, "Jump from block 0x%.*lx to " + "block 0x%.*lx executed %*lu time(s)\n", + addr_len, bucket->src_addr, + addr_len, bucket->dst_addr, + cnt_len, bucket->count); + } + } + + fprintf (file, "\n"); + + } + } + + if (file) + fclose (file); + + /* Free allocated memory. */ + + f = bb_func_head; + while (f) + { + struct bb_func *old = f; + + f = f->next; + if (old->funcname) free (old->funcname); + if (old->filename) free (old->filename); + free (old); + } + + if (bb_stack) + free (bb_stack); + + if (bb_hashbuckets) + { + int i; + + for (i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *old, *bucket = bb_hashbuckets[i]; + + while (bucket) + { + old = bucket; + bucket = bucket->next; + free (old); + } + } + free (bb_hashbuckets); + } + + for (b = bb_head; b; b = b->next) + if (b->flags) free (b->flags); } -#endif /* L_op_vnew */ -#ifdef L_new_handler -/* set_new_handler (fvoid_t *) and the default new handler, described in - 17.3.3.2 and 17.3.3.5. These functions define the result of a failure - to allocate the amount of memory requested from operator new or new []. */ +/* Called once per program. */ -#ifndef inhibit_libc -/* This gets us __GNU_LIBRARY__. */ -#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ -#include <stdio.h> +static void +__bb_init_prg () +{ -#ifdef __GNU_LIBRARY__ - /* Avoid forcing the library's meaning of `write' on the user program - by using the "internal" name (for use within the library) */ -#define write(fd, buf, n) __write((fd), (buf), (n)) + FILE *file; + char buf[BBINBUFSIZE]; + const char *p; + const char *pos; + enum bb_func_mode m; + +#ifdef ON_EXIT + /* Initialize destructor. */ + ON_EXIT (__bb_exit_func, 0); #endif -#endif /* inhibit_libc */ -typedef void (*vfp)(void); -void __default_new_handler (void); + if (!(file = fopen ("bb.in", "r"))) + return; -vfp __new_handler = (vfp)0; + while(fscanf (file, " %" BBINBUFSIZESTR "s ", buf) != EOF) + { + p = buf; + if (*p == '-') + { + m = TRACE_OFF; + p++; + } + else + { + m = TRACE_ON; + } + if (!strcmp (p, "__bb_trace__")) + bb_mode |= 1; + else if (!strcmp (p, "__bb_jumps__")) + bb_mode |= 2; + else if (!strcmp (p, "__bb_hidecall__")) + bb_mode |= 4; + else if (!strcmp (p, "__bb_showret__")) + bb_mode |= 8; + else + { + struct bb_func *f = (struct bb_func *) malloc (sizeof (struct bb_func)); + if (f) + { + unsigned long l; + f->next = bb_func_head; + if ((pos = strchr (p, ':'))) + { + if (!(f->funcname = (char *) malloc (strlen (pos+1)+1))) + continue; + strcpy (f->funcname, pos+1); + l = pos-p; + if ((f->filename = (char *) malloc (l+1))) + { + strncpy (f->filename, p, l); + f->filename[l] = '\0'; + } + else + f->filename = (char *) 0; + } + else + { + if (!(f->funcname = (char *) malloc (strlen (p)+1))) + continue; + strcpy (f->funcname, p); + f->filename = (char *) 0; + } + f->mode = m; + bb_func_head = f; + } + } + } + fclose (file); -vfp -set_new_handler (vfp handler) -{ - vfp prev_handler; +#ifdef HAVE_POPEN + + if (bb_mode & 1) + bb_tracefile = gopen ("bbtrace.gz", "w"); + +#else + + if (bb_mode & 1) + bb_tracefile = fopen ("bbtrace", "w"); + +#endif /* HAVE_POPEN */ + + if (bb_mode & 2) + { + bb_hashbuckets = (struct bb_edge **) + malloc (BB_BUCKETS * sizeof (struct bb_edge *)); + if (bb_hashbuckets) + memset (bb_hashbuckets, 0, BB_BUCKETS * sizeof (struct bb_edge *)); + } + + if (bb_mode & 12) + { + bb_stacksize = 10; + bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack)); + } + +#ifdef ON_EXIT + /* Initialize destructor. */ + ON_EXIT (__bb_exit_trace_func, 0); +#endif - prev_handler = __new_handler; - if (handler == 0) handler = __default_new_handler; - __new_handler = handler; - return prev_handler; } -#define MESSAGE "Virtual memory exceeded in `new'\n" +/* Called upon entering a basic block. */ void -__default_new_handler () +__bb_trace_func () { -#ifndef inhibit_libc - /* don't use fprintf (stderr, ...) because it may need to call malloc. */ - /* This should really print the name of the program, but that is hard to - do. We need a standard, clean way to get at the name. */ - write (2, MESSAGE, sizeof (MESSAGE)); -#endif - /* don't call exit () because that may call global destructors which - may cause a loop. */ - _exit (-1); + struct bb_edge *bucket; + + MACHINE_STATE_SAVE("1") + + if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) + goto skip; + + bb_dst = __bb.blocks->addresses[__bb.blockno]; + __bb.blocks->counts[__bb.blockno]++; + + if (bb_tracefile) + { + fwrite (&bb_dst, sizeof (unsigned long), 1, bb_tracefile); + } + + if (bb_hashbuckets) + { + struct bb_edge **startbucket, **oldnext; + + oldnext = startbucket + = & bb_hashbuckets[ (((int) bb_src*8) ^ (int) bb_dst) % BB_BUCKETS ]; + bucket = *startbucket; + + for (bucket = *startbucket; bucket; + oldnext = &(bucket->next), bucket = *oldnext) + { + if (bucket->src_addr == bb_src + && bucket->dst_addr == bb_dst) + { + bucket->count++; + *oldnext = bucket->next; + bucket->next = *startbucket; + *startbucket = bucket; + goto ret; + } + } + + bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); + + if (!bucket) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + } + + else + { + bucket->src_addr = bb_src; + bucket->dst_addr = bb_dst; + bucket->next = *startbucket; + *startbucket = bucket; + bucket->count = 1; + } + } + +ret: + bb_src = bb_dst; + +skip: + ; + + MACHINE_STATE_RESTORE("1") + } -#endif -#ifdef L_op_delete -/* operator delete (void *), described in 17.3.3.3. This function is used - by C++ programs to return to the free store a block of memory allocated - as a single object. */ +/* Called when returning from a function and `__bb_showret__' is set. */ -#ifdef WEAK_ALIAS -void __builtin_delete (void *ptr) - __attribute__ ((weak, alias ("___builtin_delete"))); -void -___builtin_delete (void *ptr) -#else -void -__builtin_delete (void *ptr) -#endif +static void +__bb_trace_func_ret () { - if (ptr) - free (ptr); + struct bb_edge *bucket; + + if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) + goto skip; + + if (bb_hashbuckets) + { + struct bb_edge **startbucket, **oldnext; + + oldnext = startbucket + = & bb_hashbuckets[ (((int) bb_dst * 8) ^ (int) bb_src) % BB_BUCKETS ]; + bucket = *startbucket; + + for (bucket = *startbucket; bucket; + oldnext = &(bucket->next), bucket = *oldnext) + { + if (bucket->src_addr == bb_dst + && bucket->dst_addr == bb_src) + { + bucket->count++; + *oldnext = bucket->next; + bucket->next = *startbucket; + *startbucket = bucket; + goto ret; + } + } + + bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); + + if (!bucket) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + } + + else + { + bucket->src_addr = bb_dst; + bucket->dst_addr = bb_src; + bucket->next = *startbucket; + *startbucket = bucket; + bucket->count = 1; + } + } + +ret: + bb_dst = bb_src; + +skip: + ; + } -#endif -#ifdef L_op_vdel -/* operator delete [] (void *), described in 17.3.3.4. This function is - used by C++ programs to return to the free store a block of memory - allocated as an array. */ +/* Called upon entering the first function of a file. */ -extern void __builtin_delete (void *); +static void +__bb_init_file (struct bb *blocks) +{ + + const struct bb_func *p; + long blk, ncounts = blocks->ncounts; + const char **functions = blocks->functions; + + /* Set up linked list. */ + blocks->zero_word = 1; + blocks->next = bb_head; + bb_head = blocks; + + blocks->flags = 0; + if (!bb_func_head + || !(blocks->flags = (char *) malloc (sizeof (char) * blocks->ncounts))) + return; + + for (blk = 0; blk < ncounts; blk++) + blocks->flags[blk] = 0; + + for (blk = 0; blk < ncounts; blk++) + { + for (p = bb_func_head; p; p = p->next) + { + if (!strcmp (p->funcname, functions[blk]) + && (!p->filename || !strcmp (p->filename, blocks->filename))) + { + blocks->flags[blk] |= p->mode; + } + } + } + +} + +/* Called when exiting from a function. */ -#ifdef WEAK_ALIAS -void __builtin_vec_delete (void *ptr) - __attribute__ ((weak, alias ("___builtin_vec_delete"))); void -___builtin_vec_delete (void *ptr) -#else +__bb_trace_ret () +{ + + MACHINE_STATE_SAVE("2") + + if (bb_callcount) + { + if ((bb_mode & 12) && bb_stacksize > bb_callcount) + { + bb_src = bb_stack[bb_callcount]; + if (bb_mode & 8) + __bb_trace_func_ret (); + } + + bb_callcount -= 1; + } + + MACHINE_STATE_RESTORE("2") + +} + +/* Called when entering a function. */ + void -__builtin_vec_delete (void *ptr) -#endif +__bb_init_trace_func (struct bb *blocks, unsigned long blockno) { - __builtin_delete (ptr); + static int trace_init = 0; + + MACHINE_STATE_SAVE("3") + + if (!blocks->zero_word) + { + if (!trace_init) + { + trace_init = 1; + __bb_init_prg (); + } + __bb_init_file (blocks); + } + + if (bb_callcount) + { + + bb_callcount += 1; + + if (bb_mode & 12) + { + if (bb_callcount >= bb_stacksize) + { + size_t newsize = bb_callcount + 100; + + bb_stack = (unsigned long *) realloc (bb_stack, newsize); + if (! bb_stack) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + bb_stacksize = 0; + goto stack_overflow; + } + bb_stacksize = newsize; + } + bb_stack[bb_callcount] = bb_src; + + if (bb_mode & 4) + bb_src = 0; + + } + +stack_overflow:; + + } + + else if (blocks->flags && (blocks->flags[blockno] & TRACE_ON)) + { + bb_callcount = 1; + bb_src = 0; + + if (bb_stack) + bb_stack[bb_callcount] = bb_src; + } + + MACHINE_STATE_RESTORE("3") } -#endif -/* End of C++ free-store management functions */ +#endif /* not inhibit_libc */ +#endif /* not BLOCK_PROFILER_CODE */ +#endif /* L_bb */ #ifdef L_shtab unsigned int __shtab[] = { @@ -1806,8 +2457,7 @@ unsigned int __shtab[] = { #define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH) void -__clear_cache (beg, end) - char *beg, *end; +__clear_cache (char *beg, char *end) { #ifdef CLEAR_INSN_CACHE CLEAR_INSN_CACHE (beg, end); @@ -1837,7 +2487,7 @@ __clear_cache (beg, end) = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH; ptr += INSN_CACHE_LINE_WIDTH; } - *(INSTRUCTION_TYPE *)(ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION; + *(INSTRUCTION_TYPE *) (ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION; initialized = 1; } @@ -1919,7 +2569,7 @@ __clear_cache (beg, end) /* Jump to a trampoline, loading the static chain address. */ -#ifdef WINNT +#if defined(WINNT) && ! defined(__CYGWIN32__) long getpagesize() { @@ -1930,24 +2580,32 @@ long getpagesize() #endif } -int mprotect(addr, len, prot) - char *addr; - int len, prot; +#ifdef i386 +extern int VirtualProtect (char *, int, int, int *) __attribute__((stdcall)); +#endif + +int +mprotect (char *addr, int len, int prot) { int np, op; - if (prot == 7) np = 0x40; - else if (prot == 5) np = 0x20; - else if (prot == 4) np = 0x10; - else if (prot == 3) np = 0x04; - else if (prot == 1) np = 0x02; - else if (prot == 0) np = 0x01; + if (prot == 7) + np = 0x40; + else if (prot == 5) + np = 0x20; + else if (prot == 4) + np = 0x10; + else if (prot == 3) + np = 0x04; + else if (prot == 1) + np = 0x02; + else if (prot == 0) + np = 0x01; if (VirtualProtect (addr, len, np, &op)) return 0; else return -1; - } #endif @@ -1967,8 +2625,7 @@ TRANSFER_FROM_TRAMPOLINE #endif void -__enable_execute_stack (addr) - char *addr; +__enable_execute_stack (char *addr) { kern_return_t r; char *eaddr = addr + TRAMPOLINE_SIZE; @@ -2016,14 +2673,14 @@ __enable_execute_stack () lowest = current; } - /* Clear instruction cache in case an old trampoline is in it. */ + /* Clear instruction cache in case an old trampoline is in it. */ asm ("pich"); } #endif /* __convex__ */ -#ifdef __DOLPHIN__ +#ifdef __sysV88__ -/* Modified from the convex -code above. */ +/* Modified from the convex -code above. */ #include <sys/param.h> #include <errno.h> @@ -2051,7 +2708,52 @@ __enable_execute_stack () errno=save_errno; } -#endif /* __DOLPHIN__ */ +#endif /* __sysV88__ */ + +#ifdef __sysV68__ + +#include <sys/signal.h> +#include <errno.h> + +/* Motorola forgot to put memctl.o in the libp version of libc881.a, + so define it here, because we need it in __clear_insn_cache below */ +/* On older versions of this OS, no memctl or MCT_TEXT are defined; + hence we enable this stuff only if MCT_TEXT is #define'd. */ + +#ifdef MCT_TEXT +asm("\n\ + global memctl\n\ +memctl:\n\ + movq &75,%d0\n\ + trap &0\n\ + bcc.b noerror\n\ + jmp cerror%\n\ +noerror:\n\ + movq &0,%d0\n\ + rts"); +#endif + +/* Clear instruction cache so we can call trampolines on stack. + This is called from FINALIZE_TRAMPOLINE in mot3300.h. */ + +void +__clear_insn_cache () +{ +#ifdef MCT_TEXT + int save_errno; + + /* Preserve errno, because users would be surprised to have + errno changing without explicitly calling any system-call. */ + save_errno = errno; + + /* Keep it simple : memctl (MCT_TEXT) always fully clears the insn cache. + No need to use an address derived from _start or %sp, as 0 works also. */ + memctl(0, 4096, MCT_TEXT); + errno = save_errno; +#endif +} + +#endif /* __sysV68__ */ #ifdef __pyr__ @@ -2063,7 +2765,7 @@ __enable_execute_stack () #include <sys/vmmac.h> /* Modified from the convex -code above. - mremap promises to clear the i-cache. */ + mremap promises to clear the i-cache. */ void __enable_execute_stack () @@ -2078,21 +2780,52 @@ __enable_execute_stack () } } #endif /* __pyr__ */ + +#if defined (sony_news) && defined (SYSTYPE_BSD) + +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <syscall.h> +#include <machine/sysnews.h> + +/* cacheflush function for NEWS-OS 4.2. + This function is called from trampoline-initialize code + defined in config/mips/mips.h. */ + +void +cacheflush (char *beg, int size, int flag) +{ + if (syscall (SYS_sysnews, NEWS_CACHEFLUSH, beg, size, FLUSH_BCACHE)) + { + perror ("cache_flush"); + fflush (stderr); + abort (); + } +} + +#endif /* sony_news */ #endif /* L_trampoline */ +#ifndef __CYGWIN32__ #ifdef L__main #include "gbl-ctors.h" /* Some systems use __main in a way incompatible with its use in gcc, in these cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to give the same symbol without quotes for an alternative entry point. You - must define both, or neither. */ + must define both, or neither. */ #ifndef NAME__MAIN #define NAME__MAIN "__main" #define SYMBOL__MAIN __main #endif -#if !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF) +#ifdef INIT_SECTION_ASM_OP +#undef HAS_INIT_SECTION +#define HAS_INIT_SECTION +#endif + +#if !defined (HAS_INIT_SECTION) || !defined (OBJECT_FORMAT_ELF) /* Run all the global destructors on exit from the program. */ void @@ -2101,12 +2834,17 @@ __do_global_dtors () #ifdef DO_GLOBAL_DTORS_BODY DO_GLOBAL_DTORS_BODY; #else - func_ptr *p; - for (p = __DTOR_LIST__ + 1; *p; ) - (*p++) (); + static func_ptr *p = __DTOR_LIST__ + 1; + while (*p) + { + p++; + (*(p-1)) (); + } #endif } +#endif +#ifndef HAS_INIT_SECTION /* Run all the global constructors on entry to the program. */ #ifndef ON_EXIT @@ -2126,7 +2864,9 @@ __do_global_ctors () DO_GLOBAL_CTORS_BODY; ON_EXIT (__do_global_dtors, 0); } +#endif /* no HAS_INIT_SECTION */ +#if !defined (HAS_INIT_SECTION) || defined (INVOKE__main) /* Subroutine called automatically by `main'. Compiling a global function named `main' produces an automatic call to this function at the beginning. @@ -2146,9 +2886,10 @@ SYMBOL__MAIN () __do_global_ctors (); } } -#endif /* no INIT_SECTION_ASM_OP or INVOKE__main */ +#endif /* no HAS_INIT_SECTION or INVOKE__main */ #endif /* L__main */ +#endif /* __CYGWIN32__ */ #ifdef L_ctors @@ -2161,7 +2902,7 @@ SYMBOL__MAIN () /* We declare the lists here with two elements each, so that they are valid empty lists if no other definition is loaded. */ -#if (!defined(INIT_SECTION_ASM_OP) || !defined(OBJECT_FORMAT_ELF)) && !defined(CTOR_LISTS_DEFINED_EXTERNALLY) +#if !defined(INIT_SECTION_ASM_OP) && !defined(CTOR_LISTS_DEFINED_EXTERNALLY) #if defined(__NeXT__) || defined(_AIX) /* After 2.3, try this definition on all systems. */ func_ptr __CTOR_LIST__[2] = {0, 0}; @@ -2177,23 +2918,83 @@ func_ptr __DTOR_LIST__[2]; #include "gbl-ctors.h" +#ifdef NEED_ATEXIT +# ifdef ON_EXIT +# undef ON_EXIT +# endif +int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */ +#endif + #ifndef ON_EXIT +#ifdef NEED_ATEXIT +# include <errno.h> + +static func_ptr *atexit_chain = 0; +static long atexit_chain_length = 0; +static volatile long last_atexit_chain_slot = -1; + +int atexit (func_ptr func) +{ + if (++last_atexit_chain_slot == atexit_chain_length) + { + atexit_chain_length += 32; + if (atexit_chain) + atexit_chain = (func_ptr *) realloc (atexit_chain, atexit_chain_length + * sizeof (func_ptr)); + else + atexit_chain = (func_ptr *) malloc (atexit_chain_length + * sizeof (func_ptr)); + if (! atexit_chain) + { + atexit_chain_length = 0; + last_atexit_chain_slot = -1; + errno = ENOMEM; + return (-1); + } + } + atexit_chain[last_atexit_chain_slot] = func; + return (0); +} +#endif /* NEED_ATEXIT */ + /* If we have no known way of registering our own __do_global_dtors routine so that it will be invoked at program exit time, then we have to define our own exit routine which will get this to happen. */ extern void __do_global_dtors (); +extern void __bb_exit_func (); extern void _cleanup (); extern void _exit () __attribute__ ((noreturn)); void -exit (status) - int status; +exit (int status) { #if !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF) +#ifdef NEED_ATEXIT + if (atexit_chain) + { + for ( ; last_atexit_chain_slot-- >= 0; ) + { + (*atexit_chain[last_atexit_chain_slot + 1]) (); + atexit_chain[last_atexit_chain_slot + 1] = 0; + } + free (atexit_chain); + atexit_chain = 0; + } +#else /* No NEED_ATEXIT */ __do_global_dtors (); +#endif /* No NEED_ATEXIT */ +#endif /* !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF) */ +/* In gbl-ctors.h, ON_EXIT is defined if HAVE_ATEXIT is defined. In + __bb_init_func and _bb_init_prg, __bb_exit_func is registered with + ON_EXIT if ON_EXIT is defined. Thus we must not call __bb_exit_func here + if HAVE_ATEXIT is defined. */ +#ifndef HAVE_ATEXIT +#ifndef inhibit_libc + __bb_exit_func (); #endif +#endif /* !HAVE_ATEXIT */ #ifdef EXIT_BODY EXIT_BODY; #else @@ -2202,245 +3003,775 @@ exit (status) _exit (status); } -#else +#else /* ON_EXIT defined */ int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */ -#endif + +# ifndef HAVE_ATEXIT +/* Provide a fake for atexit() using ON_EXIT. */ +int atexit (func_ptr func) +{ + return ON_EXIT (func, NULL); +} +# endif /* HAVE_ATEXIT */ +#endif /* ON_EXIT defined */ #endif /* L_exit */ #ifdef L_eh -typedef struct { - void *start; - void *end; - void *exception_handler; -} exception_table; - -struct exception_table_node { - exception_table *table; - void *start; - void *end; - struct exception_table_node *next; -}; -static int except_table_pos; -static void *except_pc; -static struct exception_table_node *exception_table_list; +#include "gthr.h" + +/* Shared exception handling support routines. */ + +void +__default_terminate () +{ + abort (); +} + +void (*__terminate_func)() = __default_terminate; + +void +__terminate () +{ + (*__terminate_func)(); +} + +void * +__throw_type_match (void *catch_type, void *throw_type, void *obj) +{ +#if 0 + printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n", + catch_type, throw_type); +#endif + if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0) + return obj; + return 0; +} + +void +__empty () +{ +} + + +/* Include definitions of EH context and table layout */ + +#include "eh-common.h" + +/* Allocate and return a new EH context structure. */ + +extern void __throw (); -static exception_table * -find_exception_table (pc) - void* pc; +static void * +new_eh_context () { - register struct exception_table_node *table = exception_table_list; - for ( ; table != 0; table = table->next) + struct eh_full_context { + struct eh_context c; + void *top_elt[2]; + } *ehfc = (struct eh_full_context *) malloc (sizeof *ehfc); + + if (! ehfc) + __terminate (); + + memset (ehfc, 0, sizeof *ehfc); + + ehfc->c.dynamic_handler_chain = (void **) ehfc->top_elt; + + /* This should optimize out entirely. This should always be true, + but just in case it ever isn't, don't allow bogus code to be + generated. */ + + if ((void*)(&ehfc->c) != (void*)ehfc) + __terminate (); + + return &ehfc->c; +} + +#if __GTHREADS +static __gthread_key_t eh_context_key; + +/* Destructor for struct eh_context. */ +static void +eh_context_free (void *ptr) +{ + __gthread_key_dtor (eh_context_key, ptr); + if (ptr) + free (ptr); +} +#endif + +/* Pointer to function to return EH context. */ + +static struct eh_context *eh_context_initialize (); +static struct eh_context *eh_context_static (); +#if __GTHREADS +static struct eh_context *eh_context_specific (); +#endif + +static struct eh_context *(*get_eh_context) () = &eh_context_initialize; + +/* Routine to get EH context. + This one will simply call the function pointer. */ + +void * +__get_eh_context () +{ + return (void *) (*get_eh_context) (); +} + +/* Get and set the language specific info pointer. */ + +void ** +__get_eh_info () +{ + struct eh_context *eh = (*get_eh_context) (); + return &eh->info; +} + +#if __GTHREADS +static void +eh_threads_initialize () +{ + /* Try to create the key. If it fails, revert to static method, + otherwise start using thread specific EH contexts. */ + if (__gthread_key_create (&eh_context_key, &eh_context_free) == 0) + get_eh_context = &eh_context_specific; + else + get_eh_context = &eh_context_static; +} +#endif /* no __GTHREADS */ + +/* Initialize EH context. + This will be called only once, since we change GET_EH_CONTEXT + pointer to another routine. */ + +static struct eh_context * +eh_context_initialize () +{ +#if __GTHREADS + + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + /* Make sure that get_eh_context does not point to us anymore. + Some systems have dummy thread routines in their libc that + return a success (Solaris 2.6 for example). */ + if (__gthread_once (&once, eh_threads_initialize) != 0 + || get_eh_context == &eh_context_initialize) { - if (table->start <= pc && table->end > pc) - return table->table; + /* Use static version of EH context. */ + get_eh_context = &eh_context_static; } - return 0; + +#else /* no __GTHREADS */ + + /* Use static version of EH context. */ + get_eh_context = &eh_context_static; + +#endif /* no __GTHREADS */ + + return (*get_eh_context) (); } -/* this routine takes a pc, and the address of the exception handler associated - with the closest exception table handler entry associated with that PC, - or 0 if there are no table entries the PC fits in. The algorithm works - something like this: +/* Return a static EH context. */ - while(current_entry exists) { - if(current_entry.start < pc ) - current_entry = next_entry; - else { - if(prev_entry.start <= pc && prev_entry.end > pc) { - save pointer to prev_entry; - return prev_entry.exception_handler; - } - else return 0; - } - } - return 0; +static struct eh_context * +eh_context_static () +{ + static struct eh_context *eh; + if (! eh) + eh = new_eh_context (); + return eh; +} - Assuming a correctly sorted table (ascending order) this routine should - return the tightest match... +#if __GTHREADS +/* Return a thread specific EH context. */ - In the advent of a tie, we have to give the last entry, as it represents - an inner block. - */ +static struct eh_context * +eh_context_specific () +{ + struct eh_context *eh; + eh = (struct eh_context *) __gthread_getspecific (eh_context_key); + if (! eh) + { + eh = new_eh_context (); + if (__gthread_setspecific (eh_context_key, (void *) eh) != 0) + __terminate (); + } + return eh; +} +#endif __GTHREADS + +/* Support routines for setjmp/longjmp exception handling. */ -void * -__find_first_exception_table_match(pc) -void *pc; -{ - exception_table *table = find_exception_table (pc); - int pos = 0; - int best = 0; - if (table == 0) - return (void*)0; -#if 0 - printf("find_first_exception_table_match(): pc = %x!\n",pc); +/* Calls to __sjthrow are generated by the compiler when an exception + is raised when using the setjmp/longjmp exception handling codegen + method. */ + +#ifdef DONT_USE_BUILTIN_SETJMP +extern void longjmp (void *, int); #endif - except_pc = pc; +/* Routine to get the head of the current thread's dynamic handler chain + use for exception handling. */ -#if 0 - /* We can't do this yet, as we don't know that the table is sorted. */ - do { - ++pos; - if (table[pos].start > except_pc) - /* found the first table[pos].start > except_pc, so the previous - entry better be the one we want! */ - break; - } while(table[pos].exception_handler != (void*)-1); - - --pos; - if (table[pos].start <= except_pc && table[pos].end > except_pc) +void *** +__get_dynamic_handler_chain () +{ + struct eh_context *eh = (*get_eh_context) (); + return &eh->dynamic_handler_chain; +} + +/* This is used to throw an exception when the setjmp/longjmp codegen + method is used for exception handling. + + We call __terminate if there are no handlers left. Otherwise we run the + cleanup actions off the dynamic cleanup stack, and pop the top of the + dynamic handler chain, and use longjmp to transfer back to the associated + handler. */ + +void +__sjthrow () +{ + struct eh_context *eh = (*get_eh_context) (); + void ***dhc = &eh->dynamic_handler_chain; + void *jmpbuf; + void (*func)(void *, int); + void *arg; + void ***cleanup; + + /* The cleanup chain is one word into the buffer. Get the cleanup + chain. */ + cleanup = (void***)&(*dhc)[1]; + + /* If there are any cleanups in the chain, run them now. */ + if (cleanup[0]) { - except_table_pos = pos; -#if 0 - printf("find_first_eh_table_match(): found match: %x\n",table[pos].exception_handler); + double store[200]; + void **buf = (void**)store; + buf[1] = 0; + buf[0] = (*dhc); + + /* try { */ +#ifdef DONT_USE_BUILTIN_SETJMP + if (! setjmp (&buf[2])) +#else + if (! __builtin_setjmp (&buf[2])) #endif - return table[pos].exception_handler; + { + *dhc = buf; + while (cleanup[0]) + { + func = (void(*)(void*, int))cleanup[0][1]; + arg = (void*)cleanup[0][2]; + + /* Update this before running the cleanup. */ + cleanup[0] = (void **)cleanup[0][0]; + + (*func)(arg, 2); + } + *dhc = buf[0]; + } + /* catch (...) */ + else + { + __terminate (); + } } + + /* We must call terminate if we try and rethrow an exception, when + there is no exception currently active and when there are no + handlers left. */ + if (! eh->info || (*dhc)[0] == 0) + __terminate (); + + /* Find the jmpbuf associated with the top element of the dynamic + handler chain. The jumpbuf starts two words into the buffer. */ + jmpbuf = &(*dhc)[2]; + + /* Then we pop the top element off the dynamic handler chain. */ + *dhc = (void**)(*dhc)[0]; + + /* And then we jump to the handler. */ + +#ifdef DONT_USE_BUILTIN_SETJMP + longjmp (jmpbuf, 1); #else - while (table[++pos].exception_handler != (void*)-1) { - if (table[pos].start <= except_pc && table[pos].end > except_pc) - { - /* This can apply. Make sure it is better or as good as the previous - best. */ - /* The best one ends first. */ - if (best == 0 || (table[pos].end <= table[best].end - /* The best one starts last. */ - && table[pos].start >= table[best].start)) - best = pos; - } - } - if (best != 0) - return table[best].exception_handler; + __builtin_longjmp (jmpbuf, 1); #endif +} -#if 0 - printf("find_first_eh_table_match(): else: returning NULL!\n"); +/* Run cleanups on the dynamic cleanup stack for the current dynamic + handler, then pop the handler off the dynamic handler stack, and + then throw. This is used to skip the first handler, and transfer + control to the next handler in the dynamic handler stack. */ + +void +__sjpopnthrow () +{ + struct eh_context *eh = (*get_eh_context) (); + void ***dhc = &eh->dynamic_handler_chain; + void (*func)(void *, int); + void *arg; + void ***cleanup; + + /* The cleanup chain is one word into the buffer. Get the cleanup + chain. */ + cleanup = (void***)&(*dhc)[1]; + + /* If there are any cleanups in the chain, run them now. */ + if (cleanup[0]) + { + double store[200]; + void **buf = (void**)store; + buf[1] = 0; + buf[0] = (*dhc); + + /* try { */ +#ifdef DONT_USE_BUILTIN_SETJMP + if (! setjmp (&buf[2])) +#else + if (! __builtin_setjmp (&buf[2])) #endif - return (void*)0; + { + *dhc = buf; + while (cleanup[0]) + { + func = (void(*)(void*, int))cleanup[0][1]; + arg = (void*)cleanup[0][2]; + + /* Update this before running the cleanup. */ + cleanup[0] = (void **)cleanup[0][0]; + + (*func)(arg, 2); + } + *dhc = buf[0]; + } + /* catch (...) */ + else + { + __terminate (); + } + } + + /* Then we pop the top element off the dynamic handler chain. */ + *dhc = (void**)(*dhc)[0]; + + __sjthrow (); } + +/* Support code for all exception region-based exception handling. */ -void * -__throw_type_match (void *catch_type, void *throw_type, void* obj) +/* This value identifies the place from which an exception is being + thrown. */ + +#ifdef EH_TABLE_LOOKUP + +EH_TABLE_LOOKUP + +#else + +#ifdef DWARF2_UNWIND_INFO + + +/* Return the table version of an exception descriptor */ + +short +__get_eh_table_version (exception_descriptor *table) { -#if 0 - printf("__throw_type_match (): catch_type = %s, throw_type = %s\n", - catch_type, throw_type); -#endif - if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0) - return obj; - return 0; + return table->lang.version; } -void -__register_exceptions (exception_table *table) +/* Return the originating table language of an exception descriptor */ + +short +__get_eh_table_language (exception_descriptor *table) { - struct exception_table_node *node; - exception_table *range = table + 1; + return table->lang.language; +} - if (range->start == (void*)-1) - return; +/* This routine takes a PC and a pointer to the exception region TABLE for + its translation unit, and returns the address of the exception handler + associated with the closest exception table handler entry associated + with that PC, or 0 if there are no table entries the PC fits in. - node = (struct exception_table_node*) - malloc (sizeof (struct exception_table_node)); - node->table = table; + In the advent of a tie, we have to give the last entry, as it represents + an inner block. */ - /* This look can be optimized away either if the table - is sorted, or if we pass in extra parameters. */ - node->start = range->start; - node->end = range->end; - for (range++ ; range->start != (void*)(-1); range++) +static void * +old_find_exception_handler (void *pc, old_exception_table *table) +{ + if (table) { - if (range->start < node->start) - node->start = range->start; - if (range->end > node->end) - node->end = range->end; + int pos; + int best = -1; + + /* We can't do a binary search because the table isn't guaranteed + to be sorted from function to function. */ + for (pos = 0; table[pos].start_region != (void *) -1; ++pos) + { + if (table[pos].start_region <= pc && table[pos].end_region > pc) + { + /* This can apply. Make sure it is at least as small as + the previous best. */ + if (best == -1 || (table[pos].end_region <= table[best].end_region + && table[pos].start_region >= table[best].start_region)) + best = pos; + } + /* But it is sorted by starting PC within a function. */ + else if (best >= 0 && table[pos].start_region > pc) + break; + } + if (best != -1) + return table[best].exception_handler; } - node->next = exception_table_list; - exception_table_list = node; + return (void *) 0; } -#if #machine(i386) -void -__unwind_function(void *ptr) -{ - asm("movl 8(%esp),%ecx"); - /* Undo current frame */ - asm("movl %ebp,%esp"); - asm("popl %ebp"); - /* like ret, but stay here */ - asm("addl $4,%esp"); - - /* Now, undo previous frame. */ - /* This is a test routine, as we have to dynamically probe to find out - what to pop for certain, this is just a guess. */ - asm("leal -16(%ebp),%esp"); - asm("pop %ebx"); - asm("pop %esi"); - asm("pop %edi"); - asm("movl %ebp,%esp"); - asm("popl %ebp"); - - asm("movl %ecx,0(%esp)"); - asm("ret"); -} -#elif #machine(rs6000) -__unwind_function(void *ptr) -{ - asm("mr 31,1"); - asm("l 1,0(1)"); - asm("l 31,-4(1)"); - asm("# br"); - - asm("mr 31,1"); - asm("l 1,0(1)"); - /* use 31 as a scratch register to restore the link register. */ - asm("l 31, 8(1);mtlr 31 # l lr,8(1)"); - asm("l 31,-4(1)"); - asm("# br"); - asm("mtctr 3;bctr # b 3"); -} -#elif #machine(powerpc) -__unwind_function(void *ptr) -{ - asm("mr 31,1"); - asm("lwz 1,0(1)"); - asm("lwz 31,-4(1)"); - asm("# br"); - - asm("mr 31,1"); - asm("lwz 1,0(1)"); - /* use 31 as a scratch register to restore the link register. */ - asm("lwz 31, 8(1);mtlr 31 # l lr,8(1)"); - asm("lwz 31,-4(1)"); - asm("# br"); - asm("mtctr 3;bctr # b 3"); -} -#elif #machine(vax) -__unwind_function(void *ptr) -{ - __label__ return_again; - - /* Replace our frame's return address with the label below. - During execution, we will first return here instead of to - caller, then second return takes caller's frame off the stack. - Two returns matches two actual calls, so is less likely to - confuse debuggers. `16' corresponds to RETURN_ADDRESS_OFFSET. */ - __asm ("movl %0,16(fp)" : : "p" (&& return_again)); - return; - - return_again: - return; +static void * +find_exception_handler (void *pc, exception_descriptor *table, void *eh_info) +{ + if (table) + { + /* The new model assumed the table is sorted inner-most out so the + first region we find which matches is the correct one */ + + int pos; + void *ret; + exception_table *tab = &(table->table[0]); + + /* Subtract 1 from the PC to avoid hitting the next region */ + pc--; + + /* We can't do a binary search because the table is in inner-most + to outermost address ranges within functions */ + for (pos = 0; tab[pos].start_region != (void *) -1; pos++) + { + if (tab[pos].start_region <= pc && tab[pos].end_region > pc) + { + if (tab[pos].match_info) + { + __eh_matcher matcher = ((__eh_info *)eh_info)->match_function; + /* match info but no matcher is NOT a match */ + if (matcher) + { + ret = (*matcher)(eh_info, tab[pos].match_info, table); + if (ret) + return tab[pos].exception_handler; + } + } + else + return tab[pos].exception_handler; + } + } + } + + return (void *) 0; } -#else -__unwind_function(void *ptr) +#endif /* DWARF2_UNWIND_INFO */ +#endif /* EH_TABLE_LOOKUP */ + +#ifdef DWARF2_UNWIND_INFO +/* Support code for exception handling using static unwind information. */ + +#include "frame.h" + +/* This type is used in get_reg and put_reg to deal with ABIs where a void* + is smaller than a word, such as the Irix 6 n32 ABI. We cast twice to + avoid a warning about casting between int and pointer of different + sizes. */ + +typedef int ptr_type __attribute__ ((mode (pointer))); + +/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a + frame called by UDATA or 0. */ + +static void* +get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata) { - abort (); + if (udata->saved[reg] == REG_SAVED_OFFSET) + return (void *)(ptr_type) + *(word_type *)(udata->cfa + udata->reg_or_offset[reg]); + else if (udata->saved[reg] == REG_SAVED_REG && sub_udata) + return get_reg (udata->reg_or_offset[reg], sub_udata, 0); + else + abort (); +} + +/* Overwrite the saved value for register REG in frame UDATA with VAL. */ + +static void +put_reg (unsigned reg, void *val, frame_state *udata) +{ + if (udata->saved[reg] == REG_SAVED_OFFSET) + *(word_type *)(udata->cfa + udata->reg_or_offset[reg]) + = (word_type)(ptr_type) val; + else + abort (); +} + +/* Copy the saved value for register REG from frame UDATA to frame + TARGET_UDATA. Unlike the previous two functions, this can handle + registers that are not one word large. */ + +static void +copy_reg (unsigned reg, frame_state *udata, frame_state *target_udata) +{ + if (udata->saved[reg] == REG_SAVED_OFFSET + && target_udata->saved[reg] == REG_SAVED_OFFSET) + memcpy (target_udata->cfa + target_udata->reg_or_offset[reg], + udata->cfa + udata->reg_or_offset[reg], + __builtin_dwarf_reg_size (reg)); + else + abort (); +} + +/* Retrieve the return address for frame UDATA, where SUB_UDATA is a + frame called by UDATA or 0. */ + +static inline void * +get_return_addr (frame_state *udata, frame_state *sub_udata) +{ + return __builtin_extract_return_addr + (get_reg (udata->retaddr_column, udata, sub_udata)); +} + +/* Overwrite the return address for frame UDATA with VAL. */ + +static inline void +put_return_addr (void *val, frame_state *udata) +{ + val = __builtin_frob_return_addr (val); + put_reg (udata->retaddr_column, val, udata); +} + +/* Given the current frame UDATA and its return address PC, return the + information about the calling frame in CALLER_UDATA. */ + +static void * +next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata) +{ + caller_udata = __frame_state_for (pc, caller_udata); + if (! caller_udata) + return 0; + + /* Now go back to our caller's stack frame. If our caller's CFA register + was saved in our stack frame, restore it; otherwise, assume the CFA + register is SP and restore it to our CFA value. */ + if (udata->saved[caller_udata->cfa_reg]) + caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0); + else + caller_udata->cfa = udata->cfa; + caller_udata->cfa += caller_udata->cfa_offset; + + return caller_udata; +} + +#ifdef INCOMING_REGNO +/* Is the saved value for register REG in frame UDATA stored in a register + window in the previous frame? */ + +static int +in_reg_window (int reg, frame_state *udata) +{ + if (udata->saved[reg] != REG_SAVED_OFFSET) + return 0; + +#ifdef STACK_GROWS_DOWNWARD + return udata->reg_or_offset[reg] > 0; +#else + return udata->reg_or_offset[reg] < 0; +#endif +} +#endif /* INCOMING_REGNO */ + +/* We first search for an exception handler, and if we don't find + it, we call __terminate on the current stack frame so that we may + use the debugger to walk the stack and understand why no handler + was found. + + If we find one, then we unwind the frames down to the one that + has the handler and transfer control into the handler. */ + +void +__throw () +{ + struct eh_context *eh = (*get_eh_context) (); + void *saved_pc, *pc, *handler, *retaddr; + frame_state ustruct, ustruct2; + frame_state *udata = &ustruct; + frame_state *sub_udata = &ustruct2; + frame_state my_ustruct, *my_udata = &my_ustruct; + long args_size; + int new_exception_model; + + /* This is required for C++ semantics. We must call terminate if we + try and rethrow an exception, when there is no exception currently + active. */ + if (! eh->info) + __terminate (); + + /* Start at our stack frame. */ +label: + udata = __frame_state_for (&&label, udata); + if (! udata) + __terminate (); + + /* We need to get the value from the CFA register. At this point in + compiling __throw we don't know whether or not we will use the frame + pointer register for the CFA, so we check our unwind info. */ + if (udata->cfa_reg == __builtin_dwarf_fp_regnum ()) + udata->cfa = __builtin_fp (); + else + udata->cfa = __builtin_sp (); + udata->cfa += udata->cfa_offset; + + memcpy (my_udata, udata, sizeof (*udata)); + + /* Do any necessary initialization to access arbitrary stack frames. + On the SPARC, this means flushing the register windows. */ + __builtin_unwind_init (); + + /* Now reset pc to the right throw point. */ + pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; + saved_pc = pc; + + handler = 0; + for (;;) + { + frame_state *p = udata; + udata = next_stack_level (pc, udata, sub_udata); + sub_udata = p; + + /* If we couldn't find the next frame, we lose. */ + if (! udata) + break; + + if (udata->eh_ptr == NULL) + new_exception_model = 0; + else + new_exception_model = (((exception_descriptor *)(udata->eh_ptr))-> + runtime_id_field == NEW_EH_RUNTIME); + + if (new_exception_model) + handler = find_exception_handler (pc, udata->eh_ptr, eh->info); + else + handler = old_find_exception_handler (pc, udata->eh_ptr); + + /* If we found one, we can stop searching. */ + if (handler) + { + args_size = udata->args_size; + break; + } + + /* Otherwise, we continue searching. We subtract 1 from PC to avoid + hitting the beginning of the next region. */ + pc = get_return_addr (udata, sub_udata) - 1; + } + + /* If we haven't found a handler by now, this is an unhandled + exception. */ + if (! handler) + __terminate (); + + eh->handler_label = handler; + + if (pc == saved_pc) + /* We found a handler in the throw context, no need to unwind. */ + udata = my_udata; + else + { + int i; + + /* Unwind all the frames between this one and the handler by copying + their saved register values into our register save slots. */ + + /* Remember the PC where we found the handler. */ + void *handler_pc = pc; + + /* Start from the throw context again. */ + pc = saved_pc; + memcpy (udata, my_udata, sizeof (*udata)); + + while (pc != handler_pc) + { + frame_state *p = udata; + udata = next_stack_level (pc, udata, sub_udata); + sub_udata = p; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + if (i != udata->retaddr_column && udata->saved[i]) + { +#ifdef INCOMING_REGNO + /* If you modify the saved value of the return address + register on the SPARC, you modify the return address for + your caller's frame. Don't do that here, as it will + confuse get_return_addr. */ + if (in_reg_window (i, udata) + && udata->saved[udata->retaddr_column] == REG_SAVED_REG + && udata->reg_or_offset[udata->retaddr_column] == i) + continue; +#endif + copy_reg (i, udata, my_udata); + } + + pc = get_return_addr (udata, sub_udata) - 1; + } + +#ifdef INCOMING_REGNO + /* But we do need to update the saved return address register from + the last frame we unwind, or the handler frame will have the wrong + return address. */ + if (udata->saved[udata->retaddr_column] == REG_SAVED_REG) + { + i = udata->reg_or_offset[udata->retaddr_column]; + if (in_reg_window (i, udata)) + copy_reg (i, udata, my_udata); + } +#endif + } + /* udata now refers to the frame called by the handler frame. */ + + /* Emit the stub to adjust sp and jump to the handler. */ + if (new_exception_model) + retaddr = __builtin_eh_stub (); + else + retaddr = __builtin_eh_stub_old (); + + /* And then set our return address to point to the stub. */ + if (my_udata->saved[my_udata->retaddr_column] == REG_SAVED_OFFSET) + put_return_addr (retaddr, my_udata); + else + __builtin_set_return_addr_reg (retaddr); + + /* Set up the registers we use to communicate with the stub. + We check STACK_GROWS_DOWNWARD so the stub can use adjust_stack. */ + + if (new_exception_model) + __builtin_set_eh_regs ((void *)eh, +#ifdef STACK_GROWS_DOWNWARD + udata->cfa - my_udata->cfa +#else + my_udata->cfa - udata->cfa +#endif + + args_size); + else + __builtin_set_eh_regs (handler, + +#ifdef STACK_GROWS_DOWNWARD + udata->cfa - my_udata->cfa +#else + my_udata->cfa - udata->cfa +#endif + + args_size); + + /* Epilogue: restore the handler frame's register values and return + to the stub. */ } -#endif /* powerpc */ +#endif /* DWARF2_UNWIND_INFO */ + #endif /* L_eh */ #ifdef L_pure |