OSDN Git Service

Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
[pf3gnuchains/gcc-fork.git] / gcc / libgcc2.c
index 921afef..0275525 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, 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
 \f
+/* 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)
 \f
 #ifdef L_ffsdi2
 DItype
-__ffsdi2 (u)
-     DItype u;
+__ffsdi2 (DItype u)
 {
   DIunion uu, w;
   uu.ll = u;
@@ -273,8 +285,7 @@ __ffsdi2 (u)
 \f
 #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
 \f
@@ -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)
 \f
 #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
 \f\f
+#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 ()
@@ -1418,12 +1403,12 @@ __builtin_saveregs ()
 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
 #include <stdio.h>
 /* This is used by the `assert' macro.  */
+extern void __eprintf (const char *, const char *, unsigned int, const char *)
+  __attribute__ ((__noreturn__));
+
 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 +1435,7 @@ struct bb
   const char **functions;
   const long *line_nums;
   const char **filenames;
+  char *flags;
 };
 
 #ifdef BLOCK_PROFILER_CODE
@@ -1465,19 +1451,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 +1477,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 +1592,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 +1634,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 +1665,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 +1700,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,786 +1717,2256 @@ __bb_init_func (struct bb *blocks)
   bb_head = blocks;
 }
 
-#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. */
+#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
-{
-  void *p;
-  vfp handler = (__new_handler) ? __new_handler : __default_new_handler;
+/* Maximum length of string in file bb.in.  */
 
-  /* 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 */
+#define BBINBUFSIZE 500
 
-#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.  */
+/* BBINBUFSIZE-1 with double quotes. We could use #BBINBUFSIZE or
+   "BBINBUFSIZE" but want to avoid trouble with preprocessors.  */
 
-extern void * __builtin_new (size_t);
+#define BBINBUFSIZESTR "499"
 
-#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
+struct bb_edge
 {
-  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 []. */
+  struct bb_edge *next;
+  unsigned long src_addr;
+  unsigned long dst_addr;
+  unsigned long count;
+};
 
-#ifndef inhibit_libc
-/* This gets us __GNU_LIBRARY__.  */
-#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
-#include <stdio.h>
+enum bb_func_mode
+{
+  TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2
+};
 
-#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 bb_func
+{
+  struct bb_func *next;
+  char *funcname;
+  char *filename;
+  enum bb_func_mode mode;
+};
 
-typedef void (*vfp)(void);
-void __default_new_handler (void);
+/* This is the connection to the outside world.
+   The BLOCK_PROFILER macro must set __bb.blocks
+   and __bb.blockno.  */
 
-vfp __new_handler = (vfp)0;
+struct {
+  unsigned long blockno;
+  struct bb *blocks;
+} __bb;
 
-vfp
-set_new_handler (vfp handler)
-{
-  vfp prev_handler;
+/* Vars to store addrs of source and destination basic blocks 
+   of a jump.  */
 
-  prev_handler = __new_handler;
-  if (handler == 0) handler = __default_new_handler;
-  __new_handler = handler;
-  return prev_handler;
-}
+static unsigned long bb_src = 0;
+static unsigned long bb_dst = 0;
 
-#define MESSAGE "Virtual memory exceeded in `new'\n"
+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;
 
-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
+static unsigned long *bb_stack = (unsigned long *) 0;
+static size_t bb_stacksize = 0;
 
-#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. */
+static int reported = 0;
 
-#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
+/* 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 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. */
+#ifdef HAVE_POPEN
 
-extern void __builtin_delete (void *);
+/*#include <sys/types.h>*/
+#include <sys/stat.h>
+/*#include <malloc.h>*/
 
-#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
+/* Commands executed by gopen.  */
 
-/* End of C++ free-store management functions */
-\f
-#ifdef L_shtab
-unsigned int __shtab[] = {
-    0x00000001, 0x00000002, 0x00000004, 0x00000008,
-    0x00000010, 0x00000020, 0x00000040, 0x00000080,
-    0x00000100, 0x00000200, 0x00000400, 0x00000800,
-    0x00001000, 0x00002000, 0x00004000, 0x00008000,
-    0x00010000, 0x00020000, 0x00040000, 0x00080000,
-    0x00100000, 0x00200000, 0x00400000, 0x00800000,
-    0x01000000, 0x02000000, 0x04000000, 0x08000000,
-    0x10000000, 0x20000000, 0x40000000, 0x80000000
-  };
-#endif
-\f
-#ifdef L_clear_cache
-/* Clear part of an instruction cache.  */
+#define GOPENDECOMPRESS "gzip -cd "
+#define GOPENCOMPRESS "gzip -c >"
 
-#define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH)
+/* 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.  */
 
-void
-__clear_cache (beg, end)
-     char *beg, *end;
+static FILE *
+gopen (char *fn, char *mode)
 {
-#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;
-  int offset;
-  void *start_addr
-  void *end_addr;
-  typedef (*function_ptr) ();
+  int use_gzip;
+  char *p;
 
-#if (INSN_CACHE_SIZE / INSN_CACHE_LINE_WIDTH) < 16
-  /* It's cheaper to clear the whole cache.
-     Put in a series of jump instructions so that calling the beginning
-     of the cache will clear the whole thing.  */
+  if (mode[1])
+    return (FILE *) 0;
 
-  if (! initialized)
-    {
-      int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
-                & -INSN_CACHE_LINE_WIDTH);
-      int end_ptr = ptr + INSN_CACHE_SIZE;
+  if (mode[0] != 'r' && mode[0] != 'w') 
+    return (FILE *) 0;
 
-      while (ptr < end_ptr)
-       {
-         *(INSTRUCTION_TYPE *)ptr
-           = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH;
-         ptr += INSN_CACHE_LINE_WIDTH;
-       }
-      *(INSTRUCTION_TYPE *)(ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION;
+  p = fn + strlen (fn)-1;
+  use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z'))
+             || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z'));
 
-      initialized = 1;
+  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;
+        }
     }
 
-  /* Call the beginning of the sequence.  */
-  (((function_ptr) (((int) array + INSN_CACHE_LINE_WIDTH - 1)
-                   & -INSN_CACHE_LINE_WIDTH))
-   ());
+  else
+    return fopen (fn, mode);
+}
 
