OSDN Git Service

* tree.c (unsave_expr_now): Avoid recursing into the parts of
[pf3gnuchains/gcc-fork.git] / gcc / libgcc2.c
index 4dd2843..c32a7e0 100644 (file)
@@ -1,6 +1,6 @@
 /* More subroutines needed by GCC output code on some machines.  */
 /* Compile this one with gcc.  */
-/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
+/* Copyright (C) 1989, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -16,11 +16,13 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
-/* As a special exception, if you link this library with files
-   compiled with GCC to produce an executable, this does not cause
-   the resulting executable to be covered by the GNU General Public License.
+/* As a special exception, if you link this library with other files,
+   some of which are compiled with GCC, to produce an executable,
+   this library does not by itself cause the resulting executable
+   to be covered by the GNU General Public License.
    This exception does not however invalidate any other reasons why
    the executable file might be covered by the GNU General Public License.  */
 
@@ -30,8 +32,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include "tconfig.h"
 #include "machmode.h"
+#include "defaults.h" 
 #ifndef L_trampoline
-#include "gstddef.h"
+#include <stddef.h>
 #endif
 
 /* Don't use `fancy_abort' here even if config.h says to use it.  */
@@ -39,6 +42,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #undef abort
 #endif
 
+#if (SUPPORTS_WEAK == 1) && (defined (ASM_OUTPUT_DEF) || defined (ASM_OUTPUT_WEAK_ALIAS))
+#define WEAK_ALIAS
+#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.  */
+
+#ifndef LIBGCC2_WORDS_BIG_ENDIAN
+#define LIBGCC2_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN
+#endif
+
 /* In the first part of this file, we are interfacing to calls generated
    by the compiler itself.  These calls pass values into these routines
    which have very specific modes (rather than very specific types), and
@@ -53,8 +68,10 @@ typedef       int SItype     __attribute__ ((mode (SI)));
 typedef unsigned int USItype   __attribute__ ((mode (SI)));
 typedef                 int DItype     __attribute__ ((mode (DI)));
 typedef unsigned int UDItype   __attribute__ ((mode (DI)));
+
 typedef        float SFtype    __attribute__ ((mode (SF)));
 typedef                float DFtype    __attribute__ ((mode (DF)));
+
 #if LONG_DOUBLE_TYPE_SIZE == 96
 typedef                float XFtype    __attribute__ ((mode (XF)));
 #endif
@@ -62,15 +79,7 @@ typedef              float XFtype    __attribute__ ((mode (XF)));
 typedef                float TFtype    __attribute__ ((mode (TF)));
 #endif
 
-#if BITS_PER_WORD==16
-typedef int word_type __attribute__ ((mode (HI)));
-#endif
-#if BITS_PER_WORD==32
-typedef int word_type __attribute__ ((mode (SI)));
-#endif
-#if BITS_PER_WORD==64
-typedef int word_type __attribute__ ((mode (DI)));
-#endif
+typedef int word_type __attribute__ ((mode (__word__)));
 
 /* Make sure that we don't accidentally use any normal C language built-in
    type names in the first part of this file.  Instead we want to use *only*
@@ -89,9 +98,9 @@ typedef int word_type __attribute__ ((mode (DI)));
 #define SI_TYPE_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 
 /* DIstructs are pairs of SItype values in the order determined by
-   WORDS_BIG_ENDIAN.  */
+   LIBGCC2_WORDS_BIG_ENDIAN.  */
 
-#if WORDS_BIG_ENDIAN
+#if LIBGCC2_WORDS_BIG_ENDIAN
   struct DIstruct {SItype high, low;};
 #else
   struct DIstruct {SItype low, high;};
@@ -107,7 +116,9 @@ typedef union
   DItype ll;
 } DIunion;
 
-#if defined (L_udivmoddi4) || defined (L_muldi3) || defined (L_udiv_w_sdiv)
+#if (defined (L_udivmoddi4) || defined (L_muldi3) || defined (L_udiv_w_sdiv)\
+     || defined (L_divdi3) || defined (L_udivdi3) \
+     || defined (L_moddi3) || defined (L_umoddi3))
 
 #include "longlong.h"
 
@@ -115,15 +126,19 @@ typedef union
 
 extern DItype __fixunssfdi (SFtype a);
 extern DItype __fixunsdfdi (DFtype a);
+#if LONG_DOUBLE_TYPE_SIZE == 96
 extern DItype __fixunsxfdi (XFtype a);
+#endif
+#if LONG_DOUBLE_TYPE_SIZE == 128
+extern DItype __fixunstfdi (TFtype a);
+#endif
 \f
 #if defined (L_negdi2) || defined (L_divdi3) || defined (L_moddi3)
 #if defined (L_divdi3) || defined (L_moddi3)
 static inline
 #endif
 DItype
-__negdi2 (u)
-     DItype u;
+__negdi2 (DItype u)
 {
   DIunion w;
   DIunion uu;
@@ -137,46 +152,14 @@ __negdi2 (u)
 }
 #endif
 \f
