OSDN Git Service

* except.c (expand_eh_region_start_tree): Add DECL argument so we
[pf3gnuchains/gcc-fork.git] / gcc / except.c
index 48b635b..71925a5 100644 (file)
@@ -1,4 +1,4 @@
-/* Implements exceptiom handling.
+/* Implements exception handling.
    Copyright (C) 1989, 92-95, 1996 Free Software Foundation, Inc.
    Contributed by Mike Stump <mrs@cygnus.com>.
 
@@ -20,81 +20,337 @@ the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
 
-/* This file contains the exception handling code for the compiler.
-
-   Exception handling is a mechanism by which complex flows of control
-   can be designated.  The central concepts are the `exception region',
-   the associated `exception handler' for that region and the concept of
-   throwing an exception, and the context of the throw.
-
-   Restrictions are, the regions must be non-overlapping, they can be
-   nested, and there can be zero or more per function.  For each
-   region, there is one associated handler.  Regions only ever
-   surround possible context points of throws.  Regions with no such
-   context points can be optimized away, as they are trivial, and it
-   is not possible for the associated handler to ever be used during a
-   throw.
-
-   Semantics are, when an exception is thrown, control is transferred
-   to a handler, and the code of the exception handler is executed.
-   As control is transferred, the machine state (stack pointer, all
-   callee saved registers and possibly the frame pointer) is restored.
-
-   The handler that is selected by a throw, is the handler associated
-   with the smallest (most nested) region that contains the context of
-   the throw, if such a region exists.  If no region exists, the
-   search for a handler continues in the function that called the
-   function that contains the current context of the throw, with the
-   context of the throw then becoming that point in the code that
-   contains the call instruction.
-
-
-   One can add to the basic model the concepts of thrown exception
-   type, and thrown exception value.  Semantics are as above, except a
-   further check is done when finding a handler for the thrown
-   exception to see if the given handler can handle the thrown
-   exception based upon the exception object's type and possibly its
-   value.  A common optimization is when two regions are identical,
-   the handlers are combined into just one handler so the first check
-   of the resulting handler is for the inner (nested) region's
-   handler, and the second one is for the outer region's handler.  To
-   separate these two notions of handlers, we can call the subhandlers
-   `catch blocks', and use the name `handler' to refer to the
-   combination of the two.  Currently, this layer of functionality is
-   managed by the various front ends.
-
-
-   To mark the start of a exception handling region,
-   expand_eh_region_start () is called.  To mark the end, and
-   associate a handler for the region expand_eh_region_end () is used.
-   The front end can use this interface, if useful.  The back end
-   creates exception regions with these routines.  Another interface
-   the front end can use, is TARGET_EXPR.  TARGET_EXPR gives an
-   unwind-protect style interface a la emacs.
-
-
-   In this implementation, regions do not span more than one function.
-
-   In order to help with the task of finding the associated handler for
-   a region, an exception table is built which associates handlers
-   with regions.  A 3-tuple, containing a reference to the start, the
-   end and the handler is sufficient for the exception table.
-
-   In order to help with the task of restoring callee saved registers
-   and performing other associated function exit actions, function
-   `unwinders' can be generated within those function for which a
-   generic function unwinder called __unwind_function () cannot work.
-   Whether the generic __unwind_function can work is machine dependent
-   and possibly function dependent.  The macro DOESNT_NEEED_UNWINDER
-   decides if the current function being compiled needs an unwinder or
-   not.
-
-   The default is for unwinders to be used, as the default generic
-   function unwinder only calls abort ().  The compiler-generated per
-   function function unwinders simply modify the context of thrown
-   exception to be that of the call site, and then arrange for control
-   to be transferred to __throw instead of the function's caller on
-   return, and then return.  */
+/* An exception is an event that can be signaled from within a
+   function. This event can then be "caught" or "trapped" by the
+   callers of this function. This potentially allows program flow to
+   be transferred to any arbitrary code assocated with a function call
+   several levels up the stack.
+
+   The intended use for this mechanism is for signaling "exceptional
+   events" in an out-of-band fashion, hence its name. The C++ language
+   (and many other OO-styled or functional languages) practically
+   requires such a mechanism, as otherwise it becomes very difficult
+   or even impossible to signal failure conditions in complex
+   situations.  The traditional C++ example is when an error occurs in
+   the process of constructing an object; without such a mechanism, it
+   is impossible to signal that the error occurs without adding global
+   state variables and error checks around every object construction.
+
+   The act of causing this event to occur is referred to as "throwing
+   an exception". (Alternate terms include "raising an exception" or
+   "signaling an exception".) The term "throw" is used because control
+   is returned to the callers of the function that is signaling the
+   exception, and thus there is the concept of "throwing" the
+   exception up the call stack.
+
+   There are two major codegen options for exception handling.  The
+   flag -fsjlj-exceptions can be used to select the setjmp/longjmp
+   approach, which is the default.  -fnosjlj-exceptions can be used to
+   get the PC range table approach.  While this is a compile time
+   flag, an entire application must be compiled with the same codegen
+   option.  The first is a PC range table approach, the second is a
+   setjmp/longjmp based scheme.  We will first discuss the PC range
+   table approach, after that, we will discuss the setjmp/longjmp
+   based approach.
+
+   It is appropriate to speak of the "context of a throw". This
+   context refers to the address where the exception is thrown from,
+   and is used to determine which exception region will handle the
+   exception.
+
+   Regions of code within a function can be marked such that if it
+   contains the context of a throw, control will be passed to a
+   designated "exception handler". These areas are known as "exception
+   regions".  Exception regions cannot overlap, but they can be nested
+   to any arbitrary depth. Also, exception regions cannot cross
+   function boundaries.
+
+   Exception handlers can either be specified by the user (which we
+   will call a "user-defined handler") or generated by the compiler
+   (which we will designate as a "cleanup"). Cleanups are used to
+   perform tasks such as destruction of objects allocated on the
+   stack.
+
+   In the current implementaion, cleanups are handled by allocating an
+   exception region for the area that the cleanup is designated for,
+   and the handler for the region performs the cleanup and then
+   rethrows the exception to the outer exception region. From the
+   standpoint of the current implementation, there is little
+   distinction made between a cleanup and a user-defined handler, and
+   the phrase "exception handler" can be used to refer to either one
+   equally well. (The section "Future Directions" below discusses how
+   this will change).
+
+   Each object file that is compiled with exception handling contains
+   a static array of exception handlers named __EXCEPTION_TABLE__.
+   Each entry contains the starting and ending addresses of the
+   exception region, and the address of the handler designated for
+   that region.
+
+   At program startup each object file invokes a function named
+   __register_exceptions with the address of its local
+   __EXCEPTION_TABLE__. __register_exceptions is defined in libgcc2.c,
+   and is responsible for recording all of the exception regions into
+   one list (which is kept in a static variable named exception_table_list).
+
+   The function __throw is actually responsible for doing the
+   throw. In the C++ frontend, __throw is generated on a
+   per-object-file basis for each source file compiled with
+   -fexceptions. Before __throw is invoked, the current context
+   of the throw needs to be placed in the global variable __eh_pc.
+
+   __throw attempts to find the appropriate exception handler for the 
+   PC value stored in __eh_pc by calling __find_first_exception_table_match
+   (which is defined in libgcc2.c). If __find_first_exception_table_match
+   finds a relevant handler, __throw jumps directly to it.
+
+   If a handler for the context being thrown from can't be found,
+   __throw is responsible for unwinding the stack, determining the
+   address of the caller of the current function (which will be used
+   as the new context to throw from), and then restarting the process
+   of searching for a handler for the new context. __throw may also
+   call abort if it is unable to unwind the stack, and can also
+   call an external library function named __terminate if it reaches
+   the top of the stack without finding an appropriate handler. (By
+   default __terminate invokes abort, but this behavior can be
+   changed by the user to perform some sort of cleanup behavior before
+   exiting).
+
+   Internal implementation details:
+
+   To associate a user-defined handler with a block of statements, the
+   function expand_start_try_stmts is used to mark the start of the
+   block of statements with which the handler is to be associated
+   (which is known as a "try block"). All statements that appear
+   afterwards will be associated with the try block.
+
+   A call to expand_start_all_catch marks the end of the try block,
+   and also marks the start of the "catch block" (the user-defined
+   handler) associated with the try block.
+
+   This user-defined handler will be invoked for *every* exception
+   thrown with the context of the try block. It is up to the handler
+   to decide whether or not it wishes to handle any given exception,
+   as there is currently no mechanism in this implementation for doing
+   this. (There are plans for conditionally processing an exception
+   based on its "type", which will provide a language-independent
+   mechanism).
+
+   If the handler chooses not to process the exception (perhaps by
+   looking at an "exception type" or some other additional data
+   supplied with the exception), it can fall through to the end of the
+   handler. expand_end_all_catch and expand_leftover_cleanups
+   add additional code to the end of each handler to take care of
+   rethrowing to the outer exception handler.
+
+   The handler also has the option to continue with "normal flow of
+   code", or in other words to resume executing at the statement
+   immediately after the end of the exception region. The variable
+   caught_return_label_stack contains a stack of labels, and jumping
+   to the topmost entry's label via expand_goto will resume normal
+   flow to the statement immediately after the end of the exception
+   region. If the handler falls through to the end, the exception will
+   be rethrown to the outer exception region.
+
+   The instructions for the catch block are kept as a separate
+   sequence, and will be emitted at the end of the function along with
+   the handlers specified via expand_eh_region_end. The end of the
+   catch block is marked with expand_end_all_catch.
+
+   Any data associated with the exception must currently be handled by
+   some external mechanism maintained in the frontend.  For example,
+   the C++ exception mechanism passes an arbitrary value along with
+   the exception, and this is handled in the C++ frontend by using a
+   global variable to hold the value. (This will be changing in the
+   future.)
+
+   The mechanism in C++ for handling data associated with the
+   exception is clearly not thread-safe. For a thread-based
+   environment, another mechanism must be used (possibly using a
+   per-thread allocation mechanism if the size of the area that needs
+   to be allocated isn't known at compile time.)
+
+   Internally-generated exception regions (cleanups) are marked by
+   calling expand_eh_region_start to mark the start of the region,
+   and expand_eh_region_end (handler) is used to both designate the
+   end of the region and to associate a specified handler/cleanup with
+   the region. The rtl code in HANDLER will be invoked whenever an
+   exception occurs in the region between the calls to
+   expand_eh_region_start and expand_eh_region_end. After HANDLER is
+   executed, additional code is emitted to handle rethrowing the
+   exception to the outer exception handler. The code for HANDLER will
+   be emitted at the end of the function.
+
+   TARGET_EXPRs can also be used to designate exception regions. A
+   TARGET_EXPR gives an unwind-protect style interface commonly used
+   in functional languages such as LISP. The associated expression is
+   evaluated, and whether or not it (or any of the functions that it
+   calls) throws an exception, the protect expression is always
+   invoked. This implementation takes care of the details of
+   associating an exception table entry with the expression and
+   generating the necessary code (it actually emits the protect
+   expression twice, once for normal flow and once for the exception
+   case). As for the other handlers, the code for the exception case
+   will be emitted at the end of the function.
+
+   Cleanups can also be specified by using add_partial_entry (handler)
+   and end_protect_partials. add_partial_entry creates the start of
+   a new exception region; HANDLER will be invoked if an exception is
+   thrown with the context of the region between the calls to
+   add_partial_entry and end_protect_partials. end_protect_partials is
+   used to mark the end of these regions. add_partial_entry can be
+   called as many times as needed before calling end_protect_partials.
+   However, end_protect_partials should only be invoked once for each
+   group of calls to add_partial_entry as the entries are queued
+   and all of the outstanding entries are processed simultaneously
+   when end_protect_partials is invoked. Similarly to the other
+   handlers, the code for HANDLER will be emitted at the end of the
+   function.
+
+   The generated RTL for an exception region includes
+   NOTE_INSN_EH_REGION_BEG and NOTE_INSN_EH_REGION_END notes that mark
+   the start and end of the exception region. A unique label is also
+   generated at the start of the exception region, which is available
+   by looking at the ehstack variable. The topmost entry corresponds
+   to the current region.
+
+   In the current implementation, an exception can only be thrown from
+   a function call (since the mechanism used to actually throw an
+   exception involves calling __throw).  If an exception region is
+   created but no function calls occur within that region, the region
+   can be safely optimized away (along with its exception handlers)
+   since no exceptions can ever be caught in that region.  This
+   optimization is performed unless -fasynchronous-exceptions is
+   given.  If the user wishes to throw from a signal handler, or other
+   asynchronous place, -fasynchronous-exceptions should be used when
+   compiling for maximally correct code, at the cost of additional
+   exception regions.  Using -fasynchronous-exceptions only produces
+   code that is reasonably safe in such situations, but a correct
+   program cannot rely upon this working.  It can be used in failsafe
+   code, where trying to continue on, and proceeding with potentially
+   incorrect results is better than halting the program.
+
+
+   Unwinding the stack:
+
+   The details of unwinding the stack to the next frame can be rather
+   complex. While in many cases a generic __unwind_function routine
+   can be used by the generated exception handling code to do this, it
+   is often necessary to generate inline code to do the unwinding.
+
+   Whether or not these inlined unwinders are necessary is
+   target-specific.
+
+   By default, if the target-specific backend doesn't supply a
+   definition for __unwind_function, inlined unwinders will be used
+   instead. The main tradeoff here is in text space utilization.
+   Obviously, if inline unwinders have to be generated repeatedly,
+   this uses much more space than if a single routine is used.
+
+   However, it is simply not possible on some platforms to write a
+   generalized routine for doing stack unwinding without having some
+   form of additional data associated with each function. The current
+   implementation encodes this data in the form of additional machine
+   instructions. This is clearly not desirable, as it is extremely
+   inefficient. The next implementation will provide a set of metadata
+   for each function that will provide the needed information.
+
+   The backend macro DOESNT_NEED_UNWINDER is used to conditionalize
+   whether or not per-function unwinders are needed. If DOESNT_NEED_UNWINDER
+   is defined and has a non-zero value, a per-function unwinder is
+   not emitted for the current function.
+
+   On some platforms it is possible that neither __unwind_function
+   nor inlined unwinders are available. For these platforms it is not
+   possible to throw through a function call, and abort will be
+   invoked instead of performing the throw. 
+
+   Future directions:
+
+   Currently __throw makes no differentiation between cleanups and
+   user-defined exception regions. While this makes the implementation
+   simple, it also implies that it is impossible to determine if a
+   user-defined exception handler exists for a given exception without
+   completely unwinding the stack in the process. This is undesirable
+   from the standpoint of debugging, as ideally it would be possible
+   to trap unhandled exceptions in the debugger before the process of
+   unwinding has even started.
+
+   This problem can be solved by marking user-defined handlers in a
+   special way (probably by adding additional bits to exception_table_list).
+   A two-pass scheme could then be used by __throw to iterate
+   through the table. The first pass would search for a relevant
+   user-defined handler for the current context of the throw, and if
+   one is found, the second pass would then invoke all needed cleanups
+   before jumping to the user-defined handler.
+
+   Many languages (including C++ and Ada) make execution of a
+   user-defined handler conditional on the "type" of the exception
+   thrown. (The type of the exception is actually the type of the data
+   that is thrown with the exception.) It will thus be necessary for
+   __throw to be able to determine if a given user-defined
+   exception handler will actually be executed, given the type of
+   exception.
+
+   One scheme is to add additional information to exception_table_list
+   as to the types of exceptions accepted by each handler. __throw
+   can do the type comparisons and then determine if the handler is
+   actually going to be executed.
+
+   There is currently no significant level of debugging support
+   available, other than to place a breakpoint on __throw. While
+   this is sufficient in most cases, it would be helpful to be able to
+   know where a given exception was going to be thrown to before it is
+   actually thrown, and to be able to choose between stopping before
+   every exception region (including cleanups), or just user-defined
+   exception regions. This should be possible to do in the two-pass
+   scheme by adding additional labels to __throw for appropriate
+   breakpoints, and additional debugger commands could be added to
+   query various state variables to determine what actions are to be
+   performed next.
+
+   Another major problem that is being worked on is the issue with
+   stack unwinding on various platforms. Currently the only platform
+   that has support for __unwind_function is the Sparc; all other
+   ports require per-function unwinders, which causes large amounts of
+   code bloat.
+
+   Ideally it would be possible to store a small set of metadata with
+   each function that would then make it possible to write a
+   __unwind_function for every platform. This would eliminate the
+   need for per-function unwinders.
+
+   The main reason the data is needed is that on some platforms the
+   order and types of data stored on the stack can vary depending on
+   the type of function, its arguments and returned values, and the
+   compilation options used (optimization versus non-optimization,
+   -fomit-frame-pointer, processor variations, etc).
+
+   Unfortunately, this also means that throwing through functions that
+   aren't compiled with exception handling support will still not be
+   possible on some platforms. This problem is currently being
+   investigated, but no solutions have been found that do not imply
+   some unacceptable performance penalties.
+
+   For setjmp/longjmp based exception handling, some of the details
+   are as above, but there are some additional details.  This section
+   discusses the details.
+
+   We don't use NOTE_INSN_EH_REGION_{BEG,END} pairs.  We don't
+   optimize EH regions yet.  We don't have to worry about machine
+   specific issues with unwinding the stack, as we rely upon longjmp
+   for all the machine specific details.  There is no variable context
+   of a throw, just the one implied by the dynamic handler stack
+   pointed to by the dynamic handler chain.  There is no exception
+   table, and no calls to __register_excetpions.  __sjthrow is used
+   instead of __throw, and it works by using the dynamic handler
+   chain, and longjmp.  -fasynchronous-exceptions has no effect, as
+   the elimination of trivial exception regions is not yet performed.
+
+   A frontend can set protect_cleanup_actions_with_terminate when all
+   the cleanup actions should be protected with an EH region that
+   calls terminate when an unhandled exception is throw.  C++ does
+   this, Ada does not.  */
 
 
 #include "config.h"