-#else /* Cache is large.  */
+static int
+gclose (FILE *f)
+{
+  struct stat buf;
 
-  if (! initialized)
+  if (f != 0)
     {
-      int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
-                & -INSN_CACHE_LINE_WIDTH);
-
-      while (ptr < (int) array + sizeof array)
-       {
-         *(INSTRUCTION_TYPE *)ptr = RETURN_INSTRUCTION;
-         ptr += INSN_CACHE_LINE_WIDTH;
-       }
+      if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode))
+        return pclose (f);
 
-      initialized = 1;
+      return fclose (f);
     }
+  return 0;
+}
 
-  /* Find the location in array that occupies the same cache line as BEG.  */
+#endif /* HAVE_POPEN */
 
-  offset = ((int) beg & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1);
-  start_addr = (((int) (array + INSN_CACHE_PLANE_SIZE - 1)
-                & -INSN_CACHE_PLANE_SIZE)
-               + offset);
+/* Called once per program.  */
 
-  /* Compute the cache alignment of the place to stop clearing.  */
-#if 0  /* This is not needed for gcc's purposes.  */
-  /* If the block to clear is bigger than a cache plane,
-     we clear the entire cache, and OFFSET is already correct.  */ 
-  if (end < beg + INSN_CACHE_PLANE_SIZE)
-#endif
-    offset = (((int) (end + INSN_CACHE_LINE_WIDTH - 1)
-              & -INSN_CACHE_LINE_WIDTH)
-             & (INSN_CACHE_PLANE_SIZE - 1));
+static void
+__bb_exit_trace_func ()
+{
+  FILE *file = fopen ("bb.out", "a");
+  struct bb_func *f;
+  struct bb *b;
+        
+  if (!file)
+    perror ("bb.out");
 
-#if INSN_CACHE_DEPTH > 1
-  end_addr = (start_addr & -INSN_CACHE_PLANE_SIZE) + offset;
-  if (end_addr <= start_addr)
-    end_addr += INSN_CACHE_PLANE_SIZE;
+  if (bb_mode & 1)
+    {
+      if (!bb_tracefile)
+        perror ("bbtrace");
+      else
+#ifdef HAVE_POPEN
+        gclose (bb_tracefile);
+#else
+        fclose (bb_tracefile);
+#endif /* HAVE_POPEN */
+    }
 
-  for (plane = 0; plane < INSN_CACHE_DEPTH; plane++)
+  /* Check functions in `bb.in'.  */
+
+  if (file)
     {
-      int addr = start_addr + plane * INSN_CACHE_PLANE_SIZE;
-      int stop = end_addr + plane * INSN_CACHE_PLANE_SIZE;
+      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:        ;
+        }
 
-      while (addr != stop)
-       {
-         /* Call the return instruction at ADDR.  */
-         ((function_ptr) addr) ();
+      if (printed_something)
+       fprintf (file, "\n");
 
-         addr += INSN_CACHE_LINE_WIDTH;
-       }
     }
-#else /* just one plane */
-  do
+
+  if (bb_mode & 2)
     {
-      /* Call the return instruction at START_ADDR.  */
-      ((function_ptr) start_addr) ();
+      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");
 
-      start_addr += INSN_CACHE_LINE_WIDTH;
+        }
     }
-  while ((start_addr % INSN_CACHE_SIZE) != offset);
-#endif /* just one plane */
-#endif /* Cache is large */
-#endif /* Cache exists */
-#endif /* CLEAR_INSN_CACHE */
-}
 
-#endif /* L_clear_cache */
-\f
-#ifdef L_trampoline
+   if (file)
+     fclose (file);
 
-/* Jump to a trampoline, loading the static chain address.  */
+   /* Free allocated memory.  */
 
-#ifdef TRANSFER_FROM_TRAMPOLINE 
-TRANSFER_FROM_TRAMPOLINE 
-#endif
+   f = bb_func_head;
+   while (f)
+     {
+       struct bb_func *old = f;
 
-#if defined (NeXT) && defined (__MACH__)
+       f = f->next;
+       if (old->funcname) free (old->funcname);
+       if (old->filename) free (old->filename);
+       free (old);
+     }
 
