OSDN Git Service

Updates from Bob.
authormrs <mrs@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 29 Oct 1996 01:25:27 +0000 (01:25 +0000)
committermrs <mrs@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 29 Oct 1996 01:25:27 +0000 (01:25 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@13061 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/except.c

index e5407e3..af3f50c 100644 (file)
@@ -55,10 +55,27 @@ Boston, MA 02111-1307, USA.  */
    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.
+   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
@@ -69,87 +86,130 @@ Boston, MA 02111-1307, USA.  */
    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. 
+   -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 an appropriate handler is
-   found, __throw jumps directly to it.
+   (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 address being thrown from can't be found,
+   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 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.
+   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:
 
-   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 ().
+   (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.
-
-   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.
+   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 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.
+   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.
+   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 since no exceptions can ever be caught
-   in that region.
+   can be safely optimized away (along with its exception handlers)
+   since no exceptions can ever be caught in that region.
 
    Unwinding the stack:
 
@@ -165,7 +225,15 @@ Boston, MA 02111-1307, USA.  */
    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.
+   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
@@ -175,7 +243,74 @@ Boston, MA 02111-1307, USA.  */
    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. */
+   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.  */
 
 
 #include "config.h"