@@ -112,56 +368,93 @@ Boston, MA 02111-1307, USA.  */
 #include "insn-config.h"
 #include "recog.h"
 #include "output.h"
+#include "assert.h"
 
-/* List of labels use for exception handlers.  Created by
+/* One to use setjmp/longjmp method of generating code for exception
+   handling.  */
+
+int exceptions_via_longjmp = 1;
+
+/* One to enable asynchronous exception support.  */
+
+int asynchronous_exceptions = 0;
+
+/* One to protect cleanup actions with a handler that calls
+   __terminate, zero otherwise.  */
+
+int protect_cleanup_actions_with_terminate = 0;
+
+/* A list of labels used for exception handlers.  Created by
    find_exception_handler_labels for the optimization passes.  */
 
 rtx exception_handler_labels;
 
-/* Nonzero means that throw was used.  Used for now, because __throw
-   is emitted statically in each file.  */
+/* Nonzero means that __throw was invoked. 
+
+   This is used by the C++ frontend to know if code needs to be emitted
+   for __throw or not.  */
 
 int throw_used;
 
+/* The dynamic handler chain.  Nonzero if the function has already
+   fetched a pointer to the dynamic handler chain for exception
+   handling.  */
+
+rtx current_function_dhc;
+
+/* The dynamic cleanup chain.  Nonzero if the function has already
+   fetched a pointer to the dynamic cleanup chain for exception
+   handling.  */
+
+rtx current_function_dcc;
+
 /* A stack used for keeping track of the currectly active exception
-   handling region.  As exceptions regions are started, an entry
+   handling region.  As each exception region is started, an entry
    describing the region is pushed onto this stack.  The current
    region can be found by looking at the top of the stack, and as we
-   end regions, entries are poped.  */
+   exit regions, the corresponding entries are popped. 
+
+   Entries cannot overlap; they can be nested. So there is only one
+   entry at most that corresponds to the current instruction, and that
+   is the entry on the top of the stack.  */
 