-/* 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
+   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)
+        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
+
+}
+
+/* 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
+#ifdef L_shtab
+unsigned int __shtab[] = {
+    0x00000001, 0x00000002, 0x00000004, 0x00000008,
+    0x00000010, 0x00000020, 0x00000040, 0x00000080,
+    0x00000100, 0x00000200, 0x00000400, 0x00000800,
+    0x00001000, 0x00002000, 0x00004000, 0x00008000,
+    0x00010000, 0x00020000, 0x00040000, 0x00080000,
+    0x00100000, 0x00200000, 0x00400000, 0x00800000,
+    0x01000000, 0x02000000, 0x04000000, 0x08000000,
+    0x10000000, 0x20000000, 0x40000000, 0x80000000
+  };
+#endif
+\f
+#ifdef L_clear_cache
+/* Clear part of an instruction cache.  */
+
+#define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH)
+
+void
+__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;
+  int offset;
+  void *start_addr
+  void *end_addr;
+  typedef (*function_ptr) ();
+
+#if (INSN_CACHE_SIZE / INSN_CACHE_LINE_WIDTH) < 16
+  /* It's cheaper to clear the whole cache.
+     Put in a series of jump instructions so that calling the beginning
+     of the cache will clear the whole thing.  */
+
+  if (! initialized)
+    {
+      int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+                & -INSN_CACHE_LINE_WIDTH);
+      int end_ptr = ptr + INSN_CACHE_SIZE;
+
+      while (ptr < end_ptr)
+       {
+         *(INSTRUCTION_TYPE *)ptr
+           = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH;
+         ptr += INSN_CACHE_LINE_WIDTH;
+       }
+      *(INSTRUCTION_TYPE *) (ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION;
+
+      initialized = 1;
+    }
+
+  /* Call the beginning of the sequence.  */
+  (((function_ptr) (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+                   & -INSN_CACHE_LINE_WIDTH))
+   ());
+
+#else /* Cache is large.  */
+
+  if (! initialized)
+    {
+      int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+                & -INSN_CACHE_LINE_WIDTH);
+
+      while (ptr < (int) array + sizeof array)
+       {
+         *(INSTRUCTION_TYPE *)ptr = RETURN_INSTRUCTION;
+         ptr += INSN_CACHE_LINE_WIDTH;
+       }
+
+      initialized = 1;
+    }
+
+  /* Find the location in array that occupies the same cache line as BEG.  */
+
+  offset = ((int) beg & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1);
+  start_addr = (((int) (array + INSN_CACHE_PLANE_SIZE - 1)
+                & -INSN_CACHE_PLANE_SIZE)
+               + offset);
+
+  /* Compute the cache alignment of the place to stop clearing.  */
+#if 0  /* This is not needed for gcc's purposes.  */
+  /* If the block to clear is bigger than a cache plane,
+     we clear the entire cache, and OFFSET is already correct.  */ 
+  if (end < beg + INSN_CACHE_PLANE_SIZE)
+#endif
+    offset = (((int) (end + INSN_CACHE_LINE_WIDTH - 1)
+              & -INSN_CACHE_LINE_WIDTH)
+             & (INSN_CACHE_PLANE_SIZE - 1));
+
+#if INSN_CACHE_DEPTH > 1
+  end_addr = (start_addr & -INSN_CACHE_PLANE_SIZE) + offset;
+  if (end_addr <= start_addr)
+    end_addr += INSN_CACHE_PLANE_SIZE;
+
+  for (plane = 0; plane < INSN_CACHE_DEPTH; plane++)
+    {
+      int addr = start_addr + plane * INSN_CACHE_PLANE_SIZE;
+      int stop = end_addr + plane * INSN_CACHE_PLANE_SIZE;
+
+      while (addr != stop)
+       {
+         /* Call the return instruction at ADDR.  */
+         ((function_ptr) addr) ();
+
+         addr += INSN_CACHE_LINE_WIDTH;
+       }
+    }
+#else /* just one plane */
+  do
+    {
+      /* Call the return instruction at START_ADDR.  */
+      ((function_ptr) start_addr) ();
+
+      start_addr += INSN_CACHE_LINE_WIDTH;
+    }
+  while ((start_addr % INSN_CACHE_SIZE) != offset);
+#endif /* just one plane */
+#endif /* Cache is large */
+#endif /* Cache exists */
+#endif /* CLEAR_INSN_CACHE */
+}
+
+#endif /* L_clear_cache */
+\f
+#ifdef L_trampoline
+
+/* Jump to a trampoline, loading the static chain address.  */
+
+#if defined(WINNT) && ! defined(__CYGWIN__)
+
+long getpagesize()
+{
+#ifdef _ALPHA_
+  return 8192;
+#else
+  return 4096;
+#endif
+}
+
+#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 (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.
+   This is called from INITIALIZE_TRAMPOLINE in convex.h.  */
+
+#include <sys/mman.h>
+#include <sys/vmparam.h>
+#include <machine/machparam.h>
+
+void
+__enable_execute_stack ()
+{
+  int fp;
+  static unsigned lowest = USRSTACK;
+  unsigned current = (unsigned) &fp & -NBPG;
+
+  if (lowest > current)
+    {
+      unsigned len = lowest - current;
+      mremap (current, &len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE);
+      lowest = current;
+    }
+
+  /* Clear instruction cache in case an old trampoline is in it.  */
+  asm ("pich");
+}
+#endif /* __convex__ */
+
+#ifdef __sysV88__
+
+/* 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 /* __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__
+
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/vmmac.h>
+
+/* Modified from the convex -code above.
+   mremap promises to clear the i-cache.  */
+
+void
+__enable_execute_stack ()
+{
+  int fp;
+  if (mprotect (((unsigned int)&fp/PAGSIZ)*PAGSIZ, PAGSIZ,
+               PROT_READ|PROT_WRITE|PROT_EXEC))
+    {
+      perror ("mprotect in __enable_execute_stack");
+      fflush (stderr);
+      abort ();
+    }
+}
+#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
+#ifndef __CYGWIN__
+#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.  */
+#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
-__enable_execute_stack (addr)
-     char *addr;
+__do_global_dtors ()
 {
-  kern_return_t r;
-  char *eaddr = addr + TRAMPOLINE_SIZE;
-  vm_address_t a = (vm_address_t) addr;
+#ifdef DO_GLOBAL_DTORS_BODY
+  DO_GLOBAL_DTORS_BODY;
+#else
+  static func_ptr *p = __DTOR_LIST__ + 1;
+  while (*p)
+    {
+      p++;
+      (*(p-1)) ();
+    }
+#endif
+}
+#endif
 