-#ifdef L_lshldi3
-DItype
-__lshldi3 (u, b)
-     DItype u;
-     SItype b;
-{
-  DIunion w;
-  SItype bm;
-  DIunion uu;
-
-  if (b == 0)
-    return u;
-
-  uu.ll = u;
-
-  bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
-  if (bm <= 0)
-    {
-      w.s.low = 0;
-      w.s.high = (USItype)uu.s.low << -bm;
-    }
-  else
-    {
-      USItype carries = (USItype)uu.s.low >> bm;
-      w.s.low = (USItype)uu.s.low << b;
-      w.s.high = ((USItype)uu.s.high << b) | carries;
-    }
-
-  return w.ll;
-}
-#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;
-     SItype b;
+__lshrdi3 (DItype u, word_type b)
 {
   DIunion w;
-  SItype bm;
+  word_type bm;
   DIunion uu;
 
   if (b == 0)
@@ -203,12 +186,10 @@ __lshrdi3 (u, b)
 
 #ifdef L_ashldi3
 DItype
-__ashldi3 (u, b)
-     DItype u;
-     SItype b;
+__ashldi3 (DItype u, word_type b)
 {
   DIunion w;
-  SItype bm;
+  word_type bm;
   DIunion uu;
 
   if (b == 0)
@@ -235,12 +216,10 @@ __ashldi3 (u, b)
 
 #ifdef L_ashrdi3
 DItype
-__ashrdi3 (u, b)
-     DItype u;
-     SItype b;
+__ashrdi3 (DItype u, word_type b)
 {
   DIunion w;
-  SItype bm;
+  word_type bm;
   DIunion uu;
 
   if (b == 0)
@@ -268,8 +247,7 @@ __ashrdi3 (u, b)
 \f
 #ifdef L_ffsdi2
 DItype
-__ffsdi2 (u)
-     DItype u;
+__ffsdi2 (DItype u)
 {
   DIunion uu, w;
   uu.ll = u;
@@ -289,8 +267,7 @@ __ffsdi2 (u)
 \f
 #ifdef L_muldi3
 DItype
-__muldi3 (u, v)
-     DItype u, v;
+__muldi3 (DItype u, DItype v)
 {
   DIunion w;
   DIunion uu, vv;
@@ -307,9 +284,9 @@ __muldi3 (u, v)
 #endif
 \f
 #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;
@@ -404,8 +381,19 @@ __udiv_w_sdiv (rp, a1, a0, d)
   *rp = r;
   return q;
 }
+#else
+/* If sdiv_qrnnd doesn't exist, define dummy __udiv_w_sdiv.  */
+USItype
+__udiv_w_sdiv (USItype *rp, USItype a1, USItype a0, USItype d)
+{}
+#endif
 #endif
 \f
+#if (defined (L_udivdi3) || defined (L_divdi3) || \
+     defined (L_umoddi3) || defined (L_moddi3))
+#define L_udivmoddi4
+#endif
+
 #ifdef L_udivmoddi4
 static const UQItype __clz_tab[] =
 {
@@ -419,10 +407,12 @@ static const UQItype __clz_tab[] =
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 };
 
+#if (defined (L_udivdi3) || defined (L_divdi3) || \
+     defined (L_umoddi3) || defined (L_moddi3))
+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;
@@ -532,7 +522,7 @@ __udivmoddi4 (n, d, rp)
              udiv_qrnnd (q1, n1, n2, n1, d0);
            }
 
-         /* n1 != d0... */
+         /* n1 != d0...  */
 
          udiv_qrnnd (q0, n0, n1, n0, d0);
 
@@ -643,10 +633,9 @@ __udivmoddi4 (n, d, rp)
 UDItype __udivmoddi4 ();
 
 DItype
-__divdi3 (u, v)
-     DItype u, v;
+__divdi3 (DItype u, DItype v)
 {
-  SItype c = 0;
+  word_type c = 0;
   DIunion uu, vv;
   DItype w;
 
@@ -671,10 +660,9 @@ __divdi3 (u, v)
 #ifdef L_moddi3
 UDItype __udivmoddi4 ();
 DItype
-__moddi3 (u, v)
-     DItype u, v;
+__moddi3 (DItype u, DItype v)
 {
-  SItype c = 0;
+  word_type c = 0;
   DIunion uu, vv;
   DItype w;
 
@@ -698,10 +686,9 @@ __moddi3 (u, v)
 #ifdef L_umoddi3
 UDItype __udivmoddi4 ();
 UDItype
-__umoddi3 (u, v)
-     UDItype u, v;
+__umoddi3 (UDItype u, UDItype v)
 {
-  DItype w;
+  UDItype w;
 
   (void) __udivmoddi4 (u, v, &w);
 
@@ -712,8 +699,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);
 }
@@ -721,8 +707,7 @@ __udivdi3 (n, d)
 \f
 #ifdef L_cmpdi2
 word_type
-__cmpdi2 (a, b)
-     DItype a, b;
+__cmpdi2 (DItype a, DItype b)
 {
   DIunion au, bu;
 
@@ -742,8 +727,7 @@ __cmpdi2 (a, b)
 
 #ifdef L_ucmpdi2
 word_type
-__ucmpdi2 (a, b)
-     DItype a, b;
+__ucmpdi2 (DItype a, DItype b)
 {
   DIunion au, bu;
 
@@ -766,8 +750,7 @@ __ucmpdi2 (a, b)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 DItype
-__fixunstfdi (a)
-     TFtype a;
+__fixunstfdi (TFtype a)
 {
   TFtype b;
   UDItype v;
@@ -796,8 +779,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);
@@ -810,8 +792,7 @@ __fixtfdi (a)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 DItype
-__fixunsxfdi (a)
-     XFtype a;
+__fixunsxfdi (XFtype a)
 {
   XFtype b;
   UDItype v;
@@ -840,8 +821,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);
@@ -854,8 +834,7 @@ __fixxfdi (a)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 DItype
-__fixunsdfdi (a)
-     DFtype a;
+__fixunsdfdi (DFtype a)
 {
   DFtype b;
   UDItype v;
@@ -884,8 +863,7 @@ __fixunsdfdi (a)
 
 #ifdef L_fixdfdi
 DItype
-__fixdfdi (a)
-     DFtype a;
+__fixdfdi (DFtype a)
 {
   if (a < 0)
     return - __fixunsdfdi (-a);
@@ -945,8 +923,7 @@ __fixsfdi (SFtype a)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 XFtype
-__floatdixf (u)
-     DItype u;
+__floatdixf (DItype u)
 {
   XFtype d;
   SItype negate = 0;
@@ -969,8 +946,7 @@ __floatdixf (u)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 TFtype
-__floatditf (u)
-     DItype u;
+__floatditf (DItype u)
 {
   TFtype d;
   SItype negate = 0;
@@ -993,8 +969,7 @@ __floatditf (u)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
 DFtype
-__floatdidf (u)
-     DItype u;
+__floatdidf (DItype u)
 {
   DFtype d;
   SItype negate = 0;
@@ -1015,10 +990,38 @@ __floatdidf (u)
 #define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 #define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+#define DI_SIZE (sizeof (DItype) * BITS_PER_UNIT)
+
+/* Define codes for all the float formats that we know of.  Note
+   that this is copied from real.h.  */
+   
+#define UNKNOWN_FLOAT_FORMAT 0
+#define IEEE_FLOAT_FORMAT 1
+#define VAX_FLOAT_FORMAT 2
+#define IBM_FLOAT_FORMAT 3
+
+/* Default to IEEE float if not specified.  Nearly all machines use it.  */
+#ifndef HOST_FLOAT_FORMAT
+#define        HOST_FLOAT_FORMAT       IEEE_FLOAT_FORMAT
+#endif
+
+#if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+#define DF_SIZE 53
+#define SF_SIZE 24
+#endif
+
+#if HOST_FLOAT_FORMAT == IBM_FLOAT_FORMAT
+#define DF_SIZE 56
+#define SF_SIZE 24
+#endif
+
+#if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT
+#define DF_SIZE 56
+#define SF_SIZE 24
+#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
@@ -1029,6 +1032,22 @@ __floatdisf (u)
   if (u < 0)
     u = -u, negate = 1;
 
+  /* Protect against double-rounding error.
+     Represent any low-order bits, that might be truncated in DFmode,
+     by a bit that won't be lost.  The bit can go in anywhere below the
+     rounding position of the SFmode.  A fixed mask and bit position
+     handles all usual configurations.  It doesn't handle the case
+     of 128-bit DImode, however.  */
+  if (DF_SIZE < DI_SIZE
+      && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE))
+    {
+#define REP_BIT ((USItype) 1 << (DI_SIZE - DF_SIZE))
+      if (u >= ((UDItype) 1 << DF_SIZE))
+       {
+         if ((USItype) u & (REP_BIT - 1))
+           u |= REP_BIT;
+       }
+    }
   f = (USItype) (u >> WORD_SIZE);
   f *= HIGH_HALFWORD_COEFF;
   f *= HIGH_HALFWORD_COEFF;
@@ -1039,11 +1058,20 @@ __floatdisf (u)
 #endif
 
 #if defined(L_fixunsxfsi) && LONG_DOUBLE_TYPE_SIZE == 96
-#include "glimits.h"
+/* Reenable the normal types, in case limits.h needs them.  */
+#undef char
+#undef short
+#undef int
+#undef long
+#undef unsigned
+#undef float
+#undef double
+#undef MIN
+#undef MAX
+#include <limits.h>
 
 USItype
-__fixunsxfsi (a)
-     XFtype a;
+__fixunsxfsi (XFtype a)
 {
   if (a >= - (DFtype) LONG_MIN)
     return (SItype) (a + LONG_MIN) - LONG_MIN;
@@ -1052,11 +1080,20 @@ __fixunsxfsi (a)
 #endif
 
 #ifdef L_fixunsdfsi
-#include "glimits.h"
+/* Reenable the normal types, in case limits.h needs them.  */
+#undef char
+#undef short
+#undef int
+#undef long
+#undef unsigned
+#undef float
+#undef double
+#undef MIN
+#undef MAX
+#include <limits.h>
 
 USItype
-__fixunsdfsi (a)
-     DFtype a;
+__fixunsdfsi (DFtype a)
 {
   if (a >= - (DFtype) LONG_MIN)
     return (SItype) (a + LONG_MIN) - LONG_MIN;
@@ -1065,7 +1102,17 @@ __fixunsdfsi (a)
 #endif
 
 #ifdef L_fixunssfsi
-#include "glimits.h"
+/* Reenable the normal types, in case limits.h needs them.  */
+#undef char
+#undef short
+#undef int
+#undef long
+#undef unsigned
+#undef float
+#undef double
+#undef MIN
+#undef MAX
+#include <limits.h>
 
 USItype
 __fixunssfsi (SFtype a)
@@ -1096,13 +1143,11 @@ __fixunssfsi (SFtype a)
 #ifdef L__gcc_bcmp
 
 /* Like bcmp except the sign is meaningful.
-   Reult is negative if S1 is less than S2,
+   Result is negative if S1 is less than S2,
    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)
     {
@@ -1116,6 +1161,11 @@ __gcc_bcmp (s1, s2, size)
 
 #endif
 \f\f
+#ifdef L__dummy
+void
+__dummy () {}
+#endif
+
 #ifdef L_varargs
 #ifdef __i860__
 #if defined(__svr4__) || defined(__alliant__)
@@ -1184,6 +1234,73 @@ asm ("___builtin_saveregs:");
        asm ("  st.l    %r28,12(%r16)"); /* pointer to overflow args */
 
 #else /* not __svr4__ */
+#if defined(__PARAGON__)
+       /*
+        *      we'll use SVR4-ish varargs but need SVR3.2 assembler syntax,
+        *      and we stand a better chance of hooking into libraries
+        *      compiled by PGI.  [andyp@ssd.intel.com]
+        */
+       asm ("  .text");
+       asm ("  .align  4");
+       asm (".globl    __builtin_saveregs");
+asm ("__builtin_saveregs:");
+       asm (".globl    ___builtin_saveregs");
+asm ("___builtin_saveregs:");
+
+        asm (" andnot  0x0f,sp,sp");   /* round down to 16-byte boundary */
+       asm ("  adds    -96,sp,sp");    /* allocate stack space for reg save
+                                          area and also for a new va_list
+                                          structure */
+       /* Save all argument registers in the arg reg save area.  The
+          arg reg save area must have the following layout (according
+          to the svr4 ABI):
+
+               struct {
+                 union  {
+                   float freg[8];
+                   double dreg[4];
+                 } float_regs;
+                 long  ireg[12];
+               };
+       */
+
+       asm ("  fst.q   f8,  0(sp)");
+       asm ("  fst.q   f12,16(sp)"); 
+       asm ("  st.l    r16,32(sp)");
+       asm ("  st.l    r17,36(sp)"); 
+       asm ("  st.l    r18,40(sp)");
+       asm ("  st.l    r19,44(sp)");
+       asm ("  st.l    r20,48(sp)");
+       asm ("  st.l    r21,52(sp)");
+       asm ("  st.l    r22,56(sp)");
+       asm ("  st.l    r23,60(sp)");
+       asm ("  st.l    r24,64(sp)");
+       asm ("  st.l    r25,68(sp)");
+       asm ("  st.l    r26,72(sp)");
+       asm ("  st.l    r27,76(sp)");
+
+       asm ("  adds    80,sp,r16");  /* compute the address of the new
+                                          va_list structure.  Put in into
+                                          r16 so that it will be returned
+                                          to the caller.  */
+
+       /* Initialize all fields of the new va_list structure.  This
+          structure looks like:
+
+               typedef struct {
+                   unsigned long       ireg_used;
+                   unsigned long       freg_used;
+                   long                *reg_base;
+                   long                *mem_ptr;
+               } va_list;
+       */
+
+       asm ("  st.l    r0, 0(r16)"); /* nfixed */
+       asm ("  st.l    r0, 4(r16)"); /* nfloating */
+       asm ("  st.l    sp, 8(r16)"); /* __va_ctl points to __va_struct.  */
+       asm ("  bri     r1");           /* delayed return */
+       asm ("   st.l   r28,12(r16)"); /* pointer to overflow args */
+#else /* not __PARAGON__ */
        asm ("  .text");
        asm ("  .align  4");
 
@@ -1221,6 +1338,7 @@ asm ("___builtin_saveregs:");
        asm ("  mov     r30,sp");
                                /* recover stack and pass address to start 
                                   of data.  */
+#endif /* not __PARAGON__ */
 #endif /* not __svr4__ */
 #else /* not __i860__ */
 #ifdef __sparc__
@@ -1255,11 +1373,14 @@ 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 ()
 {
   abort ();
 }
+
 #endif /* not __mips__ */
 #endif /* not __sparc__ */
 #endif /* not __i860__ */
@@ -1272,11 +1393,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,
+          int line, const char *filename)
 {
   fprintf (stderr, string, expression, line, filename);
   fflush (stderr);
@@ -1303,29 +1421,26 @@ struct bb
   const char **functions;
   const long *line_nums;
   const char **filenames;
+  char *flags;
 };
 
 #ifdef BLOCK_PROFILER_CODE
 BLOCK_PROFILER_CODE
 #else
+#ifndef inhibit_libc
 
 /* Simple minded basic block profiling output dumper for
-   systems that don't provde tcov support.  At present,
+   systems that don't provide tcov support.  At present,
    it requires atexit and stdio.  */
 
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
 #include <stdio.h>
+char *ctime ();
 
-#ifdef HAVE_ATEXIT
-extern void atexit (void (*) (void));
-#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"
 
-static struct bb *bb_head = (struct bb *)0;
+static struct bb *bb_head;
 
 /* Return the number of digits needed to print a value */
 /* __inline__ */ static int num_digits (long value, int base)
@@ -1347,8 +1462,118 @@ static struct bb *bb_head = (struct bb *)0;
 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;
+             unsigned char tmp;
+             int i;
+             int ret = 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;
+                     unsigned char tmp;
+                     int j;
+                     int ret = 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.  Perferably, 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");
@@ -1359,22 +1584,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;
@@ -1398,7 +1626,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])
@@ -1429,17 +1657,20 @@ __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,
                         (ptr->functions[i]) ? ptr->functions[i] : "<none>");
 
              if (line_p)
-               fprintf (file, " line= %*d", line_len, ptr->line_nums[i]);
+               fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]);
 
              if (file_p)
                fprintf (file, " file= %s",
@@ -1461,7 +1692,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;
@@ -1478,97 +1709,884 @@ __bb_init_func (struct bb *blocks)
   bb_head = blocks;
 }
 
-#endif /* !BLOCK_PROFILER_CODE */
-#endif /* L_bb */
-\f
-/* frills for C++ */
+#ifndef MACHINE_STATE_SAVE
+#define MACHINE_STATE_SAVE(ID)
+#endif
+#ifndef MACHINE_STATE_RESTORE
+#define MACHINE_STATE_RESTORE(ID)
+#endif
 
-#ifdef L_op_new
-typedef void (*vfp)(void);
+#include <string.h>
 
-extern vfp __new_handler;
+/* Number of buckets in hashtable of basic block addresses.  */
 
-/* void * operator new (size_t sz) */
-void *
-__builtin_new (size_t sz)
+#define BB_BUCKETS 311
+
+/* 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;
+  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);
-  if (p == 0)
-    (*__new_handler) ();
-  return p;
-}
-#endif /* L_op_new */
+enum bb_func_mode
+{
+  TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2
+};
 
-#ifdef L_new_handler
+struct bb_func
+{
+  struct bb_func *next;
+  char *funcname;
+  char *filename;
+  enum bb_func_mode mode;
+};
 
-#ifndef inhibit_libc
-/* This gets us __GNU_LIBRARY__.  */
-#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
-#include <stdio.h>
+/* This is the connection to the outside world.
+   The BLOCK_PROFILER macro must set __bb.blocks
+   and __bb.blockno.  */
 
-#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))
-#endif
-#endif /* inhibit_libc */
+struct {
+  unsigned long blockno;
+  struct bb *blocks;
+} __bb;
 
-typedef void (*vfp)(void);
+/* Vars to store addrs of source and destination basic blocks 
+   of a jump.  */
 
-extern void *__builtin_new (size_t);
-static void default_new_handler (void);
+static unsigned long bb_src = 0;
+static unsigned long bb_dst = 0;
 
-vfp __new_handler = default_new_handler;
+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;
 
-vfp
-__set_new_handler (handler)
-     vfp handler;
+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)
 {
-  vfp prev_handler;
+  int use_gzip;
+  char *p;
 
-  prev_handler = __new_handler;
-  if (handler == 0) handler = default_new_handler;
-  __new_handler = handler;
-  return prev_handler;
+  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)
+    {
+      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;
+        }
+    }
+
+  else
+    return fopen (fn, mode);
 }
 
-vfp
-set_new_handler (handler)
-     vfp handler;
+static int
+gclose (FILE *f)
 {
-  return __set_new_handler (handler);
+  struct stat buf;
+
+  if (f != 0)
+    {
+      if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode))
+        return pclose (f);
+
+      return fclose (f);
+    }
+  return 0;
 }
 