-struct eh_stack ehstack;
+static struct eh_stack ehstack;
 
-/* A queue used for tracking which exception regions have closed, but
-   whose handlers have not yet been expanded.  As we end regions, we
-   enqueue the entry onto this queue.  Entries are dequeue from the
-   queue during expand_leftover_cleanups and expand_start_all_catch,
-   and the handlers for regions are expanded in groups in an effort to
-   group all the handlers together in the same region of program space
-   to improve page performance.  We should redo things, so that we
-   either take RTL for the handler, or we expand the handler expressed
-   as a tree immediately at region end time.  */
+/* A queue used for tracking which exception regions have closed but
+   whose handlers have not yet been expanded. Regions are emitted in
+   groups in an attempt to improve paging performance.
 
-struct eh_queue ehqueue;
+   As we exit a region, we enqueue a new entry. The entries are then
+   dequeued during expand_leftover_cleanups and expand_start_all_catch,
 
-/* Insns for the catch clauses.  */
+   We should redo things so that we either take RTL for the handler,
+   or we expand the handler expressed as a tree immediately at region
+   end time.  */
+
+static struct eh_queue ehqueue;
+
+/* Insns for all of the exception handlers for the current function.
+   They are currently emitted by the frontend code.  */
 
 rtx catch_clauses;
 
-/* A list of actions for handlers for regions that are not yet
-   closed.  */
+/* A TREE_CHAINed list of handlers for regions that are not yet
+   closed. The TREE_VALUE of each entry contains the handler for the
+   corresponding entry on the ehstack.  */
 
-tree protect_list;
+static tree protect_list;
 
 /* Stacks to keep track of various labels.  */
 
-/* Keeps track of the label to resume to, should one want to resume
-   the normal control flow out of a handler.  Also used to rethrow
-   exceptions caught in handlers, as if they were physically emitted
-   inline.  */
+/* Keeps track of the label to resume to should one want to resume
+   normal control flow out of a handler (instead of, say, returning to
+   the caller of the current function or exiting the program).  Also
+   used as the context of a throw to rethrow an exception to the outer
+   exception region.  */
 
 struct label_node *caught_return_label_stack = NULL;
 
-/* A spare data area for the front end's own use.  */
+/* A random data area for the front end's own use.  */
 
 struct label_node *false_label_stack = NULL;
 
@@ -226,7 +519,7 @@ top_label_entry (stack)
   return (*stack)->u.tlabel;
 }
 
-/* Copy an entry.  */
+/* Make a copy of ENTRY using xmalloc to allocate the space.  */
 
 static struct eh_entry *
 copy_eh_entry (entry)
@@ -240,7 +533,8 @@ copy_eh_entry (entry)
   return newentry;
 }
 
-/* Push an entry onto the given STACK.  */
+/* Push a new eh_node entry onto STACK, and return the start label for
+   the entry.  */
 
 static rtx
 push_eh_entry (stack)
@@ -324,7 +618,10 @@ dequeue_eh_entry (queue)
 \f
 /* Routine to see if exception exception handling is turned on.
    DO_WARN is non-zero if we want to inform the user that exception
-   handling is turned off.  */
+   handling is turned off. 
+
+   This is used to ensure that -fexceptions has been specified if the
+   compiler tries to use any exception-specific functions.  */
 
 int
 doing_eh (do_warn)
@@ -343,9 +640,8 @@ doing_eh (do_warn)
   return 1;
 }
 
-/* Given the return address in ADDR, compute the new pc to throw.
-   This has to work for the current frame of the current function, and
-   the one above it in the case of throw.  */
+/* Given a return address in ADDR, determine the address we should use
+   to find the corresponding EH region.  */
 
 rtx
 eh_outer_context (addr)
@@ -359,9 +655,9 @@ eh_outer_context (addr)
                               addr, MASK_RETURN_ADDR)));
 #endif
 
-  /* Then subtract out enough to get into the prior region.  If this
-     is defined, assume we don't need to subtract anything, as it is
-     already within the region.  */
+  /* Then subtract out enough to get into the appropriate region.  If
+     this is defined, assume we don't need to subtract anything as it
+     is already within the correct region.  */
 #if ! defined (RETURN_ADDR_OFFSET)
   addr = plus_constant (addr, -1);
 #endif
@@ -369,10 +665,313 @@ eh_outer_context (addr)
   return addr;
 }
 
-/* Output a note marking the start of an exception handling region.  */
+/* Start a new exception region for a region of code that has a
+   cleanup action and push the HANDLER for the region onto
+   protect_list. All of the regions created with add_partial_entry
+   will be ended when end_protect_partials is invoked.  */
 
 void