-  /* turn on execute access on stack */
-  r = vm_protect (task_self (), a, TRAMPOLINE_SIZE, FALSE, VM_PROT_ALL);
-  if (r != KERN_SUCCESS)
+#ifndef HAS_INIT_SECTION
+/* Run all the global constructors on entry to the program.  */
+
+#ifndef ON_EXIT
+#define ON_EXIT(a, b)
+#else
+/* Make sure the exit routine is pulled in to define the globals as
+   bss symbols, just in case the linker does not automatically pull
+   bss definitions from the library.  */
+
+extern int _exit_dummy_decl;
+int *_exit_dummy_ref = &_exit_dummy_decl;
+#endif /* ON_EXIT */
+
+void
+__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.
+
+   For many systems, this routine calls __do_global_ctors.
+   For systems which support a .init section we use the .init section
+   to run __do_global_ctors, so we need not do anything here.  */
+
+void
+SYMBOL__MAIN ()
+{
+  /* Support recursive calls to `main': run initializers just once.  */
+  static int initialized;
+  if (! initialized)
     {
-      mach_error("vm_protect VM_PROT_ALL", r);
-      exit(1);
+      initialized = 1;
+      __do_global_ctors ();
     }
+}
+#endif /* no HAS_INIT_SECTION or INVOKE__main */
 
-  /* We inline the i-cache invalidation for speed */
+#endif /* L__main */
+#endif /* __CYGWIN__ */
+\f
+#ifdef L_ctors
 
-#ifdef CLEAR_INSN_CACHE
-  CLEAR_INSN_CACHE (addr, eaddr);
+#include "gbl-ctors.h"
+
+/* Provide default definitions for the lists of constructors and
+   destructors, so that we don't get linker errors.  These symbols are
+   intentionally bss symbols, so that gld and/or collect will provide
+   the right values.  */
+
+/* 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)
+#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};
 #else
-  __clear_cache ((int) addr, (int) eaddr);
+func_ptr __CTOR_LIST__[2];
+func_ptr __DTOR_LIST__[2];
 #endif
-} 
+#endif /* no INIT_SECTION_ASM_OP and not CTOR_LISTS_DEFINED_EXTERNALLY */
+#endif /* L_ctors */
+\f
+#ifdef L_exit
 
-#endif /* defined (NeXT) && defined (__MACH__) */
+#include "gbl-ctors.h"
 
-#ifdef __convex__
+#ifdef NEED_ATEXIT
+# ifdef ON_EXIT
+#  undef ON_EXIT
+# endif
+int _exit_dummy_decl = 0;      /* prevent compiler & linker warnings */
+#endif
 
-/* Make stack executable so we can call trampolines on stack.
-   This is called from INITIALIZE_TRAMPOLINE in convex.h.  */
+#ifndef ON_EXIT
 
-#include <sys/mman.h>
-#include <sys/vmparam.h>
-#include <machine/machparam.h>
+#ifdef NEED_ATEXIT
+# include <errno.h>
 
