OSDN Git Service

Manson: Doc +
authorkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 15 Sep 1996 00:47:01 +0000 (00:47 +0000)
committerkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 15 Sep 1996 00:47:01 +0000 (00:47 +0000)
(add_partial_entry): New routine.
(expand_start_try_stmts): Moved from cp/except.c.
(expand_start_all_catch): Move functionality of expand_end_try_stmts here.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@12716 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/except.c

index 64946d3..e5407e3 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,162 @@ 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.
+
+   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.
+
+   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. 
+
+   __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 an appropriate handler is
+   found, __throw jumps directly to it.
+
+   If a handler for the address 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 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.
+
+   Note that some of the regions and handlers are implicitly
+   generated. The handlers for these regions perform necessary
+   cleanups (in C++ these cleanups are responsible for invoking
+   necessary object destructors) before rethrowing the exception to
+   the outer exception region.
+
+   Internal implementation details:
+
+   The start of an exception region is indicated by calling
+   expand_eh_region_start (). expand_eh_region_end (handler) is
+   subsequently invoked to end the region and to associate a handler
+   with the region. This is used to create a region that has an
+   associated cleanup routine for performing tasks like object
+   destruction.
+
+   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 usually known as a "try block"). All statements that
+   appear afterwards will be associated with the try block.
+
+   A call to expand_start_all_catch () will mark the end of the try
+   block, and also marks the start of the "catch block" associated
+   with the try block. This catch block will only be invoked if an
+   exception is thrown through the try block. 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.
+
+   Internally-generated exception regions are marked by calling
+   expand_eh_region_start () to mark the start of the region, and
+   expand_eh_region_end () is used to both designate the end of the
+   region and to associate a handler/cleanup with the region. These
+   functions generate the appropriate RTL sequences to mark the start
+   and end of the exception regions and ensure that an appropriate
+   exception region entry will be added to the exception region table.
+   expand_eh_region_end () also queues the provided handler to be
+   emitted at the end of the current 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 if it (or any of the functions that it calls) throws
+   an exception it is caught by the associated cleanup. The backend
+   also takes care of the details of associating an exception table
+   entry with the expression and generating the necessary code.
+
+   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.
+
+   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 since no exceptions can ever be caught
+   in that region.
+
+   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 more space than if a single routine is used.
+
+   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. */
 
 
 #include "config.h"
@@ -112,56 +193,67 @@ 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
+/* 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;
 
 /* 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 must 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;
 
-/* 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.
+
+   As we exit a region, we enqueue a new entry. The entries are then
+   dequeued during expand_leftover_cleanups () and expand_start_all_catch (),
+
+   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.  */
 
 struct eh_queue ehqueue;
 
-/* Insns for the catch clauses.  */
+/* 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 +318,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 +332,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 +417,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 +439,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 +454,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,7 +464,26 @@ eh_outer_context (addr)
   return addr;
 }
 
-/* Output a note marking the start of an exception handling region.  */
+/* Start a new exception region 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
+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 ();
+  protect_list = tree_cons (NULL_TREE, handler, protect_list);
+  pop_obstacks ();
+}
+
+/* Output a note marking the start of 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 ()
@@ -391,8 +505,14 @@ expand_eh_region_start ()
     = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
 }
 
-/* Output a note marking the end of an exception handling region.
-   HANDLER is the the handler for the exception region.  */
+/* Output a note marking the end of the exception handling region 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)
@@ -410,27 +530,33 @@ expand_eh_region_end (handler)
   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.  */
+     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 0
-  /* Makebe do this to prevent jumping in and so on...  */
+  /* Maybe do this to prevent jumping in and so on...  */
   poplevel (1, 0, 0);
 #endif
 }
 
-/* Emit a call to __throw and note that we threw something.  */
+/* 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. */
 
 static void
 emit_throw ()
@@ -445,9 +571,10 @@ emit_throw ()
   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 +583,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 +595,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,8 +606,14 @@ 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 ();
@@ -487,22 +621,31 @@ expand_leftover_cleanups ()
        {
          /* 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.  */
+         /* 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,11 +656,14 @@ 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
@@ -527,10 +673,12 @@ expand_start_all_catch ()
      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 +687,48 @@ 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;
+       }
 
       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.  */
+         /* 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 ()
@@ -572,8 +739,15 @@ expand_end_all_catch ()
     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.  */
+     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.  */
@@ -593,8 +767,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 ()
@@ -607,15 +781,24 @@ end_protect_partials ()
 }
 \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)
@@ -646,9 +829,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 ()
@@ -659,7 +843,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)
@@ -684,7 +872,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 ()
@@ -731,8 +919,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 ()
@@ -745,7 +937,7 @@ start_eh_unwinder ()
   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
@@ -770,14 +962,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
@@ -810,30 +1007,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 ()
@@ -849,9 +1093,11 @@ 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));
+
+  /* Eeeeeeew. */
   labels -= min_labelno;
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
@@ -862,7 +1108,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
@@ -889,10 +1136,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 ()
@@ -903,6 +1151,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))
@@ -926,8 +1178,8 @@ 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
@@ -951,7 +1203,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.  */
 
@@ -960,6 +1212,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);
@@ -969,7 +1223,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 ()
@@ -982,13 +1236,17 @@ init_eh_for_function ()
   protect_list = NULL_TREE;
 }
 
-/* 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;
@@ -999,13 +1257,16 @@ save_eh_status (p)
   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;
@@ -1024,13 +1285,13 @@ 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.  */
+     and CODE_LABELs could throw. */
   if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL)
     return 1;
 #endif
@@ -1038,10 +1299,22 @@ can_throw (insn)
   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)
@@ -1054,6 +1327,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.  */
@@ -1080,7 +1359,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
@@ -1129,8 +1408,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 ()
@@ -1138,12 +1418,17 @@ exception_optimize ()
   rtx insn, regions = NULL_RTX;
   int n;
 
-  /* First remove empty regions.  */
+  /* 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);
        }
     }