-expand_eh_region_start ()
+add_partial_entry (handler)
+     tree handler;
+{
+  expand_eh_region_start ();
+
+  /* Make sure the entry is on the correct obstack.  */
+  push_obstacks_nochange ();
+  resume_temporary_allocation ();
+
+  /* Because this is a cleanup action, we may have to protect the handler
+     with __terminate.  */
+  handler = protect_with_terminate (handler);
+
+  protect_list = tree_cons (NULL_TREE, handler, protect_list);
+  pop_obstacks ();
+}
+
+/* Get a reference to the dynamic handler chain.  It points to the
+   pointer to the next element in the dynamic handler chain.  It ends
+   when there are no more elements in the dynamic handler chain, when
+   the value is &top_elt from libgcc2.c.  Immediately after the
+   pointer, is an area suitable for setjmp/longjmp when
+   USE_BUILTIN_SETJMP isn't defined, and an area suitable for
+   __builtin_setjmp/__builtin_longjmp when USE_BUILTIN_SETJMP is
+   defined.
+
+   This routine is here to facilitate the porting of this code to
+   systems with threads.  One can either replace the routine we emit a
+   call for here in libgcc2.c, or one can modify this routine to work
+   with their thread system.  */
+
+rtx
+get_dynamic_handler_chain ()
+{
+#if 0
+  /* Do this once we figure out how to get this to the front of the
+     function, and we really only want one per real function, not one
+     per inlined function.  */
+  if (current_function_dhc == 0)
+    {
+      rtx dhc, insns;
+      start_sequence ();
+
+      dhc = emit_library_call_value (get_dynamic_handler_chain_libfunc,
+                                    NULL_RTX, 1,
+                                    Pmode, 0);
+      current_function_dhc = copy_to_reg (dhc);
+      insns = get_insns ();
+      end_sequence ();
+      emit_insns_before (insns, get_first_nonparm_insn ());
+    }
+#else
+  rtx dhc;
+  dhc = emit_library_call_value (get_dynamic_handler_chain_libfunc,
+                                NULL_RTX, 1,
+                                Pmode, 0);
+  current_function_dhc = copy_to_reg (dhc);
+#endif
+
+  /* We don't want a copy of the dhc, but rather, the single dhc.  */
+  return gen_rtx (MEM, Pmode, current_function_dhc);
+}
+
+/* Get a reference to the dynamic cleanup chain.  It points to the
+   pointer to the next element in the dynamic cleanup chain.
+   Immediately after the pointer, are two Pmode variables, one for a
+   pointer to a function that performs the cleanup action, and the
+   second, the argument to pass to that function.  */
+
+rtx
+get_dynamic_cleanup_chain ()
+{
+  rtx dhc, dcc;
+
+  dhc = get_dynamic_handler_chain ();
+  dcc = plus_constant (dhc, GET_MODE_SIZE (Pmode));
+
+  current_function_dcc = copy_to_reg (dcc);
+
+  /* We don't want a copy of the dcc, but rather, the single dcc.  */
+  return gen_rtx (MEM, Pmode, current_function_dcc);
+}
+
+/* Generate code to evaluate X and jump to LABEL if the value is nonzero.
+   LABEL is an rtx of code CODE_LABEL, in this function.  */
+
+void
+jumpif_rtx (x, label)
+     rtx x;
+     rtx label;
+{
+  jumpif (make_tree (type_for_mode (GET_MODE (x), 0), x), label);
+}
+
+/* Generate code to evaluate X and jump to LABEL if the value is zero.
+   LABEL is an rtx of code CODE_LABEL, in this function.  */
+
+void
+jumpifnot_rtx (x, label)
+     rtx x;
+     rtx label;
+{
+  jumpifnot (make_tree (type_for_mode (GET_MODE (x), 0), x), label);
+}
+
+/* Start a dynamic cleanup on the EH runtime dynamic cleanup stack.
+   We just need to create an element for the cleanup list, and push it
+   into the chain.
+
+   A dynamic cleanup is a cleanup action implied by the presence of an
+   element on the EH runtime dynamic cleanup stack that is to be
+   performed when an exception is thrown.  The cleanup action is
+   performed by __sjthrow when an exception is thrown.  Only certain
+   actions can be optimized into dynamic cleanup actions.  For the
+   restrictions on what actions can be performed using this routine,
+   see expand_eh_region_start_tree.  */
+
+static void
+start_dynamic_cleanup (func, arg)
+     tree func;
+     tree arg;
+{
+  rtx dhc, dcc;
+  rtx new_func, new_arg;
+  rtx x, buf;
+  int size;
+
+  /* We allocate enough room for a pointer to the function, and
+     one argument.  */
+  size = 2;
+
+  /* XXX, FIXME: The stack space allocated this way is too long lived,
+     but there is no allocation routine that allocates at the level of
+     the last binding contour.  */
+  buf = assign_stack_local (BLKmode,
+                           GET_MODE_SIZE (Pmode)*(size+1),
+                           0);
+
+  buf = change_address (buf, Pmode, NULL_RTX);
+
+  /* Store dcc into the first word of the newly allocated buffer.  */
+
+  dcc = get_dynamic_cleanup_chain ();
+  emit_move_insn (buf, dcc);
+
+  /* Store func and arg into the cleanup list element.  */
+
+  new_func = gen_rtx (MEM, Pmode, plus_constant (XEXP (buf, 0),
+                                                GET_MODE_SIZE (Pmode)));
+  new_arg = gen_rtx (MEM, Pmode, plus_constant (XEXP (buf, 0),
+                                               GET_MODE_SIZE (Pmode)*2));
+  x = expand_expr (func, new_func, Pmode, 0);
+  if (x != new_func)
+    emit_move_insn (new_func, x);
+
+  x = expand_expr (arg, new_arg, Pmode, 0);
+  if (x != new_arg)
+    emit_move_insn (new_arg, x);
+
+  /* Update the cleanup chain.  */
+
+  emit_move_insn (dcc, XEXP (buf, 0));
+}
+
+/* Emit RTL to start a dynamic handler on the EH runtime dynamic
+   handler stack.  This should only be used by expand_eh_region_start
+   or expand_eh_region_start_tree.  */
+
+static void
+start_dynamic_handler ()
+{
+  rtx dhc, dcc;
+  rtx x, arg;
+  int size;
+
+#ifdef USE_BUILTIN_SETJMP
+  /* The number of Pmode words for the setjmp buffer, when using the
+     builtin setjmp/longjmp, see expand_builtin, case
+     BUILT_IN_LONGJMP.  */
+  size = 5;
+#else
+#ifdef JMP_BUF_SIZE
+  size = JMP_BUF_SIZE;
+#else
+  /* Should be large enough for most systems, if it is not,
+     JMP_BUF_SIZE should be defined with the proper value.  It will
+     also tend to be larger than necessary for most systems, a more
+     optimal port will define JMP_BUF_SIZE.  */
+  size = FIRST_PSEUDO_REGISTER+2;
+#endif
+#endif
+  /* XXX, FIXME: The stack space allocated this way is too long lived,
+     but there is no allocation routine that allocates at the level of
+     the last binding contour.  */
+  arg = assign_stack_local (BLKmode,
+                           GET_MODE_SIZE (Pmode)*(size+1),
+                           0);
+
+  arg = change_address (arg, Pmode, NULL_RTX);
+
+  /* Store dhc into the first word of the newly allocated buffer.  */
+
+  dhc = get_dynamic_handler_chain ();
+  dcc = gen_rtx (MEM, Pmode, plus_constant (XEXP (arg, 0),
+                                           GET_MODE_SIZE (Pmode)));
+  emit_move_insn (arg, dhc);
+
+  /* Zero out the start of the cleanup chain.  */
+  emit_move_insn (dcc, const0_rtx);
+
+  /* The jmpbuf starts two words into the area allocated.  */
+
+  x = emit_library_call_value (setjmp_libfunc, NULL_RTX, 1, SImode, 1,
+                              plus_constant (XEXP (arg, 0), GET_MODE_SIZE (Pmode)*2),
+                              Pmode);
+
+  /* If we come back here for a catch, transfer control to the
+     handler.  */
+
+  jumpif_rtx (x, ehstack.top->entry->exception_handler_label);
+
+  /* We are committed to this, so update the handler chain.  */
+
+  emit_move_insn (dhc, XEXP (arg, 0));
+}
+
+/* Start an exception handling region for the given cleanup action.
+   All instructions emitted after this point are considered to be part
+   of the region until expand_eh_region_end is invoked.  CLEANUP is
+   the cleanup action to perform.  The return value is true if the
+   exception region was optimized away.  If that case,
+   expand_eh_region_end does not need to be called for this cleanup,
+   nor should it be.
+
+   This routine notices one particular common case in C++ code
+   generation, and optimizes it so as to not need the exception
+   region.  It works by creating a dynamic cleanup action, instead of
+   of a using an exception region.  */
+
+int
+expand_eh_region_start_tree (decl, cleanup)
+     tree decl;
+     tree cleanup;
+{
+  rtx note;
+
+  /* This is the old code.  */
+  if (! doing_eh (0))
+    return 0;
+
+  /* The optimization only applies to actions protected with
+     terminate, and only applies if we are using the setjmp/longjmp
+     codegen method.  */
+  if (exceptions_via_longjmp
+      && protect_cleanup_actions_with_terminate)
+    {
+      tree func, arg;
+      tree args;
+
+      /* Ignore any UNSAVE_EXPR.  */
+      if (TREE_CODE (cleanup) == UNSAVE_EXPR)
+       cleanup = TREE_OPERAND (cleanup, 0);
+      
+      /* Further, it only applies if the action is a call, if there
+        are 2 arguments, and if the second argument is 2.  */
+
+      if (TREE_CODE (cleanup) == CALL_EXPR
+         && (args = TREE_OPERAND (cleanup, 1))
+         && (func = TREE_OPERAND (cleanup, 0))
+         && (arg = TREE_VALUE (args))
+         && (args = TREE_CHAIN (args))
+
+         /* is the second argument 2?  */
+         && TREE_CODE (TREE_VALUE (args)) == INTEGER_CST
+         && TREE_INT_CST_LOW (TREE_VALUE (args)) == 2
+         && TREE_INT_CST_HIGH (TREE_VALUE (args)) == 0
+
+         /* Make sure there are no other arguments.  */
+         && TREE_CHAIN (args) == NULL_TREE)
+       {
+         /* Arrange for returns and gotos to pop the entry we make on the
+            dynamic cleanup stack.  */
+         expand_dcc_cleanup (decl);
+         start_dynamic_cleanup (func, arg);
+         return 1;
+       }
+    }
+
+  expand_eh_region_start_for_decl (decl);
+
+  return 0;
+}
+
+/* Just like expand_eh_region_start, except if a cleanup action is
+   entered on the cleanup chain, the TREE_PURPOSE of the element put
+   on the chain is DECL.  DECL should be the associated VAR_DECL, if
+   any, otherwise it should be NULL_TREE.  */
+
+void
+expand_eh_region_start_for_decl (decl)
+     tree decl;
 {
   rtx note;
 
@@ -380,26 +979,61 @@ expand_eh_region_start ()
   if (! doing_eh (0))
     return;
 
-#if 0
-  /* Maybe do this to prevent jumping in and so on...  */
-  pushlevel (0);
-#endif
+  if (exceptions_via_longjmp)
+    {
+      /* We need a new block to record the start and end of the
+        dynamic handler chain.  We could always do this, but we
+        really want to permit jumping into such a block, and we want
+        to avoid any errors or performance impact in the SJ EH code
+        for now.  */
+      expand_start_bindings (0);
+
+      /* But we don't need or want a new temporary level.  */
+      pop_temp_slots ();
+
+      /* Mark this block as created by expand_eh_region_start.  This
+        is so that we can pop the block with expand_end_bindings
+        automatically.  */
+      mark_block_as_eh_region ();
+
+      /* Arrange for returns and gotos to pop the entry we make on the
+        dynamic handler stack.  */
+      expand_dhc_cleanup (decl);
+    }
 
-  note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG);
+  if (exceptions_via_longjmp == 0)
+    note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG);
   emit_label (push_eh_entry (&ehstack));