-void
-__enable_execute_stack ()
+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)
 {
-  int fp;
-  static unsigned lowest = USRSTACK;
-  unsigned current = (unsigned) &fp & -NBPG;
+  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 (lowest > current)
+/* 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 (int status)
+{
+#if !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF)
+#ifdef NEED_ATEXIT
+  if (atexit_chain)
     {
-      unsigned len = lowest - current;
-      mremap (current, &len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE);
-      lowest = current;
+      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
+  _cleanup ();
+#endif
+  _exit (status);
+}
 
-  /* Clear instruction cache in case an old trampoline is in it. */
-  asm ("pich");
+#else /* ON_EXIT defined */
+int _exit_dummy_decl = 0;      /* prevent compiler & linker warnings */
+
+# ifndef HAVE_ATEXIT
+/* Provide a fake for atexit() using ON_EXIT.  */
+int atexit (func_ptr func)
+{
+  return ON_EXIT (func, NULL);
 }
-#endif /* __convex__ */
+# endif /* HAVE_ATEXIT */
+#endif /* ON_EXIT defined */
 
-#ifdef __DOLPHIN__
+#endif /* L_exit */
+\f
+#ifdef L_eh
 
-/* Modified from the convex -code above. */
+#include "gthr.h"
 
-#include <sys/param.h>
-#include <errno.h>
-#include <sys/m88kbcs.h>
+/* Shared exception handling support routines.  */
+
+extern void __default_terminate (void) __attribute__ ((__noreturn__));
 
 void
-__enable_execute_stack ()
+__default_terminate ()
 {
-  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.   */
+  abort ();
+}
 
-  save_errno=errno;
-  if (lowest > current)
+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 ()
+{
+}
+\f
+
+/* Include definitions of EH context and table layout */
+
+#include "eh-common.h"
+#ifndef inhibit_libc
+#include <stdio.h>
+#endif
+
+/* Allocate and return a new EH context structure. */
+
+extern void __throw ();
+
+static void *
+new_eh_context ()
+{
+  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;
+}
+\f
+#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)
     {
-      unsigned len=lowest-current;
-      memctl(current,len,MCT_TEXT);
-      lowest = current;
+      /* Use static version of EH context. */
+      get_eh_context = &eh_context_static;
+    }
+
+#else /* no __GTHREADS */
+
+  /* Use static version of EH context. */
+  get_eh_context = &eh_context_static;
+
+#endif /* no __GTHREADS */
+
+  return (*get_eh_context) ();
+}
+
+/* Return a static EH context. */
+
+static struct eh_context *
+eh_context_static ()
+{
+  static struct eh_context eh;
+  static int initialized;
+  static void *top_elt[2];
+
+  if (! initialized)
+    {
+      initialized = 1;
+      memset (&eh, 0, sizeof eh);
+      eh.dynamic_handler_chain = top_elt;
+    }
+  return &eh;
+}
+
+#if __GTHREADS
+/* Return a thread specific EH context. */
+
+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 ();
     }
-  else
-    memctl(current,NBPC,MCT_TEXT);
-  errno=save_errno;
+
+  return eh;
 }
+#endif __GTHREADS
+\f
+/* Support routines for setjmp/longjmp exception handling.  */
 
-#endif /* __DOLPHIN__ */
+/* Calls to __sjthrow are generated by the compiler when an exception
+   is raised when using the setjmp/longjmp exception handling codegen
+   method.  */
 
-#ifdef __pyr__
+#ifdef DONT_USE_BUILTIN_SETJMP
+extern void longjmp (void *, int);
+#endif
 
-#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
-#include <stdio.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/vmmac.h>
+/* Routine to get the head of the current thread's dynamic handler chain
+   use for exception handling. */
 
-/* Modified from the convex -code above.
-   mremap promises to clear the i-cache. */
+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.  */
+
+extern void __sjthrow (void) __attribute__ ((__noreturn__));
 
 void
-__enable_execute_stack ()
+__sjthrow ()
 {
-  int fp;
-  if (mprotect (((unsigned int)&fp/PAGSIZ)*PAGSIZ, PAGSIZ,
-               PROT_READ|PROT_WRITE|PROT_EXEC))
+  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])
     {
-      perror ("mprotect in __enable_execute_stack");
-      fflush (stderr);
-      abort ();
+      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 ();
+       }
     }
+  
+  /* 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
+  __builtin_longjmp (jmpbuf, 1);
+#endif
 }
-#endif /* __pyr__ */
-#endif /* L_trampoline */
-\f
-#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. */
-#ifndef NAME__MAIN
-#define NAME__MAIN "__main"
-#define SYMBOL__MAIN __main
-#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.  */
 
-#if !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF)
-/* Run all the global destructors on exit from the program.  */
+extern void __sjpopnthrow (void) __attribute__ ((__noreturn__));
 
 void
-__do_global_dtors ()
+__sjpopnthrow ()
 {
-#ifdef DO_GLOBAL_DTORS_BODY
-  DO_GLOBAL_DTORS_BODY;
+  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
-  func_ptr *p;
-  for (p = __DTOR_LIST__ + 1; *p; )
-    (*p++) ();
+      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 ();
 }
+\f
+/* Support code for all exception region-based exception handling.  */
+
+int
+__eh_rtime_match (void *rtime)
+{
+  void *info;
+  __eh_matcher matcher;
+  void *ret;
+
+  info = *(__get_eh_info ());
+  matcher = ((__eh_info *)info)->match_function;
+  if (! matcher)
+    {
+#ifndef inhibit_libc
+      fprintf (stderr, "Internal Compiler Bug: No runtime type matcher.");
 #endif
+      return 0;
+    }
+  ret = (*matcher) (info, rtime, (void *)0);
+  return (ret != NULL);
+}
 
-#ifndef INIT_SECTION_ASM_OP
-/* Run all the global constructors on entry to the program.  */
+/* This value identifies the place from which an exception is being
+   thrown.  */
+
+#ifdef EH_TABLE_LOOKUP
+
+EH_TABLE_LOOKUP
 
-#ifndef ON_EXIT
-#define ON_EXIT(a, b)
 #else
-/* Make sure the exit routine is pulled in to define the globals as
-   bss symbols, just in case the linker does not automatically pull
-   bss definitions from the library.  */
 
-extern int _exit_dummy_decl;
-int *_exit_dummy_ref = &_exit_dummy_decl;
-#endif /* ON_EXIT */
+#ifdef DWARF2_UNWIND_INFO
 
