except.c

来自「GCC编译器源代码」· C语言 代码 · 共 2,017 行 · 第 1/5 页

C
2,017
字号
/* Implements exception handling.   Copyright (C) 1989, 92-96, 1997 Free Software Foundation, Inc.   Contributed by Mike Stump <mrs@cygnus.com>.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING.  If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA.  *//* 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 associated 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.  -fno-sjlj-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 implementation, 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.   If the target does not use the DWARF 2 frame unwind information, 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).   On targets that support crtstuff.c, the unwind information   is stored in a section named .eh_frame and the information for the   entire shared object or program is registered with a call to   __register_frame_info.  On other targets, the information for each   translation unit is registered from the file generated by collect2.   __register_frame_info is defined in frame.c, and is responsible for   recording all of the unwind regions into one list (which is kept in a   static variable named unwind_table_list).   The function __throw is actually responsible for doing the   throw. On machines that have unwind info support, __throw is generated   by code in libgcc2.c, otherwise __throw is generated on a   per-object-file basis for each source file compiled with   -fexceptions by the the C++ frontend.  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 transfers control directly to it.   If a handler for the context being thrown from can't be found, __throw   walks (see Walking the stack below) the stack up the dynamic call chain to   continue searching for an appropriate exception handler based upon the   caller of the function it last sought a exception handler for.  It stops   then either an exception handler is found, or when the top of the   call chain is reached.   If no handler is found, an external library function named   __terminate is called.  If a handler is found, then we restart   our search for a handler at the end of the call chain, and repeat   the search process, but instead of just walking up the call chain,   we unwind the call chain as we walk up it.   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.   Walking the stack:   The stack is walked by starting with a pointer to the current   frame, and finding the pointer to the callers frame.  The unwind info   tells __throw how to find it.   Unwinding the stack:   When we use the term unwinding the stack, we mean undoing the   effects of the function prologue in a controlled fashion so that we   still have the flow of control.  Otherwise, we could just return   (jump to the normal end of function epilogue).   This is done in __throw in libgcc2.c when we know that a handler exists   in a frame higher up the call stack than its immediate caller.   To unwind, we find the unwind data associated with the frame, if any.   If we don't find any, we call the library routine __terminate.  If we do   find it, we use the information to copy the saved register values from   that frame into the register save area in the frame for __throw, return   into a stub which updates the stack pointer, and jump to the handler.   The normal function epilogue for __throw handles restoring the saved   values into registers.   When unwinding, we use this method if we know it will   work (if DWARF2_UNWIND_INFO is defined).  Otherwise, we know that   an inline unwinder will have been emitted for any function that   __unwind_function cannot unwind.  The inline unwinder appears as a   normal exception handler for the entire function, for any function   that we know cannot be unwound by __unwind_function.  We inform the   compiler of whether a function can be unwound with   __unwind_function by having DOESNT_NEED_UNWINDER evaluate to true   when the unwinder isn't needed.  __unwind_function is used as an   action of last resort.  If no other method can be used for   unwinding, __unwind_function is used.  If it cannot unwind, it   should call __terminate.   By default, if the target-specific backend doesn't supply a definition   for __unwind_function and doesn't support DWARF2_UNWIND_INFO, 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 can encode this data in the form of additional   machine instructions or as static data in tabular form.  The later   is called the unwind data.   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.  If the static unwind data is supported, then   a per-function unwinder is not emitted.   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.    The reason the unwind data may be 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.   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 platforms that have   support for the generation of a generic unwinder are the SPARC and MIPS.   All other ports require per-function unwinders, which produce large   amounts of code bloat.   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_exceptions.  __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"#include "defaults.h"#include <stdio.h>#include "rtl.h"#include "tree.h"#include "flags.h"#include "except.h"#include "function.h"#include "insn-flags.h"#include "expr.h"#include "insn-codes.h"#include "regs.h"#include "hard-reg-set.h"#include "insn-config.h"

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?