-  NOTE_BLOCK_NUMBER (note)
-    = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
+  if (exceptions_via_longjmp == 0)
+    NOTE_BLOCK_NUMBER (note)
+      = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
+  if (exceptions_via_longjmp)
+    start_dynamic_handler ();
 }
 
-/* Output a note marking the end of an exception handling region.
-   HANDLER is the the handler for the exception region.  */
+/* Start an exception handling region.  All instructions emitted after
+   this point are considered to be part of the region until
+   expand_eh_region_end is invoked.  */
+
+void
+expand_eh_region_start ()
+{
+  expand_eh_region_start_for_decl (NULL_TREE);
+}
+
+/* End an exception handling region.  The information about the region
+   is found on the top of ehstack.
+
+   HANDLER is either the cleanup for the exception region, or if we're
+   marking the end of a try block, HANDLER is integer_zero_node.
+
+   HANDLER will be transformed to rtl when expand_leftover_cleanups
+   is invoked.  */
 
 void
 expand_eh_region_end (handler)
      tree handler;
 {
-  rtx note;
-
   struct eh_entry *entry;
 
   if (! doing_eh (0))
@@ -407,47 +1041,82 @@ expand_eh_region_end (handler)
 
   entry = pop_eh_entry (&ehstack);
 
-  note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END);
-  NOTE_BLOCK_NUMBER (note) = CODE_LABEL_NUMBER (entry->exception_handler_label);
+  if (exceptions_via_longjmp == 0)
+    {
+      rtx note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END);
+      NOTE_BLOCK_NUMBER (note) = CODE_LABEL_NUMBER (entry->exception_handler_label);
+    }
 
+  /* Emit a label marking the end of this exception region.  */
   emit_label (entry->end_label);
 
-  /* Put in something that takes up space, as otherwise the end
-     address for the EH region could have the exact same address as
-     the outer region, causing us to miss the fact that resuming
-     exception handling with this PC value would be inside the outer
-     region.  */
-  emit_insn (gen_nop ());
+  if (exceptions_via_longjmp == 0)
+    {
+      /* Put in something that takes up space, as otherwise the end
+        address for this EH region could have the exact same address as
+        its outer region. This would cause us to miss the fact that
+        resuming exception handling with this PC value would be inside
+        the outer region.  */
+      emit_insn (gen_nop ());
+    }
 
   entry->finalization = handler;
 
   enqueue_eh_entry (&ehqueue, entry);
 
+  /* If we have already started ending the bindings, don't recurse.
+     This only happens when exceptions_via_longjmp is true.  */
+  if (is_eh_region ())
+    {
+      /* Because we don't need or want a new temporary level and
+        because we didn't create one in expand_eh_region_start,
+        create a fake one now to avoid removing one in
+        expand_end_bindings.  */
+      push_temp_slots ();
 
-#if 0
-  /* Makebe do this to prevent jumping in and so on...  */
-  poplevel (1, 0, 0);
-#endif
+      mark_block_as_not_eh_region ();
+
+      /* Maybe do this to prevent jumping in and so on...  */
+      expand_end_bindings (NULL_TREE, 0, 0);
+    }
 }
 
-/* Emit a call to __throw and note that we threw something.  */
+/* If we are using the setjmp/longjmp EH codegen method, we emit a
+   call to __sjthrow.
 
-static void
+   Otherwise, we emit a call to __throw and note that we threw
+   something, so we know we need to generate the necessary code for
+   __throw.
+
+   Before invoking throw, the __eh_pc variable must have been set up
+   to contain the PC being thrown from. This address is used by
+   __throw to determine which exception region (if any) is
+   responsible for handling the exception.  */
+
+void
 emit_throw ()
 {
+  if (exceptions_via_longjmp)
+    {
+      emit_library_call (sjthrow_libfunc, 0, VOIDmode, 0);
+    }
+  else
+    {
 #ifdef JUMP_TO_THROW
-  emit_indirect_jump (throw_libfunc);
+      emit_indirect_jump (throw_libfunc);
 #else
-  SYMBOL_REF_USED (throw_libfunc) = 1;
-  emit_library_call (throw_libfunc, 0, VOIDmode, 0);
+      SYMBOL_REF_USED (throw_libfunc) = 1;
+      emit_library_call (throw_libfunc, 0, VOIDmode, 0);
 #endif
-  throw_used = 1;
+      throw_used = 1;
+    }
   emit_barrier ();
 }
 
-/* An internal throw with an indirect CONTEXT we want to throw from.  */
+/* An internal throw with an indirect CONTEXT we want to throw from.
+   CONTEXT evaluates to the context of the throw.  */
 
-void
+static void
 expand_internal_throw_indirect (context)
      rtx context;
 {
@@ -456,8 +1125,9 @@ expand_internal_throw_indirect (context)
   emit_throw ();
 }
 
-/* An internal throw with a direct CONTEXT we want to throw from.  The
-   context should be a label.  */
+/* An internal throw with a direct CONTEXT we want to throw from.
+   CONTEXT must be a label; its address will be used as the context of
+   the throw.  */
 
 void
 expand_internal_throw (context)