-void
-__do_global_ctors ()
+
+/* Return the table version of an exception descriptor */
+
+short 
+__get_eh_table_version (exception_descriptor *table) 
 {
-  DO_GLOBAL_CTORS_BODY;
-  ON_EXIT (__do_global_dtors, 0);
+  return table->lang.version;
 }
-#endif /* no INIT_SECTION_ASM_OP */
 
-#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main)
-/* Subroutine called automatically by `main'.
-   Compiling a global function named `main'
-   produces an automatic call to this function at the beginning.
+/* Return the originating table language of an exception descriptor */
 
-   For many systems, this routine calls __do_global_ctors.
-   For systems which support a .init section we use the .init section
-   to run __do_global_ctors, so we need not do anything here.  */
+short 
+__get_eh_table_language (exception_descriptor *table)
+{
+  return table->lang.language;
+}
 
-void
-SYMBOL__MAIN ()
+/* 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.
+
+   In the advent of a tie, we have to give the last entry, as it represents
+   an inner block.  */
+
+static void *
+old_find_exception_handler (void *pc, old_exception_table *table)
 {
-  /* Support recursive calls to `main': run initializers just once.  */
-  static int initialized;
-  if (! initialized)
+  if (table)
     {
-      initialized = 1;
-      __do_global_ctors ();
+      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;
     }
-}
-#endif /* no INIT_SECTION_ASM_OP or INVOKE__main */
-
-#endif /* L__main */
-\f
-#ifdef L_ctors
 
-#include "gbl-ctors.h"
+  return (void *) 0;
+}
 
-/* Provide default definitions for the lists of constructors and
-   destructors, so that we don't get linker errors.  These symbols are
-   intentionally bss symbols, so that gld and/or collect will provide
-   the right values.  */
+/* find_exception_handler finds the correct handler, if there is one, to
+   handle an exception.
+   returns a pointer to the handler which controlled should be transferred
+   to, or NULL if there is nothing left.
+   Parameters:
+   PC - pc where the exception originates. If this is a rethrow, 
+        then this starts out as a pointer to the exception table
+       entry we wish to rethrow out of.
+   TABLE - exception table for the current module.
+   EH_INFO - eh info pointer for this exception.
+   RETHROW - 1 if this is a rethrow. (see incoming value of PC).
+   CLEANUP - returned flag indicating whether this is a cleanup handler.
+*/
+static void *
+find_exception_handler (void *pc, exception_descriptor *table, 
+                        __eh_info *eh_info, int rethrow, int *cleanup)
+{
 
-/* 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)
-#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};
-#else
-func_ptr __CTOR_LIST__[2];
-func_ptr __DTOR_LIST__[2];
-#endif
-#endif /* no INIT_SECTION_ASM_OP and not CTOR_LISTS_DEFINED_EXTERNALLY */
-#endif /* L_ctors */
+  void *retval = NULL;
+  *cleanup = 1;
+  if (table)
+    {
+      int pos = 0;
+      /* The new model assumed the table is sorted inner-most out so the
+         first region we find which matches is the correct one */
+
+      exception_table *tab = &(table->table[0]);
+
+      /* Subtract 1 from the PC to avoid hitting the next region */
+      if (rethrow) 
+        {
+          /* pc is actually the region table entry to rethrow out of */
+          pos = ((exception_table *) pc) - tab;
+          pc = ((exception_table *) pc)->end_region - 1;
+
+          /* The label is always on the LAST handler entry for a region, 
+             so we know the next entry is a different region, even if the
+             addresses are the same. Make sure its not end of table tho. */
+          if (tab[pos].start_region != (void *) -1)
+            pos++;
+        }
+      else
+        pc--;
+      
+      /* We can't do a binary search because the table is in inner-most
+         to outermost address ranges within functions */
+      for ( ; 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->match_function;
+                  /* match info but no matcher is NOT a match */
+                  if (matcher) 
+                    {
+                      void *ret = (*matcher)((void *) eh_info, 
+                                             tab[pos].match_info, table);
+                      if (ret) 
+                        {
+                          if (retval == NULL)
+                            retval = tab[pos].exception_handler;
+                          *cleanup = 0;
+                          break;
+                        }
+                    }
+                }
+              else
+                {
+                  if (retval == NULL)
+                    retval = tab[pos].exception_handler;
+                }
+            }
+        }
+    }
+  return retval;
+}
+#endif /* DWARF2_UNWIND_INFO */
+#endif /* EH_TABLE_LOOKUP */
 \f
-#ifdef L_exit
+#ifdef DWARF2_UNWIND_INFO
+/* Support code for exception handling using static unwind information.  */
 
-#include "gbl-ctors.h"
+#include "frame.h"
 
-#ifndef ON_EXIT
+/* 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.  */
 
-/* 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.  */
+typedef int ptr_type __attribute__ ((mode (pointer)));
 
-extern void __do_global_dtors ();
-extern void _cleanup ();
-extern void _exit () __attribute__ ((noreturn));
+#ifdef INCOMING_REGNO
+/* Is the saved value for register REG in frame UDATA stored in a register
+   window in the previous frame?  */
 