-#define MESSAGE "Virtual memory exceeded in `new'\n"
+#endif /* HAVE_POPEN */
+
+/* Called once per program.  */
 
 static void
-default_new_handler ()
+__bb_exit_trace_func ()
 {
-  /* 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));
-  /* don't call exit () because that may call global destructors which
-     may cause a loop.  */
-  _exit (-1);
-}
-#endif
+  FILE *file = fopen ("bb.out", "a");
+  struct bb_func *f;
+  struct bb_edge *e;
+  struct bb *b;
+        
+  if (!file)
+    perror ("bb.out");
 
-#ifdef L_op_delete
-/* void operator delete (void *ptr) */
-void
-__builtin_delete (void *ptr)
-{
-  if (ptr)
-    free (ptr);
-}
-#endif
-\f
+  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 %*d 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);
+}
+
+/* Called once per program.  */
+
+static void
+__bb_init_prg ()
+{
+
+  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
+
+  if (!(file = fopen ("bb.in", "r")))
+    return;
+
+  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);
+
+#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)
+        bzero ((char *) bb_hashbuckets, BB_BUCKETS);
+    }
+
+  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
+
+}
+
+/* Called upon entering a basic block.  */
+
+void
+__bb_trace_func ()
+{
+  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")
+
+}
+
+/* Called when returning from a function and `__bb_showret__' is set.  */
+
+static void
+__bb_trace_func_ret ()
+{
+  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:
+  ;
+
+}
+
+/* Called upon entering the first function of a file.  */
+
+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.  */
+
+void
+__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
+__bb_init_trace_func (struct bb *blocks, unsigned long blockno)
+{
+  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 /* not inhibit_libc */
+#endif /* not BLOCK_PROFILER_CODE */
+#endif /* L_bb */
+\f
+/* Default free-store management functions for C++, per sections 12.5 and
+   17.3.3 of the Working Paper.  */
+
+#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.  */
+
+typedef void (*vfp)(void);
+extern vfp __new_handler;
+extern void __default_new_handler (void);
+
+#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
+{
+  void *p;
+  vfp handler = (__new_handler) ? __new_handler : __default_new_handler;
+
+  /* malloc (0) is unpredictable; avoid it.  */
+  if (sz == 0)
+    sz = 1;
+  p = (void *) malloc (sz);
+  while (p == 0)
+    {
+      (*handler) ();
+      p = (void *) malloc (sz);
+    }
+  
+  return p;
+}
+#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.  */
+
+extern void * __builtin_new (size_t);
+
+#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 __builtin_new (sz);
+}
+#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 [].  */
+
+#ifndef inhibit_libc
+/* This gets us __GNU_LIBRARY__.  */
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
+#include <stdio.h>
+
+#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))
+#endif
+#endif /* inhibit_libc */
+
+typedef void (*vfp)(void);
+void __default_new_handler (void);
+
+vfp __new_handler = (vfp) 0;
+
+vfp
+set_new_handler (vfp handler)
+{
+  vfp prev_handler;
+
+  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"
+
+void
+__default_new_handler ()
+{
+#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);
+}
+#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.  */
+
+#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
+{
+  if (ptr)
+    free (ptr);
+}
+#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.  */
+
+extern void __builtin_delete (void *);
+
+#ifdef WEAK_ALIAS
+void __builtin_vec_delete (void *ptr)
+     __attribute__ ((weak, alias ("___builtin_vec_delete")));
+void
+___builtin_vec_delete (void *ptr)
+#else
+void
+__builtin_vec_delete (void *ptr)
+#endif
+{
+  __builtin_delete (ptr);
+}
+#endif
+
+/* End of C++ free-store management functions */
+\f
 #ifdef L_shtab
 unsigned int __shtab[] = {
     0x00000001, 0x00000002, 0x00000004, 0x00000008,
@@ -1588,12 +2606,14 @@ 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);
+#else
 #ifdef INSN_CACHE_SIZE
   static char array[INSN_CACHE_SIZE + INSN_CACHE_PLANE_SIZE + INSN_CACHE_LINE_WIDTH];
-  static int initialized = 0;
+  static int initialized;
   int offset;
   void *start_addr
   void *end_addr;
@@ -1616,7 +2636,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;
     }