@@ -467,7 +1137,7 @@ expand_internal_throw (context)
 }
 
 /* Called from expand_exception_blocks and expand_end_catch_block to
-   expand any pending handlers.  */
+   emit any pending handlers/cleanups queued from expand_eh_region_end.  */
 
 void
 expand_leftover_cleanups ()
@@ -478,31 +1148,51 @@ expand_leftover_cleanups ()
     {
       rtx prev;
 
+      /* A leftover try block. Shouldn't be one here.  */
+      if (entry->finalization == integer_zero_node)
+       abort ();
+
+      /* Output the label for the start of the exception handler.  */
       emit_label (entry->exception_handler_label);
 
+      /* And now generate the insns for the handler.  */
       expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
 
       prev = get_last_insn ();
-      if (! (prev && GET_CODE (prev) == BARRIER))
+      if (prev == NULL || GET_CODE (prev) != BARRIER)
        {
-         /* The below can be optimized away, and we could just fall into the
-            next EH handler, if we are certain they are nested.  */
-         /* Code to throw out to outer context, if we fall off end of the
-            handler.  */
-         expand_internal_throw (entry->end_label);
+         if (exceptions_via_longjmp)
+           emit_throw ();
+         else
+           {
+             /* The below can be optimized away, and we could just fall into the
+                next EH handler, if we are certain they are nested.  */
+             /* Emit code to throw to the outer context if we fall off
+                the end of the handler.  */
+             expand_internal_throw (entry->end_label);
+           }
        }
 
-      /* leftover try block, opps.  */
-      if (entry->finalization == integer_zero_node)
-       abort ();
-
       free (entry);
     }
 }
 
-/* Generate RTL for the start of all the catch blocks.  Used for
-   arranging for the exception handling code to be placed farther out
-   of line than normal.  */
+/* Called at the start of a block of try statements.  */
+void
+expand_start_try_stmts ()
+{
+  if (! doing_eh (1))
+    return;
+
+  expand_eh_region_start ();
+}
+
+/* Generate RTL for the start of a group of catch clauses. 
+
+   It is responsible for starting a new instruction sequence for the
+   instructions in the catch block, and expanding the handlers for the
+   internally-generated exception regions nested within the try block
+   corresponding to this catch block.  */
 
 void
 expand_start_all_catch ()
@@ -513,24 +1203,32 @@ expand_start_all_catch ()
   if (! doing_eh (1))
     return;
 
+  /* End the try block.  */
+  expand_eh_region_end (integer_zero_node);
+
   emit_line_note (input_filename, lineno);
   label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
 
-  /* The label for the exception handling block we will save.  This is
-     Lresume, in the documention.  */
+  /* The label for the exception handling block that we will save.
+     This is Lresume in the documention.  */
   expand_label (label);
   
-  /* Put in something that takes up space, as otherwise the end
-     address for the EH region could have the exact same address as
-     the outer region, causing us to miss the fact that resuming
-     exception handling with this PC value would be inside the outer
-     region.  */
-  emit_insn (gen_nop ());
+  if (exceptions_via_longjmp == 0)
+    {
+      /* Put in something that takes up space, as otherwise the end
+        address for the EH region could have the exact same address as
+        the outer region, causing us to miss the fact that resuming
+        exception handling with this PC value would be inside the outer
+        region.  */
+      emit_insn (gen_nop ());
+    }
 
+  /* Push the label that points to where normal flow is resumed onto
+     the top of the label stack.  */
   push_label_entry (&caught_return_label_stack, NULL_RTX, label);
 
   /* Start a new sequence for all the catch blocks.  We will add this
-     to the gloabl sequence catch_clauses, when we have completed all
+     to the global sequence catch_clauses when we have completed all
      the handlers in this handler-seq.  */
   start_sequence ();
 
@@ -539,29 +1237,55 @@ expand_start_all_catch ()
       rtx prev;
 
       entry = dequeue_eh_entry (&ehqueue);
+      /* Emit the label for the exception handler for this region, and
+        expand the code for the handler. 
+
+        Note that a catch region is handled as a side-effect here;
+        for a try block, entry->finalization will contain
+        integer_zero_node, so no code will be generated in the
+        expand_expr call below. But, the label for the handler will
+        still be emitted, so any code emitted after this point will
+        end up being the handler.  */
       emit_label (entry->exception_handler_label);
 
-      expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
-
-      /* When we get down to the matching entry, stop.  */
+      /* When we get down to the matching entry for this try block, stop.  */
       if (entry->finalization == integer_zero_node)
-       break;
+       {
+         /* Don't forget to free this entry.  */
+         free (entry);
+         break;
+       }
+
+      /* And now generate the insns for the handler.  */
+      expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
 
       prev = get_last_insn ();
-      if (! (prev && GET_CODE (prev) == BARRIER))
+      if (prev == NULL || GET_CODE (prev) != BARRIER)
        {
-         /* The below can be optimized away, and we could just fall into the
-            next EH handler, if we are certain they are nested.  */
-         /* Code to throw out to outer context, if we fall off end of the
-            handler.  */
-         expand_internal_throw (entry->end_label);
-       }
+         if (exceptions_via_longjmp)
+           emit_throw ();
+         else
+           {
+             /* Code to throw out to outer context when we fall off end
+                of the handler. We can't do this here for catch blocks,
+                so it's done in expand_end_all_catch instead.
+
+                The below can be optimized away (and we could just fall
+                into the next EH handler) if we are certain they are
+                nested.  */
 
+             expand_internal_throw (entry->end_label);
+           }
+       }
       free (entry);
     }
 }
 
-/* Generate RTL for the end of all the catch blocks.  */
+/* Finish up the catch block.  At this point all the insns for the
+   catch clauses have already been generated, so we only have to add
+   them to the catch_clauses list. We also want to make sure that if
+   we fall off the end of the catch clauses that we rethrow to the
+   outer EH region.  */
 
 void
 expand_end_all_catch ()
@@ -571,10 +1295,22 @@ expand_end_all_catch ()
   if (! doing_eh (1))
     return;
 
-  /* Code to throw out to outer context, if we fall off end of catch
-     handlers.  This is rethrow (Lresume, same id, same obj); in the
-     documentation.  */
-  expand_internal_throw (DECL_RTL (top_label_entry (&caught_return_label_stack)));
+  if (exceptions_via_longjmp)
+    emit_throw ();
+  else
+    {
+      /* Code to throw out to outer context, if we fall off end of catch
+        handlers.  This is rethrow (Lresume, same id, same obj) in the
+        documentation. We use Lresume because we know that it will throw
+        to the correct context.
+
+        In other words, if the catch handler doesn't exit or return, we
+        do a "throw" (using the address of Lresume as the point being
+        thrown from) so that the outer EH region can then try to process
+        the exception.  */
+
+      expand_internal_throw (DECL_RTL (top_label_entry (&caught_return_label_stack)));
+    }
 
   /* Now we have the complete catch sequence.  */
   new_catch_clause = get_insns ();
@@ -593,8 +1329,8 @@ expand_end_all_catch ()
   /* Here we fall through into the continuation code.  */
 }
 
-/* End all the pending exception regions from protect_list that have
-   been started, but not yet completed.  */
+/* End all the pending exception regions on protect_list. The handlers
+   will be emitted when expand_leftover_cleanups is invoked.  */
 
 void
 end_protect_partials ()
@@ -605,17 +1341,69 @@ end_protect_partials ()
       protect_list = TREE_CHAIN (protect_list);
     }
 }