-void 
-exit (status)
-     int status;
+/* ??? The Sparc INCOMING_REGNO references TARGET_FLAT.  This allows us
+   to use the macro here.  One wonders, though, that perhaps TARGET_FLAT
+   compiled functions won't work with the frame-unwind stuff here.  
+   Perhaps the entireity of in_reg_window should be conditional on having
+   seen a DW_CFA_GNU_window_save?  */
+#define target_flags 0
+
+static int
+in_reg_window (int reg, frame_state *udata)
 {
-#if !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF)
-  __do_global_dtors ();
-#endif
-#ifdef EXIT_BODY
-  EXIT_BODY;
+  if (udata->saved[reg] == REG_SAVED_REG)
+    return INCOMING_REGNO (reg) == reg;
+  if (udata->saved[reg] != REG_SAVED_OFFSET)
+    return 0;
+
+#ifdef STACK_GROWS_DOWNWARD
+  return udata->reg_or_offset[reg] > 0;
 #else
-  _cleanup ();
+  return udata->reg_or_offset[reg] < 0;
 #endif
-  _exit (status);
 }
-
 #else
-int _exit_dummy_decl = 0;      /* prevent compiler & linker warnings */
-#endif
+static inline int in_reg_window (int reg, frame_state *udata) { return 0; }
+#endif /* INCOMING_REGNO */
 
-#endif /* L_exit */
-\f
-#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;
-};
+/* Get the address of register REG as saved in UDATA, where SUB_UDATA is a
+   frame called by UDATA or 0.  */
 
-static int except_table_pos;
-static void *except_pc;
-static struct exception_table_node *exception_table_list;
-
-static exception_table *
-find_exception_table (pc)
-     void* pc;
+static word_type *
+get_reg_addr (unsigned reg, frame_state *udata, frame_state *sub_udata)
 {
-  register struct exception_table_node *table = exception_table_list;
-  for ( ; table != 0; table = table->next)
+  while (udata->saved[reg] == REG_SAVED_REG)
     {
-      if (table->start <= pc && table->end > pc)
-       return table->table;
+      reg = udata->reg_or_offset[reg];
+      if (in_reg_window (reg, udata))
+       {
+          udata = sub_udata;
+         sub_udata = NULL;
+       }
     }
-  return 0;
+  if (udata->saved[reg] == REG_SAVED_OFFSET)
+    return (word_type *)(udata->cfa + udata->reg_or_offset[reg]);
+  else
+    abort ();
 }
 
-/* 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:
+/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a
+   frame called by UDATA or 0.  */
 
-    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 inline void *
+get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata)
+{
+  return (void *)(ptr_type) *get_reg_addr (reg, udata, sub_udata);
+}
 
-   Assuming a correctly sorted table (ascending order) this routine should
-   return the tightest match...
+/* Overwrite the saved value for register REG in frame UDATA with VAL.  */
 
-   In the advent of a tie, we have to give the last entry, as it represents
-   an inner block.
- */
+static inline void
+put_reg (unsigned reg, void *val, frame_state *udata)
+{
+  *get_reg_addr (reg, udata, NULL) = (word_type)(ptr_type) val;
+}
 
+/* 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.  */
 
-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);
-#endif
+static void
+copy_reg (unsigned reg, frame_state *udata, frame_state *target_udata)
+{
+  word_type *preg = get_reg_addr (reg, udata, NULL);
+  word_type *ptreg = get_reg_addr (reg, target_udata, NULL);
 
-  except_pc = pc;
+  memcpy (ptreg, preg, __builtin_dwarf_reg_size (reg));
+}
 
-#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)
-    {
-      except_table_pos = pos;
-#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 <= 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;
-#endif
+/* Retrieve the return address for frame UDATA.  */
 
-#if 0
-  printf("find_first_eh_table_match(): else: returning NULL!\n");
-#endif
-  return (void*)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));
 }
 
-void *
-__throw_type_match (void *catch_type, void *throw_type, void* obj)
+/* Overwrite the return address for frame UDATA with VAL.  */
+
+static inline void
+put_return_addr (void *val, frame_state *udata)
 {
-#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;
+  val = __builtin_frob_return_addr (val);
+  put_reg (udata->retaddr_column, val, udata);
 }
 