@@ -1689,6 +2709,7 @@ __clear_cache (beg, end)
 #endif /* just one plane */
 #endif /* Cache is large */
 #endif /* Cache exists */
+#endif /* CLEAR_INSN_CACHE */
 }
 
 #endif /* L_clear_cache */
@@ -1697,10 +2718,77 @@ __clear_cache (beg, end)
 
 /* Jump to a trampoline, loading the static chain address.  */
 
+#ifdef WINNT
+
+long getpagesize()
+{
+#ifdef _ALPHA_
+  return 8192;
+#else
+  return 4096;
+#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 (VirtualProtect (addr, len, np, &op))
+    return 0;
+  else
+    return -1;
+    
+}
+
+#endif
+
 #ifdef TRANSFER_FROM_TRAMPOLINE 
 TRANSFER_FROM_TRAMPOLINE 
 #endif
 
+#if defined (NeXT) && defined (__MACH__)
+
+/* Make stack executable so we can call trampolines on stack.
+   This is called from INITIALIZE_TRAMPOLINE in next.h.  */
+#ifdef NeXTStep21
+ #include <mach.h>
+#else
+ #include <mach/mach.h>
+#endif
+
+void
+__enable_execute_stack (char *addr)
+{
+  kern_return_t r;
+  char *eaddr = addr + TRAMPOLINE_SIZE;
+  vm_address_t a = (vm_address_t) addr;
+
+  /* turn on execute access on stack */
+  r = vm_protect (task_self (), a, TRAMPOLINE_SIZE, FALSE, VM_PROT_ALL);
+  if (r != KERN_SUCCESS)
+    {
+      mach_error("vm_protect VM_PROT_ALL", r);
+      exit(1);
+    }
+
+  /* We inline the i-cache invalidation for speed */
+
+#ifdef CLEAR_INSN_CACHE
+  CLEAR_INSN_CACHE (addr, eaddr);
+#else
+  __clear_cache ((int) addr, (int) eaddr);
+#endif
+} 
+
+#endif /* defined (NeXT) && defined (__MACH__) */
+
 #ifdef __convex__
 
 /* Make stack executable so we can call trampolines on stack.
@@ -1724,11 +2812,43 @@ __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__
+
+/* Modified from the convex -code above.  */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/m88kbcs.h>
+
+void
+__enable_execute_stack ()
+{
+  int save_errno;
+  static unsigned long lowest = USRSTACK;
+  unsigned long current = (unsigned long) &save_errno & -NBPC;
+  
+  /* Ignore errno being set. memctl sets errno to EINVAL whenever the
+     address is seen as 'negative'. That is the case with the stack.   */
+
+  save_errno=errno;
+  if (lowest > current)
+    {
+      unsigned len=lowest-current;
+      memctl(current,len,MCT_TEXT);
+      lowest = current;
+    }
+  else
+    memctl(current,NBPC,MCT_TEXT);
+  errno=save_errno;
+}
+
+#endif /* __DOLPHIN__ */
+
 #ifdef __pyr__
 
 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