+
+/* Arrange for __terminate to be called if there is an unhandled throw
+   from within E.  */
+
+tree
+protect_with_terminate (e)
+     tree e;
+{
+  /* We only need to do this when using setjmp/longjmp EH and the
+     language requires it, as otherwise we protect all of the handlers
+     at once, if we need to.  */
+  if (exceptions_via_longjmp && protect_cleanup_actions_with_terminate)
+    {
+      tree handler, result;
+
+      /* All cleanups must be on the function_obstack.  */
+      push_obstacks_nochange ();
+      resume_temporary_allocation ();
+
+      handler = make_node (RTL_EXPR);
+      TREE_TYPE (handler) = void_type_node;
+      RTL_EXPR_RTL (handler) = const0_rtx;
+      TREE_SIDE_EFFECTS (handler) = 1;
+      start_sequence_for_rtl_expr (handler);
+
+      emit_library_call (terminate_libfunc, 0, VOIDmode, 0);
+      emit_barrier ();
+
+      RTL_EXPR_SEQUENCE (handler) = get_insns ();
+      end_sequence ();
+       
+      result = build (TRY_CATCH_EXPR, TREE_TYPE (e), e, handler);
+      TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (e);
+      TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (e);
+      TREE_READONLY (result) = TREE_READONLY (e);
+
+      pop_obstacks ();
+
+      e = result;
+    }
+
+  return e;
+}
 \f
 /* The exception table that we build that is used for looking up and
-   dispatching exceptions, it's size, and it's maximum size before we
-   have to extend it.  */
+   dispatching exceptions, the current number of entries, and its
+   maximum size before we have to extend it. 
+
+   The number in eh_table is the code label number of the exception
+   handler for the region. This is added by add_eh_table_entry and
+   used by output_exception_table_entry.  */
+
 static int *eh_table;
 static int eh_table_size;
 static int eh_table_max_size;
 
 /* Note the need for an exception table entry for region N.  If we
-   don't need to output an explicit exception table, avoid all the
-   extra work.  Called during final_scan_insn time.  */
+   don't need to output an explicit exception table, avoid all of the
+   extra work.
+
+   Called from final_scan_insn when a NOTE_INSN_EH_REGION_BEG is seen.
+   N is the NOTE_BLOCK_NUMBER of the note, which comes from the code
+   label number of the exception handler for the region.  */
 
 void
 add_eh_table_entry (n)
@@ -631,7 +1419,8 @@ add_eh_table_entry (n)
          if (eh_table_max_size < 0)
            abort ();
 
-         if ((eh_table = (int *) realloc (eh_table, eh_table_max_size))
+         if ((eh_table = (int *) realloc (eh_table,
+                                          eh_table_max_size * sizeof (int)))
              == 0)
            fatal ("virtual memory exhausted");
        }
@@ -645,9 +1434,10 @@ add_eh_table_entry (n)
 #endif
 }
 
-/* Conditional to test to see if we need to output an exception table.
-   Note, on some platforms, we don't have to output a table
-   explicitly.  This routine doesn't mean we don't have one.  */
+/* Return a non-zero value if we need to output an exception table.
+
+   On some platforms, we don't have to output a table explicitly.
+   This routine doesn't mean we don't have one.  */
 
 int
 exception_table_p ()
@@ -658,7 +1448,11 @@ exception_table_p ()
   return 0;
 }
 
-/* Output an entry N for the exception table to the specified FILE.  */
+/* Output the entry of the exception table corresponding to to the
+   exception region numbered N to file FILE. 
+
+   N is the code label number corresponding to the handler of the
+   region.  */
 
 static void
 output_exception_table_entry (file, n)
@@ -683,7 +1477,7 @@ output_exception_table_entry (file, n)
   putc ('\n', file);           /* blank line */
 }
 
-/* Output the exception table if we have one and need one.  */
+/* Output the exception table if we have and need one.  */
 
 void
 output_exception_table ()
@@ -730,8 +1524,12 @@ register_exception_table ()
                     Pmode);
 }
 \f
-/* Emit the RTL for the start of the per function unwinder for the
-   current function.  */
+/* Emit the RTL for the start of the per-function unwinder for the
+   current function. See emit_unwinder for further information.
+
+   DOESNT_NEED_UNWINDER is a target-specific macro that determines if
+   the current function actually needs a per-function unwinder or not.
+   By default, all functions need one.  */
 
 void
 start_eh_unwinder ()
@@ -741,10 +1539,16 @@ start_eh_unwinder ()
     return;
 #endif
 
+  /* If we are using the setjmp/longjmp implementation, we don't need a
+     per function unwinder.  */
+
+  if (exceptions_via_longjmp)
+    return;
+
   expand_eh_region_start ();
 }
 
-/* Emit the RTL for the end of the per function unwinder for the
+/* Emit insns for the end of the per-function unwinder for the
    current function.  */
 
 void
@@ -761,6 +1565,12 @@ end_eh_unwinder ()
     return;
 #endif
 
+  /* If we are using the setjmp/longjmp implementation, we don't need a
+     per function unwinder.  */
+
+  if (exceptions_via_longjmp)
+    return;
+
   assemble_external (eh_saved_pc);
 
   expr = make_node (RTL_EXPR);
@@ -769,14 +1579,19 @@ end_eh_unwinder ()
   TREE_SIDE_EFFECTS (expr) = 1;
   start_sequence_for_rtl_expr (expr);
 
+  /* ret_val will contain the address of the code where the call
+     to the current function occurred.  */
   ret_val = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
                                        0, hard_frame_pointer_rtx);
   return_val_rtx = copy_to_reg (ret_val);
 
+  /* Get the address we need to use to determine what exception
+     handler should be invoked, and store it in __eh_pc.  */
   return_val_rtx = eh_outer_context (return_val_rtx);
-
   emit_move_insn (eh_saved_pc_rtx, return_val_rtx);
   
+  /* Either set things up so we do a return directly to __throw, or
+     we return here instead.  */
 #ifdef JUMP_TO_THROW
   emit_move_insn (ret_val, throw_libfunc);
 #else
@@ -795,6 +1610,7 @@ end_eh_unwinder ()
 
   RTL_EXPR_SEQUENCE (expr) = get_insns ();
   end_sequence ();
+
   expand_eh_region_end (expr);
 
   emit_jump (end);
@@ -809,30 +1625,77 @@ end_eh_unwinder ()
   emit_label (end);
 }
 
-/* Emit the RTL for the per function unwinder for the current
-   function, if needed.  Called after all the code that needs unwind
-   protection is output.  */
+/* If necessary, emit insns for the per function unwinder for the
+   current function.  Called after all the code that needs unwind
+   protection is output.  
+
+   The unwinder takes care of catching any exceptions that have not
+   been previously caught within the function, unwinding the stack to
+   the next frame, and rethrowing using the address of the current
+   function's caller as the context of the throw.
+
+   On some platforms __throw can do this by itself (or with the help
+   of __unwind_function) so the per-function unwinder is
+   unnecessary.
+  
+   We cannot place the unwinder into the function until after we know
+   we are done inlining, as we don't want to have more than one
+   unwinder per non-inlined function.  */
 
 void
 emit_unwinder ()
 {
-  rtx insns;
+  rtx insns, insn;
 
   start_sequence ();
   start_eh_unwinder ();
   insns = get_insns ();
   end_sequence ();
 
+  /* We place the start of the exception region associated with the
+     per function unwinder at the top of the function.  */
   if (insns)
     emit_insns_after (insns, get_insns ());
 
+  start_sequence ();
   end_eh_unwinder ();
+  insns = get_insns ();
+  end_sequence ();
+
+  /* And we place the end of the exception region before the USE and
+     CLOBBER insns that may come at the end of the function.  */
+  if (insns == 0)
+    return;
+
+  insn = get_last_insn ();
+  while (GET_CODE (insn) == NOTE
+        || (GET_CODE (insn) == INSN
+            && (GET_CODE (PATTERN (insn)) == USE
+                || GET_CODE (PATTERN (insn)) == CLOBBER)))
+    insn = PREV_INSN (insn);
+
+  if (GET_CODE (insn) == CODE_LABEL
+      && GET_CODE (PREV_INSN (insn)) == BARRIER)
+    {
+      insn = PREV_INSN (insn);
+    }
+  else
+    {
+      rtx label = gen_label_rtx ();
+      emit_label_after (label, insn);
+      insn = emit_jump_insn_after (gen_jump (label), insn);
+      insn = emit_barrier_after (insn);
+    }
+    
+  emit_insns_after (insns, insn);
 }
 
-/* Scan the current insns and build a list of handler labels.  Called
-   after the last exception handling region is added to the current
-   function (when the rtl is almost all built for the current
-   function) and before the jump optimization pass.  */
+/* Scan the current insns and build a list of handler labels. The
+   resulting list is placed in the global variable exception_handler_labels.
+
+   It is called after the last exception handling region is added to
+   the current function (when the rtl is almost all built for the
+   current function) and before the jump optimization pass.  */
 
 void
 find_exception_handler_labels ()
@@ -848,9 +1711,12 @@ find_exception_handler_labels ()
   if (! doing_eh (0))
     return;
 
