OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / libgcc2.c
index dd30b71..b40fb58 100644 (file)
@@ -10,6 +10,15 @@ it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
 GNU CC is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -20,13 +29,6 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-/* As a special exception, if you link this library with other files,
-   some of which are compiled with GCC, to produce an executable,
-   this library does not by itself cause the resulting executable
-   to be covered by the GNU General Public License.
-   This exception does not however invalidate any other reasons why
-   the executable file might be covered by the GNU General Public License.  */
-
 /* It is incorrect to include config.h here, because this file is being
    compiled for the target, and hence definitions concerning only the host
    do not apply.  */
@@ -42,13 +44,6 @@ Boston, MA 02111-1307, USA.  */
 #undef abort
 #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
-
 #include "libgcc2.h"
 \f
 #if defined (L_negdi2) || defined (L_divdi3) || defined (L_moddi3)
@@ -3193,7 +3188,76 @@ eh_context_specific (void)
 
   return eh;
 }
-#endif __GTHREADS
+#endif /* __GTHREADS */
+\f
+/* Support routines for alloc/free during exception handling */
+
+/* __eh_alloc and __eh_free attempt allocation using malloc, but fall back to
+   the small arena in the eh_context. This is needed because throwing an
+   out-of-memory exception would fail otherwise. The emergency space is
+   allocated in blocks of size EH_ALLOC_ALIGN, the
+   minimum allocation being two blocks. A bitmask indicates which blocks
+   have been allocated. To indicate the size of an allocation, the bit for
+   the final block is not set. Hence each allocation is a run of 1s followed
+   by a zero. */
+void *
+__eh_alloc (size_t size)
+{
+  void *p;
+  
+  if (!size)
+    abort();
+  p = malloc (size);
+  if (p == 0)
+    {
+      struct eh_context *eh = __get_eh_context ();
+      unsigned blocks = (size + EH_ALLOC_ALIGN - 1) / EH_ALLOC_ALIGN;
+      unsigned real_mask = eh->alloc_mask | (eh->alloc_mask << 1);
+      unsigned our_mask;
+      unsigned ix;
+      
+      if (blocks > EH_ALLOC_SIZE / EH_ALLOC_ALIGN)
+        __terminate ();
+      blocks += blocks == 1;
+      our_mask = (1 << blocks) - 1;
+      
+      for (ix = EH_ALLOC_SIZE / EH_ALLOC_ALIGN - blocks; ix; ix--)
+       if (! ((real_mask >> ix) & our_mask))
+         {
+           /* found some space */
+           p = &eh->alloc_buffer[ix * EH_ALLOC_ALIGN];
+           eh->alloc_mask |= (our_mask >> 1) << ix;
+           return p;
+         }
+      __terminate ();
+    }
+  return p;
+}
+
+/* Free the memory for an cp_eh_info and associated exception, given
+   a pointer to the cp_eh_info.  */
+void
+__eh_free (void *p)
+{
+  struct eh_context *eh = __get_eh_context ();
+
+  ptrdiff_t  diff = (char *)p - &eh->alloc_buffer[0];
+  if (diff >= 0 && diff < EH_ALLOC_SIZE)
+    {
+      unsigned mask = eh->alloc_mask;
+      unsigned bit = 1 << (diff / EH_ALLOC_ALIGN);
+      
+      do
+       {
+         mask ^= bit;
+         bit <<= 1;
+       }
+      while (mask & bit);
+      eh->alloc_mask = mask;
+    }
+  else
+    free (p);
+}
 \f
 /* Support routines for setjmp/longjmp exception handling.  */
 
@@ -3647,6 +3711,9 @@ next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata)
     caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0);
   else
     caller_udata->cfa = udata->cfa;
+  if (caller_udata->indirect)
+    caller_udata->cfa = *(word_type *)((unsigned char *)caller_udata->cfa 
+                                 + caller_udata->base_offset);
   caller_udata->cfa += caller_udata->cfa_offset;
 
   return caller_udata;
@@ -3945,6 +4012,172 @@ label:
 }
 #endif /* DWARF2_UNWIND_INFO */
 