@@ -1739,7 +2859,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 ()
@@ -1754,6 +2874,31 @@ __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 */
 \f
 #ifdef L__main
@@ -1762,12 +2907,18 @@ __enable_execute_stack ()
 /* 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 niether. */
+   must define both, or neither.  */
 #ifndef NAME__MAIN
 #define NAME__MAIN "__main"
 #define SYMBOL__MAIN __main
 #endif
 
+#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
@@ -1776,25 +2927,17 @@ __do_global_dtors ()
 #ifdef DO_GLOBAL_DTORS_BODY
   DO_GLOBAL_DTORS_BODY;
 #else
-  unsigned nptrs = (unsigned HOST_WIDE_INT) __DTOR_LIST__[0];
-  unsigned i;
-
-  /* Some systems place the number of pointers
-     in the first word of the table.
-     On other systems, that word is -1.
-     In all cases, the table is null-terminated.  */
-
-  /* If the length is not recorded, count up to the null.  */
-  if (nptrs == -1)
-    for (nptrs = 0; __DTOR_LIST__[nptrs + 1] != 0; nptrs++);
-
-  /* GNU LD format.  */
-  for (i = nptrs; i >= 1; i--)
-    __DTOR_LIST__[i] ();
+  static func_ptr *p = __DTOR_LIST__ + 1;
+  while (*p)
+    {
+      p++;
+      (*(p-1)) ();
+    }
 #endif
 }