-  /* First we generate a handy reference to each label.  */
+  /* Generate a handy reference to each label.  */
 
   labels = (rtx *) alloca ((max_labelno - min_labelno) * sizeof (rtx));
+  bzero ((char *) labels, (max_labelno - min_labelno) * sizeof (rtx));
+
+  /* Arrange for labels to be indexed directly by CODE_LABEL_NUMBER.  */
   labels -= min_labelno;
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
@@ -861,7 +1727,8 @@ find_exception_handler_labels ()
          labels[CODE_LABEL_NUMBER (insn)] = insn;
     }
 
-  /* Then for each start of a region, we add its label to the list.  */
+  /* For each start of a region, add its label to the list.  */
+
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
       if (GET_CODE (insn) == NOTE
@@ -888,10 +1755,11 @@ find_exception_handler_labels ()
     }
 }
 
-/* Do some sanity checking on the exception_handler_labels list.  Can
-   be called after find_exception_handler_labels is called to build
-   the list of exception handlers for the current function, and before
-   we finish processing the current function.  */
+/* Perform sanity checking on the exception_handler_labels list.
+
+   Can be called after find_exception_handler_labels is called to
+   build the list of exception handlers for the current function and
+   before we finish processing the current function.  */
 
 void
 check_exception_handler_labels ()
@@ -902,6 +1770,10 @@ check_exception_handler_labels ()
   if (! doing_eh (0))
     return;
 
+  /* Ensure that the CODE_LABEL_NUMBER for the CODE_LABEL entry point
+     in each handler corresponds to the CODE_LABEL_NUMBER of the
+     handler.  */
+
   for (handler = exception_handler_labels;
        handler;
        handler = XEXP (handler, 1))
@@ -925,13 +1797,13 @@ check_exception_handler_labels ()
                 CODE_LABEL_NUMBER (XEXP (handler, 0)));
     }
 
-  /* Now go through, and make sure that for each region we have, that we
-     have the corresponding label.  */
+  /* Now go through and make sure that for each region there is a
+     corresponding label.  */
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
       if (GET_CODE (insn) == NOTE
-         && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG ||
-             NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
+         && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
+             || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
        {
          for (handler = exception_handler_labels;
               handler;
@@ -950,7 +1822,7 @@ check_exception_handler_labels ()
 \f
 /* This group of functions initializes the exception handling data
    structures at the start of the compilation, initializes the data
-   structures at the start of a function, saves and restores the
+   structures at the start of a function, and saves and restores the
    exception handling data structures for the start/end of a nested
    function.  */
 
@@ -959,6 +1831,8 @@ check_exception_handler_labels ()
 void
 init_eh ()
 {
+  /* Generate rtl to reference the variable in which the PC of the
+     current context is saved.  */
   tree type = build_pointer_type (make_node (VOID_TYPE));
 
   eh_saved_pc = build_decl (VAR_DECL, get_identifier ("__eh_pc"), type);
@@ -968,7 +1842,7 @@ init_eh ()
   eh_saved_pc_rtx = DECL_RTL (eh_saved_pc);
 }
 
-/* Initialize various EH things.  */
+/* Initialize the per-function EH information.  */
 
 void
 init_eh_for_function ()
@@ -979,38 +1853,51 @@ init_eh_for_function ()
   false_label_stack = 0;
   caught_return_label_stack = 0;
   protect_list = NULL_TREE;
+  current_function_dhc = NULL_RTX;
+  current_function_dcc = NULL_RTX;
 }
 
-/* Save various EH things for the current function into the save area
-   denoted by P.  */
+/* Save some of the per-function EH info into the save area denoted by
+   P. 
+
+   This is currently called from save_stmt_status.  */
 
 void
 save_eh_status (p)
      struct function *p;
 {
+  assert (p != NULL);
+
   p->ehstack = ehstack;
   p->ehqueue = ehqueue;
   p->catch_clauses = catch_clauses;
   p->false_label_stack = false_label_stack;
   p->caught_return_label_stack = caught_return_label_stack;
   p->protect_list = protect_list;
+  p->dhc = current_function_dhc;
+  p->dcc = current_function_dcc;
 
   init_eh ();
 }
 
-/* Restore various EH things for the current function from the save
-   area denoted by P.  */
+/* Restore the per-function EH info saved into the area denoted by P.  
+
+   This is currently called from restore_stmt_status.  */
 
 void
 restore_eh_status (p)
      struct function *p;
 {
+  assert (p != NULL);
+
   protect_list = p->protect_list;
   caught_return_label_stack = p->caught_return_label_stack;
   false_label_stack = p->false_label_stack;
   catch_clauses        = p->catch_clauses;
   ehqueue = p->ehqueue;
   ehstack = p->ehstack;
+  current_function_dhc = p->dhc;
+  current_function_dcc = p->dcc;
 }
 \f
 /* This section is for the exception handling specific optimization
@@ -1023,24 +1910,37 @@ static int
 can_throw (insn)
      rtx insn;
 {
-  /* The only things that can possibly throw are calls.  */
+  /* Calls can always potentially throw exceptions.  */
   if (GET_CODE (insn) == CALL_INSN)
     return 1;
 
-#ifdef ASYNCH_EXCEPTIONS
-  /* If we wanted asynchronous exceptions, then everything but NOTEs
-     and CODE_LABELs could throw.  */
-  if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL)
-    return 1;
-#endif
+  if (asynchronous_exceptions)
+    {
+      /* If we wanted asynchronous exceptions, then everything but NOTEs
+        and CODE_LABELs could throw.  */
+      if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL)
+       return 1;
+    }
 
   return 0;
 }
 
-/* Scan a region, looking for a matching end, and decide if the region
-   can be removed.  INSN is the start of the region, N is the region
-   number, and DELETE_OUTER is to note if anything in this region can
-   throw.  */
+/* Scan a exception region looking for the matching end and then
+   remove it if possible. INSN is the start of the region, N is the
+   region number, and DELETE_OUTER is to note if anything in this
+   region can throw.
+
+   Regions are removed if they cannot possibly catch an exception.
+   This is determined by invoking can_throw on each insn within the
+   region; if can_throw returns true for any of the instructions, the
+   region can catch an exception, since there is an insn within the
+   region that is capable of throwing an exception.
+
+   Returns the NOTE_INSN_EH_REGION_END corresponding to this region, or
+   calls abort if it can't find one.
+
+   Can abort if INSN is not a NOTE_INSN_EH_REGION_BEGIN, or if N doesn't
+   correspond to the region number, or if DELETE_OUTER is NULL.  */
 
 static rtx
 scan_region (insn, n, delete_outer)
@@ -1053,6 +1953,12 @@ scan_region (insn, n, delete_outer)
   /* Assume we can delete the region.  */
   int delete = 1;
 
+  assert (insn != NULL_RTX
+         && GET_CODE (insn) == NOTE
+         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
+         && NOTE_BLOCK_NUMBER (insn) == n
+         && delete_outer != NULL);
+
   insn = NEXT_INSN (insn);
 
   /* Look for the matching end.  */
@@ -1079,7 +1985,7 @@ scan_region (insn, n, delete_outer)
   if (NOTE_BLOCK_NUMBER (insn) != n)
     abort ();
 
-  /* If anything can throw, we can throw.  */
+  /* If anything in this exception region can throw, we can throw.  */
   if (! delete)
     *delete_outer = 0;
   else
@@ -1128,8 +2034,9 @@ scan_region (insn, n, delete_outer)
 /* Perform various interesting optimizations for exception handling
    code.
 
-   We find empty exception regions, and remove them.  The jump
-   optimization code will remove the handler if nothing else uses it.  */
+   We look for empty exception regions and make them go (away). The
+   jump optimization code will remove the handler if nothing else uses
+   it.  */
 
 void
 exception_optimize ()
@@ -1137,12 +2044,21 @@ exception_optimize ()
   rtx insn, regions = NULL_RTX;
   int n;
 
-  /* First remove empty regions.  */
+  /* The below doesn't apply to setjmp/longjmp EH.  */
+  if (exceptions_via_longjmp)
+    return;
+
+  /* Remove empty regions.  */
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
       if (GET_CODE (insn) == NOTE
          && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
        {
+         /* Since scan_region will return the NOTE_INSN_EH_REGION_END
+            insn, we will indirectly skip through all the insns
+            inbetween. We are also guaranteed that the value of insn
+            returned will be valid, as otherwise scan_region won't
+            return.  */
          insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &n);
        }
     }