+#ifdef IA64_UNWIND_INFO
+#include "frame.h"
+
+/* Return handler to which we want to transfer control, NULL if we don't
+   intend to handle this exception here.  */
+void *
+__ia64_personality_v1 (void *pc, old_exception_table *table)
+{
+  if (table)
+    {
+      int pos;
+      int best = -1;
+
+      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;
+            }
+          /* 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;
+    }
+  return (void *) 0;
+}
+
+static void
+ia64_throw_helper (throw_frame, caller, throw_bsp)
+     ia64_frame_state *throw_frame;
+     ia64_frame_state *caller;
+     void *throw_bsp;
+{
+  void *throw_pc = __builtin_return_address (0);
+  unwind_info_ptr *info;
+  void *pc, *handler = NULL;
+  void *pc_base;
+  int frame_count;
+  void *bsp;
+
+  __builtin_ia64_flushrs ();      /*  Make the local register stacks available.  */
+
+  /* Start at our stack frame, get our state.  */
+  __build_ia64_frame_state (throw_pc, throw_frame, throw_bsp, &pc_base);
+
+  /* Now we have to find the proper frame for pc, and see if there
+     is a handler for it. if not, we keep going back frames until
+     we do find one. Otherwise we call uncaught ().  */
+
+  frame_count = 0;
+  memcpy (caller, throw_frame, sizeof (*caller));
+  while (!handler)
+    {
+      void *(*personality) ();
+      void *eh_table;
+
+      frame_count++;
+      /* We only care about the RP right now, so we dont need to keep
+         any other information about a call frame right now.  */
+      pc = __get_real_reg_value (&caller->rp) - 1;
+      bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), caller->my_bsp);
+      info = __build_ia64_frame_state (pc, caller, bsp, &pc_base);
+
+      /* If we couldn't find the next frame, we lose.  */
+      if (! info)
+       break;
+
+      personality = __get_personality (info); 
+      /* TODO Haven't figured out how to actually load the personality address
+         yet, so just always default to the one we expect for now.  */
+      if (personality != 0)
+       personality = __ia64_personality_v1;
+      eh_table = __get_except_table (info);
+      /* If there is no personality routine, we'll keep unwinding.  */
+      if (personality)
+       /* Pass a segment relative PC address to the personality routine,
+          because the unwind_info section uses segrel relocs.  */
+       handler = personality (pc - pc_base, eh_table);
+    }
+  
+  if (!handler)
+   __terminate ();
+
+  /* Handler is a segment relative address, so we must adjust it here.  */
+  handler += (long) pc_base;
+
+  /* If we found a handler, we need to unwind the stack to that point.
+     We do this by copying saved values from previous frames into the
+     save slot for the throw_frame saved slots.  when __throw returns,
+     it'll pickup the correct values.  */
+  
+  /* Start with where __throw saved things, and copy each saved register
+     of each previous frame until we get to the one before we're 
+     throwing back to.  */
+  memcpy (caller, throw_frame, sizeof (*caller));
+  for ( ; frame_count > 0; frame_count--)
+    {
+      pc = __get_real_reg_value (&caller->rp) - 1;
+      bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), caller->my_bsp);
+      __build_ia64_frame_state (pc, caller, bsp, &pc_base);
+      /* Any regs that were saved can be put in the throw frame now.  */
+      /* We don't want to copy any saved register from the 
+         target destination, but we do want to load up it's frame.  */
+      if (frame_count > 1)
+       __copy_saved_reg_state (throw_frame, caller);
+    }
+
+  /* Set return address of the throw frame to the handler. */
+  __set_real_reg_value (&throw_frame->rp, handler);
+
+  /* TODO, do we need to do anything to make the values we wrote 'stick'? */
+  /* DO we need to go through the whole loadrs seqeunce?  */
+
+}
+
+void
+__throw ()
+{
+  struct eh_context *eh = (*get_eh_context) ();
+  ia64_frame_state my_frame;
+  ia64_frame_state originator; /* For the context handler is in.  */
+  void *bsp, *tmp_bsp;
+  long offset;
+
+  /* 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 ();
+
+  __builtin_unwind_init ();
+  /* We have to call another routine to actually process the frame 
+     information, which will force all of __throw's local registers into
+     backing store.  */
+
+  /* Get the value of ar.bsp while we're here.  */
+
+  bsp = __builtin_ia64_bsp ();
+  ia64_throw_helper (&my_frame, &originator, bsp);
+
+  /* Now we have to fudge the bsp by the amount in our (__throw)
+     frame marker, since the return is going to adjust it by that much. */
+
+  tmp_bsp = __calc_caller_bsp ((long)__get_real_reg_value (&my_frame.pfs), 
+                            my_frame.my_bsp);
+  offset = (char *)my_frame.my_bsp - (char *)tmp_bsp;
+  tmp_bsp = (char *)originator.my_bsp + offset;
+
+  /* A throw handler is trated like a  non-local goto, which is architeched
+     to set the FP (or PSP) in r7 before branching.  gr[0-3] map to 
+     r4-r7, so we want gr[3].  */
+  __set_real_reg_value (&my_frame.gr[3], __get_real_reg_value (&originator.psp));
+
+  __builtin_eh_return (tmp_bsp, offset, originator.my_sp);
+
+  /* The return address was already set by throw_helper.  */
+}
+
+#endif /* IA64_UNWIND_INFO  */
+
 #endif /* L_eh */
 \f
 #ifdef L_pure