+#endif
 
-#ifndef INIT_SECTION_ASM_OP
+#ifndef HAS_INIT_SECTION
 /* Run all the global constructors on entry to the program.  */
 
 #ifndef ON_EXIT
@@ -1814,9 +2957,9 @@ __do_global_ctors ()
   DO_GLOBAL_CTORS_BODY;
   ON_EXIT (__do_global_dtors, 0);
 }
-#endif /* no INIT_SECTION_ASM_OP */
+#endif /* no HAS_INIT_SECTION */
 
-#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main)
+#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.
@@ -1829,14 +2972,14 @@ void
 SYMBOL__MAIN ()
 {
   /* Support recursive calls to `main': run initializers just once.  */
-  static int initialized = 0;
+  static int initialized;
   if (! initialized)
     {
       initialized = 1;
       __do_global_ctors ();
     }
 }
-#endif /* no INIT_SECTION_ASM_OP or INVOKE__main */
+#endif /* no HAS_INIT_SECTION or INVOKE__main */
 
 #endif /* L__main */
 \f
@@ -1852,7 +2995,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(CTOR_LISTS_DEFINED_EXTERNALLY)
-#ifdef __NeXT__
+#if defined(__NeXT__) || defined(_AIX)
 /* After 2.3, try this definition on all systems.  */
 func_ptr __CTOR_LIST__[2] = {0, 0};
 func_ptr __DTOR_LIST__[2] = {0, 0};