-void
-__register_exceptions (exception_table *table)
+/* 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)
 {
-  struct exception_table_node *node;
-  exception_table *range = table + 1;
+  caller_udata = __frame_state_for (pc, caller_udata);
+  if (! caller_udata)
+    return 0;
 
-  if (range->start == (void*)-1)
-    return;
+  /* 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;
+}
+
+/* Hook to call before __terminate if only cleanup handlers remain. */
+void 
+__unwinding_cleanup ()
+{
+}
+
+/* throw_helper performs some of the common grunt work for a throw. This
+   routine is called by throw and rethrows. This is pretty much split 
+   out from the old __throw routine. An addition has been added which allows
+   for a dummy call to a routine __unwinding_cleanup() when there are nothing
+   but cleanups remaining. This allows a debugger to examine the state
+   at which the throw was executed, before any cleanups, rather than
+   at the terminate point after the stack has been unwound. */
+
+static void *
+throw_helper (eh, pc, my_udata, udata_p)
+     struct eh_context *eh;
+     void *pc;
+     frame_state *my_udata;
+     frame_state **udata_p;
+{
+  frame_state *udata = *udata_p;
+  frame_state ustruct;
+  frame_state *sub_udata = &ustruct;
+  void *saved_pc = pc;
+  void *handler;
+  void *handler_p;
+  void *pc_p;
+  frame_state saved_ustruct;
+  int new_eh_model;
+  int cleanup = 0;
+  int only_cleanup = 0;
+  int rethrow = 0;
+  int saved_state = 0;
+  __eh_info *eh_info = (__eh_info *)eh->info;
+
+  /* Do we find a handler based on a re-throw PC? */
+  if (eh->table_index != (void *) 0)
+    rethrow = 1;
+
+  handler = (void *) 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_eh_model = 0;
+      else
+        new_eh_model = (((exception_descriptor *)(udata->eh_ptr))->
+                                          runtime_id_field == NEW_EH_RUNTIME);
+
+      if (rethrow) 
+        {
+          rethrow = 0;
+          handler = find_exception_handler (eh->table_index, udata->eh_ptr, 
+                                          eh_info, 1, &cleanup);
+          eh->table_index = (void *)0;
+        }
+      else
+        if (new_eh_model)
+          handler = find_exception_handler (pc, udata->eh_ptr, eh_info, 
+                                            0, &cleanup);
+        else
+          handler = old_find_exception_handler (pc, udata->eh_ptr);
+
+      /* If we found one, we can stop searching, if its not a cleanup. 
+         for cleanups, we save the state, and keep looking. This allows
+         us to call a debug hook if there are nothing but cleanups left. */
+      if (handler)
+       if (cleanup)
+         {
+           if (!saved_state)
+             {
+               saved_ustruct = *udata;
+               handler_p = handler;
+               pc_p = pc;
+               saved_state = 1;
+               only_cleanup = 1;
+             }
+         }
+       else
+         {
+           only_cleanup = 0;
+           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;
+    }
 
-  node = (struct exception_table_node*)
-    malloc (sizeof (struct exception_table_node));
-  node->table = table;
+  if (saved_state) 
+    {
+      udata = &saved_ustruct;
+      handler = handler_p;
+      pc = pc_p;
+      if (only_cleanup)
+        __unwinding_cleanup ();
+    }
+
+  /* If we haven't found a handler by now, this is an unhandled
+     exception.  */
+  if (! handler) 
+    __terminate();
+
+  eh->handler_label = handler;
 
-  /* 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 (pc == saved_pc)
+    /* We found a handler in the throw context, no need to unwind.  */
+    udata = my_udata;
+  else
     {
-      if (range->start < node->start)
-       node->start = range->start;
-      if (range->end > node->end)
-       node->end = range->end;
+      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])
+             {
+               /* 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;
+               copy_reg (i, udata, my_udata);
+             }
+
+         pc = get_return_addr (udata, sub_udata) - 1;
+       }
+
+      /* 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);
+       }
     }
+  /* udata now refers to the frame called by the handler frame.  */
 
-  node->next = exception_table_list;
-  exception_table_list = node;
+  *udata_p = udata;
+  return handler;
 }
 
-#if #machine(i386)
+
+/* 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.  */
+
+/*extern void __throw(void) __attribute__ ((__noreturn__));*/
+
 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;
-}
+__throw ()
+{
+  struct eh_context *eh = (*get_eh_context) ();
+  void *pc, *handler;
+  frame_state ustruct;
+  frame_state *udata = &ustruct;
+  frame_state my_ustruct, *my_udata = &my_ustruct;
+
+  /* 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. */
+  udata->cfa = __builtin_dwarf_cfa ();
+
+  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;
+
+  handler = throw_helper (eh, pc, my_udata, &udata);
+
+  /* Now go!  */
+
+  __builtin_eh_return ((void *)eh,
+#ifdef STACK_GROWS_DOWNWARD
+                      udata->cfa - my_udata->cfa,
 #else
-__unwind_function(void *ptr)
+                      my_udata->cfa - udata->cfa,
+#endif
+                      handler);
+
+  /* Epilogue:  restore the handler frame's register values and return
+     to the stub.  */
+}
+
+/*extern void __rethrow(void *) __attribute__ ((__noreturn__));*/
+
+void
+__rethrow (index)
+     void *index;
 {
-  abort ();
+  struct eh_context *eh = (*get_eh_context) ();
+  void *pc, *handler;
+  frame_state ustruct;
+  frame_state *udata = &ustruct;
+  frame_state my_ustruct, *my_udata = &my_ustruct;
+
+  /* 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 ();
+
+  /* This is the table index we want to rethrow from. The value of
+     the END_REGION label is used for the PC of the throw, and the
+     search begins with the next table entry. */
+  eh->table_index = index;
+    
+  /* 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. */
+  udata->cfa = __builtin_dwarf_cfa ();
+
+  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;
+
+  handler = throw_helper (eh, pc, my_udata, &udata);
+
+  /* Now go!  */
+
+  __builtin_eh_return ((void *)eh,
+#ifdef STACK_GROWS_DOWNWARD
+                      udata->cfa - my_udata->cfa,
+#else
+                      my_udata->cfa - udata->cfa,
+#endif
+                      handler);
+
+  /* Epilogue:  restore the handler frame's register values and return
+     to the stub.  */
 }
-#endif /* powerpc */
+#endif /* DWARF2_UNWIND_INFO */
+
 #endif /* L_eh */
 \f
 #ifdef L_pure
@@ -2435,6 +3990,6 @@ __pure_virtual ()
 #ifndef inhibit_libc
   write (2, MESSAGE, sizeof (MESSAGE) - 1);
 #endif
-  _exit (-1);
+  __terminate ();
 }
 #endif