@@ -1867,21 +3010,77 @@ 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 volatile void _exit ();
+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
+#ifndef inhibit_libc
+  __bb_exit_func ();
+#endif
 #ifdef EXIT_BODY
   EXIT_BODY;
 #else
@@ -1896,37 +3095,463 @@ int _exit_dummy_decl = 0;      /* prevent compiler & linker warnings */
 
 #endif /* L_exit */
 \f
-/* In a.out systems, we need to have these dummy constructor and destructor
-   lists in the library.
-
-   When using `collect', the first link will resolve __CTOR_LIST__
-   and __DTOR_LIST__ to these symbols.  We will then run "nm" on the
-   result, build the correct __CTOR_LIST__ and __DTOR_LIST__, and relink.
-   Since we don't do the second link if no constructors existed, these
-   dummies must be fully functional empty lists.
-
-   When using `gnu ld', these symbols will be used if there are no
-   constructors.  If there are constructors, the N_SETV symbol defined
-   by the linker from the N_SETT's in input files will define __CTOR_LIST__
-   and __DTOR_LIST__ rather than its being allocated as common storage
-   by the definitions below.
-
-   When using a linker that supports constructor and destructor segments,
-   these definitions will not be used, since crtbegin.o and crtend.o
-   (from crtstuff.c) will have already defined __CTOR_LIST__ and
-    __DTOR_LIST__.  The crt*.o files are passed directly to the linker
-   on its command line, by gcc.  */
-
-/* The list needs two elements:  one is ignored (the old count); the
-   second is the terminating zero.  Since both values are zero, this
-   declaration is not initialized, and it becomes `common'.  */
-
-#ifdef L_ctor_list
-#include "gbl-ctors.h"
-func_ptr __CTOR_LIST__[2];
+#ifdef L_eh
+
+#ifdef EH_TABLE_LOOKUP
+
+EH_TABLE_LOOKUP
+
+#else
+
+void
+__default_terminate ()
+{
+  abort ();
+}
+
+void (*__terminate_func)() = __default_terminate;
+
+void
+__terminate ()
+{
+  (*__terminate_func)();
+}
+
+/* Calls to __sjthrow are generated by the compiler when an exception
+   is raised when using the setjmp/longjmp exception handling codegen
+   method.  */
+
+extern void longjmp (void *, int);
+
+extern void *__eh_type;
+
+static void *top_elt[2];
+void **__dynamic_handler_chain = top_elt;
+
+/* Routine to get the head of the current thread's dynamic handler chain
+   use for exception handling.
+
+   TODO: make thread safe.  */
+
+void ***
+__get_dynamic_handler_chain ()
+{
+  return &__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 (we know this
+   when the dynamic handler chain is top_elt).  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 ()
+{
+  void ***dhc = __get_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])
+    {
+      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
+       {
+         *dhc = buf;
+         while (cleanup[0])
+           {
+             func = (void(*)(void*, int))cleanup[0][1];
+             arg = (void*)cleanup[0][2];
 
-#ifdef L_dtor_list
-#include "gbl-ctors.h"
-func_ptr __DTOR_LIST__[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_type || (*dhc) == top_elt)
+    __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
+  __builtin_longjmp (jmpbuf, 1);
+#endif
+}
+
+/* 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 ()
+{
+  void ***dhc = __get_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])
+    {
+      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
+       {
+         *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 ();
+}
+
+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 struct exception_table_node *exception_table_list;
+
+/* 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:
+
+    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;
+
+   Assuming a correctly sorted table (ascending order) this routine should
+   return the tightest match...
+
+   In the advent of a tie, we have to give the last entry, as it represents
+   an inner block.  */
+
+void *
+__find_first_exception_table_match (void *pc)
+{
+  register struct exception_table_node *tnp;
+  register exception_table *table;
+  int pos;
+  int best;
+
+#if 0
+  printf ("find_first_exception_table_match (): pc = %x!\n", pc);
+#endif
+
+  for (tnp = exception_table_list; tnp != 0; tnp = tnp->next)
+    {
+      if (tnp->start > pc || tnp->end <= pc)
+       continue;
+
+      table = tnp->table;
+
+      pos = 0;
+      best = 0;
+#if 0
+      /* We can't do this yet, as we don't know that the table is sorted.  */
+      do {
+       ++pos;
+       if (table[pos].start > pc)
+         /* found the first table[pos].start > pc, so the previous
+            entry better be the one we want! */
+         break;
+      } while (table[pos].exception_handler != (void *) -1);
+
+      --pos;
+      if (table[pos].start <= pc && table[pos].end > pc)
+       {
+#if 0
+         printf ("find_first_eh_table_match (): found match: %x\n", table[pos].exception_handler);
+#endif
+         return table[pos].exception_handler;
+       }
+#else
+      while (table[++pos].exception_handler != (void *) -1) {
+       if (table[pos].start <= pc && table[pos].end > 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;
+#endif
+    }
+
+#if 0
+  printf ("find_first_eh_table_match (): else: returning NULL!\n");
+#endif
+  return (void *) 0;
+}
+
+void
+__register_exceptions (exception_table *table)
+{
+  struct exception_table_node *node;
+  exception_table *range = table + 1;
+
+  if (range->start == (void *) -1)
+    return;
+
+  node = (struct exception_table_node *)
+    malloc (sizeof (struct exception_table_node));
+  node->table = table;
+
+  /* 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++)
+    {
+      if (range->start < node->start)
+       node->start = range->start;
+      if (range->end > node->end)
+       node->end = range->end;
+    }
+
+  node->next = exception_table_list;
+  exception_table_list = node;
+}
+#endif
+
+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;
+}
+
+/* Throw stub routine.
+
+   This is work in progress, but not completed yet.  */
+
+void
+__throw ()
+{
+  abort ();
+}
+
+/* This value identifies the place from which an exception is being
+   thrown.  */
+
+void *__eh_pc;
+
+/* See expand_builtin_throw for details.  */
+
+void **__eh_ffetmnpc () {
+  static void *buf[2] = {
+    &__find_first_exception_table_match,
+    &__eh_pc
+  };
+  return buf;
+}
+
+void
+__empty ()
+{
+}
+
+#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) && !defined _ARCH_PPC
+__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(rs6000) || #machine(powerpc)) && defined _ARCH_PPC
+__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;
+}
+#else
+__unwind_function(void *ptr)
+{
+  abort ();
+}
+#endif /* powerpc */
+#endif /* L_eh */
+\f
+#ifdef L_pure
+#ifndef inhibit_libc
+/* This gets us __GNU_LIBRARY__.  */
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
+#include <stdio.h>
+
+#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))
+#endif
+#endif /* inhibit_libc */
+
+#define MESSAGE "pure virtual method called\n"
+
+void
+__pure_virtual ()
+{
+#ifndef inhibit_libc
+  write (2, MESSAGE, sizeof (MESSAGE) - 1);
+#endif
+  _exit (-1);
+}
 #endif