diff options
Diffstat (limited to 'contrib/gcc/except.c')
-rw-r--r-- | contrib/gcc/except.c | 2967 |
1 files changed, 0 insertions, 2967 deletions
diff --git a/contrib/gcc/except.c b/contrib/gcc/except.c deleted file mode 100644 index f7d78d687eff..000000000000 --- a/contrib/gcc/except.c +++ /dev/null @@ -1,2967 +0,0 @@ -/* Implements exception handling. - Copyright (C) 1989, 92-97, 1998 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 modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - - -/* 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 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 "eh-common.h" -#include "system.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" -#include "recog.h" -#include "output.h" -#include "toplev.h" -#include "intl.h" -#include "obstack.h" - -/* One to use setjmp/longjmp method of generating code for exception - handling. */ - -int exceptions_via_longjmp = 2; - -/* 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; - -/* A list of labels used for exception handlers. Created by - find_exception_handler_labels for the optimization passes. */ - -rtx exception_handler_labels; - -/* The EH context. Nonzero if the function has already - fetched a pointer to the EH context for exception handling. */ - -rtx current_function_ehc; - -/* A stack used for keeping track of the currently active exception - 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 - 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. */ - -static struct eh_stack ehstack; - - -/* This stack is used to represent what the current eh region is - for the catch blocks beings processed */ - -static struct eh_stack catchstack; - -/* 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. */ - -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 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. */ - -static tree protect_list; - -/* Stacks to keep track of various labels. */ - -/* 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). */ - -struct label_node *caught_return_label_stack = NULL; - -/* Keeps track of the label used as the context of a throw to rethrow an - exception to the outer exception region. */ - -struct label_node *outer_context_label_stack = NULL; - -/* A random data area for the front end's own use. */ - -struct label_node *false_label_stack = NULL; - -/* Pseudos used to hold exception return data in the interim between - __builtin_eh_return and the end of the function. */ - -static rtx eh_return_context; -static rtx eh_return_stack_adjust; -static rtx eh_return_handler; - -/* Used to mark the eh return stub for flow, so that the Right Thing - happens with the values for the hardregs therin. */ - -rtx eh_return_stub_label; - -/* This is used for targets which can call rethrow with an offset instead - of an address. This is subtracted from the rethrow label we are - interested in. */ - -static rtx first_rethrow_symbol = NULL_RTX; -static rtx final_rethrow = NULL_RTX; -static rtx last_rethrow_symbol = NULL_RTX; - - -/* Prototypes for local functions. */ - -static void push_eh_entry PROTO((struct eh_stack *)); -static struct eh_entry * pop_eh_entry PROTO((struct eh_stack *)); -static void enqueue_eh_entry PROTO((struct eh_queue *, struct eh_entry *)); -static struct eh_entry * dequeue_eh_entry PROTO((struct eh_queue *)); -static rtx call_get_eh_context PROTO((void)); -static void start_dynamic_cleanup PROTO((tree, tree)); -static void start_dynamic_handler PROTO((void)); -static void expand_rethrow PROTO((rtx)); -static void output_exception_table_entry PROTO((FILE *, int)); -static int can_throw PROTO((rtx)); -static rtx scan_region PROTO((rtx, int, int *)); -static void eh_regs PROTO((rtx *, rtx *, rtx *, int)); -static void set_insn_eh_region PROTO((rtx *, int)); -#ifdef DONT_USE_BUILTIN_SETJMP -static void jumpif_rtx PROTO((rtx, rtx)); -#endif - -rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx)); - -/* Various support routines to manipulate the various data structures - used by the exception handling code. */ - -extern struct obstack permanent_obstack; - -/* Generate a SYMBOL_REF for rethrow to use */ -static rtx -create_rethrow_ref (region_num) - int region_num; -{ - rtx def; - char *ptr; - char buf[60]; - - push_obstacks_nochange (); - end_temporary_allocation (); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", region_num); - ptr = (char *) obstack_copy0 (&permanent_obstack, buf, strlen (buf)); - def = gen_rtx_SYMBOL_REF (Pmode, ptr); - SYMBOL_REF_NEED_ADJUST (def) = 1; - - pop_obstacks (); - return def; -} - -/* Push a label entry onto the given STACK. */ - -void -push_label_entry (stack, rlabel, tlabel) - struct label_node **stack; - rtx rlabel; - tree tlabel; -{ - struct label_node *newnode - = (struct label_node *) xmalloc (sizeof (struct label_node)); - - if (rlabel) - newnode->u.rlabel = rlabel; - else - newnode->u.tlabel = tlabel; - newnode->chain = *stack; - *stack = newnode; -} - -/* Pop a label entry from the given STACK. */ - -rtx -pop_label_entry (stack) - struct label_node **stack; -{ - rtx label; - struct label_node *tempnode; - - if (! *stack) - return NULL_RTX; - - tempnode = *stack; - label = tempnode->u.rlabel; - *stack = (*stack)->chain; - free (tempnode); - - return label; -} - -/* Return the top element of the given STACK. */ - -tree -top_label_entry (stack) - struct label_node **stack; -{ - if (! *stack) - return NULL_TREE; - - return (*stack)->u.tlabel; -} - -/* get an exception label. These must be on the permanent obstack */ - -rtx -gen_exception_label () -{ - rtx lab; - lab = gen_label_rtx (); - return lab; -} - -/* Push a new eh_node entry onto STACK. */ - -static void -push_eh_entry (stack) - struct eh_stack *stack; -{ - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); - struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry)); - - rtx rlab = gen_exception_label (); - entry->finalization = NULL_TREE; - entry->label_used = 0; - entry->exception_handler_label = rlab; - entry->false_label = NULL_RTX; - if (! flag_new_exceptions) - entry->outer_context = gen_label_rtx (); - else - entry->outer_context = create_rethrow_ref (CODE_LABEL_NUMBER (rlab)); - entry->rethrow_label = entry->outer_context; - - node->entry = entry; - node->chain = stack->top; - stack->top = node; -} - -/* push an existing entry onto a stack. */ -static void -push_entry (stack, entry) - struct eh_stack *stack; - struct eh_entry *entry; -{ - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); - node->entry = entry; - node->chain = stack->top; - stack->top = node; -} - -/* Pop an entry from the given STACK. */ - -static struct eh_entry * -pop_eh_entry (stack) - struct eh_stack *stack; -{ - struct eh_node *tempnode; - struct eh_entry *tempentry; - - tempnode = stack->top; - tempentry = tempnode->entry; - stack->top = stack->top->chain; - free (tempnode); - - return tempentry; -} - -/* Enqueue an ENTRY onto the given QUEUE. */ - -static void -enqueue_eh_entry (queue, entry) - struct eh_queue *queue; - struct eh_entry *entry; -{ - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); - - node->entry = entry; - node->chain = NULL; - - if (queue->head == NULL) - { - queue->head = node; - } - else - { - queue->tail->chain = node; - } - queue->tail = node; -} - -/* Dequeue an entry from the given QUEUE. */ - -static struct eh_entry * -dequeue_eh_entry (queue) - struct eh_queue *queue; -{ - struct eh_node *tempnode; - struct eh_entry *tempentry; - - if (queue->head == NULL) - return NULL; - - tempnode = queue->head; - queue->head = queue->head->chain; - - tempentry = tempnode->entry; - free (tempnode); - - return tempentry; -} - -static void -receive_exception_label (handler_label) - rtx handler_label; -{ - emit_label (handler_label); - -#ifdef HAVE_exception_receiver - if (! exceptions_via_longjmp) - if (HAVE_exception_receiver) - emit_insn (gen_exception_receiver ()); -#endif - -#ifdef HAVE_nonlocal_goto_receiver - if (! exceptions_via_longjmp) - if (HAVE_nonlocal_goto_receiver) - emit_insn (gen_nonlocal_goto_receiver ()); -#endif -} - - -struct func_eh_entry -{ - int range_number; /* EH region number from EH NOTE insn's */ - rtx rethrow_label; /* Label for rethrow */ - struct handler_info *handlers; -}; - - -/* table of function eh regions */ -static struct func_eh_entry *function_eh_regions = NULL; -static int num_func_eh_entries = 0; -static int current_func_eh_entry = 0; - -#define SIZE_FUNC_EH(X) (sizeof (struct func_eh_entry) * X) - -/* Add a new eh_entry for this function, and base it off of the information - in the EH_ENTRY parameter. A NULL parameter is invalid. - OUTER_CONTEXT is a label which is used for rethrowing. The number - returned is an number which uniquely identifies this exception range. */ - -static int -new_eh_region_entry (note_eh_region, rethrow) - int note_eh_region; - rtx rethrow; -{ - if (current_func_eh_entry == num_func_eh_entries) - { - if (num_func_eh_entries == 0) - { - function_eh_regions = - (struct func_eh_entry *) malloc (SIZE_FUNC_EH (50)); - num_func_eh_entries = 50; - } - else - { - num_func_eh_entries = num_func_eh_entries * 3 / 2; - function_eh_regions = (struct func_eh_entry *) - realloc (function_eh_regions, SIZE_FUNC_EH (num_func_eh_entries)); - } - } - function_eh_regions[current_func_eh_entry].range_number = note_eh_region; - if (rethrow == NULL_RTX) - function_eh_regions[current_func_eh_entry].rethrow_label = - create_rethrow_ref (note_eh_region); - else - function_eh_regions[current_func_eh_entry].rethrow_label = rethrow; - function_eh_regions[current_func_eh_entry].handlers = NULL; - - return current_func_eh_entry++; -} - -/* Add new handler information to an exception range. The first parameter - specifies the range number (returned from new_eh_entry()). The second - parameter specifies the handler. By default the handler is inserted at - the end of the list. A handler list may contain only ONE NULL_TREE - typeinfo entry. Regardless where it is positioned, a NULL_TREE entry - is always output as the LAST handler in the exception table for a region. */ - -void -add_new_handler (region, newhandler) - int region; - struct handler_info *newhandler; -{ - struct handler_info *last; - - newhandler->next = NULL; - last = function_eh_regions[region].handlers; - if (last == NULL) - function_eh_regions[region].handlers = newhandler; - else - { - for ( ; ; last = last->next) - { - if (last->type_info == CATCH_ALL_TYPE) - pedwarn ("additional handler after ..."); - if (last->next == NULL) - break; - } - last->next = newhandler; - } -} - -/* Remove a handler label. The handler label is being deleted, so all - regions which reference this handler should have it removed from their - list of possible handlers. Any region which has the final handler - removed can be deleted. */ - -void remove_handler (removing_label) - rtx removing_label; -{ - struct handler_info *handler, *last; - int x; - for (x = 0 ; x < current_func_eh_entry; ++x) - { - last = NULL; - handler = function_eh_regions[x].handlers; - for ( ; handler; last = handler, handler = handler->next) - if (handler->handler_label == removing_label) - { - if (last) - { - last->next = handler->next; - handler = last; - } - else - function_eh_regions[x].handlers = handler->next; - } - } -} - -/* This function will return a malloc'd pointer to an array of - void pointer representing the runtime match values that - currently exist in all regions. */ - -int -find_all_handler_type_matches (array) - void ***array; -{ - struct handler_info *handler, *last; - int x,y; - void *val; - void **ptr; - int max_ptr; - int n_ptr = 0; - - *array = NULL; - - if (!doing_eh (0) || ! flag_new_exceptions) - return 0; - - max_ptr = 100; - ptr = (void **)malloc (max_ptr * sizeof (void *)); - - if (ptr == NULL) - return 0; - - for (x = 0 ; x < current_func_eh_entry; x++) - { - last = NULL; - handler = function_eh_regions[x].handlers; - for ( ; handler; last = handler, handler = handler->next) - { - val = handler->type_info; - if (val != NULL && val != CATCH_ALL_TYPE) - { - /* See if this match value has already been found. */ - for (y = 0; y < n_ptr; y++) - if (ptr[y] == val) - break; - - /* If we break early, we already found this value. */ - if (y < n_ptr) - continue; - - /* Do we need to allocate more space? */ - if (n_ptr >= max_ptr) - { - max_ptr += max_ptr / 2; - ptr = (void **)realloc (ptr, max_ptr * sizeof (void *)); - if (ptr == NULL) - return 0; - } - ptr[n_ptr] = val; - n_ptr++; - } - } - } - *array = ptr; - return n_ptr; -} - -/* Create a new handler structure initialized with the handler label and - typeinfo fields passed in. */ - -struct handler_info * -get_new_handler (handler, typeinfo) - rtx handler; - void *typeinfo; -{ - struct handler_info* ptr; - ptr = (struct handler_info *) malloc (sizeof (struct handler_info)); - ptr->handler_label = handler; - ptr->handler_number = CODE_LABEL_NUMBER (handler); - ptr->type_info = typeinfo; - ptr->next = NULL; - - return ptr; -} - - - -/* Find the index in function_eh_regions associated with a NOTE region. If - the region cannot be found, a -1 is returned. This should never happen! */ - -int -find_func_region (insn_region) - int insn_region; -{ - int x; - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].range_number == insn_region) - return x; - - return -1; -} - -/* Get a pointer to the first handler in an exception region's list. */ - -struct handler_info * -get_first_handler (region) - int region; -{ - return function_eh_regions[find_func_region (region)].handlers; -} - -/* Clean out the function_eh_region table and free all memory */ - -static void -clear_function_eh_region () -{ - int x; - struct handler_info *ptr, *next; - for (x = 0; x < current_func_eh_entry; x++) - for (ptr = function_eh_regions[x].handlers; ptr != NULL; ptr = next) - { - next = ptr->next; - free (ptr); - } - free (function_eh_regions); - num_func_eh_entries = 0; - current_func_eh_entry = 0; -} - -/* Make a duplicate of an exception region by copying all the handlers - for an exception region. Return the new handler index. The final - parameter is a routine which maps old labels to new ones. */ - -int -duplicate_eh_handlers (old_note_eh_region, new_note_eh_region, map) - int old_note_eh_region, new_note_eh_region; - rtx (*map) PARAMS ((rtx)); -{ - struct handler_info *ptr, *new_ptr; - int new_region, region; - - region = find_func_region (old_note_eh_region); - if (region == -1) - fatal ("Cannot duplicate non-existant exception region."); - - /* duplicate_eh_handlers may have been called during a symbol remap. */ - new_region = find_func_region (new_note_eh_region); - if (new_region != -1) - return (new_region); - - new_region = new_eh_region_entry (new_note_eh_region, NULL_RTX); - - ptr = function_eh_regions[region].handlers; - - for ( ; ptr; ptr = ptr->next) - { - new_ptr = get_new_handler (map (ptr->handler_label), ptr->type_info); - add_new_handler (new_region, new_ptr); - } - - return new_region; -} - - -/* Given a rethrow symbol, find the EH region number this is for. */ -int -eh_region_from_symbol (sym) - rtx sym; -{ - int x; - if (sym == last_rethrow_symbol) - return 1; - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].rethrow_label == sym) - return function_eh_regions[x].range_number; - return -1; -} - - -/* When inlining/unrolling, we have to map the symbols passed to - __rethrow as well. This performs the remap. If a symbol isn't foiund, - the original one is returned. This is not an efficient routine, - so don't call it on everything!! */ -rtx -rethrow_symbol_map (sym, map) - rtx sym; - rtx (*map) PARAMS ((rtx)); -{ - int x, y; - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].rethrow_label == sym) - { - /* We've found the original region, now lets determine which region - this now maps to. */ - rtx l1 = function_eh_regions[x].handlers->handler_label; - rtx l2 = map (l1); - y = CODE_LABEL_NUMBER (l2); /* This is the new region number */ - x = find_func_region (y); /* Get the new permanent region */ - if (x == -1) /* Hmm, Doesn't exist yet */ - { - x = duplicate_eh_handlers (CODE_LABEL_NUMBER (l1), y, map); - /* Since we're mapping it, it must be used. */ - SYMBOL_REF_USED (function_eh_regions[x].rethrow_label) = 1; - } - return function_eh_regions[x].rethrow_label; - } - return sym; -} - -int -rethrow_used (region) - int region; -{ - if (flag_new_exceptions) - { - rtx lab = function_eh_regions[find_func_region (region)].rethrow_label; - return (SYMBOL_REF_USED (lab)); - } - return 0; -} - - -/* Routine to see if exception handling is turned on. - DO_WARN is non-zero if we want to inform the user that exception - 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) - int do_warn; -{ - if (! flag_exceptions) - { - static int warned = 0; - if (! warned && do_warn) - { - error ("exception handling disabled, use -fexceptions to enable"); - warned = 1; - } - return 0; - } - return 1; -} - -/* Given a return address in ADDR, determine the address we should use - to find the corresponding EH region. */ - -rtx -eh_outer_context (addr) - rtx addr; -{ - /* First mask out any unwanted bits. */ -#ifdef MASK_RETURN_ADDR - expand_and (addr, MASK_RETURN_ADDR, addr); -#endif - - /* Then adjust to find the real return address. */ -#if defined (RETURN_ADDR_OFFSET) - addr = plus_constant (addr, RETURN_ADDR_OFFSET); -#endif - - return addr; -} - -/* 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 -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 (); -} - -/* Emit code to get EH context to current function. */ - -static rtx -call_get_eh_context () -{ - static tree fn; - tree expr; - - if (fn == NULL_TREE) - { - tree fntype; - fn = get_identifier ("__get_eh_context"); - push_obstacks_nochange (); - end_temporary_allocation (); - fntype = build_pointer_type (build_pointer_type - (build_pointer_type (void_type_node))); - fntype = build_function_type (fntype, NULL_TREE); - fn = build_decl (FUNCTION_DECL, fn, fntype); - DECL_EXTERNAL (fn) = 1; - TREE_PUBLIC (fn) = 1; - DECL_ARTIFICIAL (fn) = 1; - TREE_READONLY (fn) = 1; - make_decl_rtl (fn, NULL_PTR, 1); - assemble_external (fn); - pop_obstacks (); - } - - expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); - expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), - expr, NULL_TREE, NULL_TREE); - TREE_SIDE_EFFECTS (expr) = 1; - - return copy_to_reg (expand_expr (expr, NULL_RTX, VOIDmode, 0)); -} - -/* Get a reference to the EH context. - We will only generate a register for the current function EH context here, - and emit a USE insn to mark that this is a EH context register. - - Later, emit_eh_context will emit needed call to __get_eh_context - in libgcc2, and copy the value to the register we have generated. */ - -rtx -get_eh_context () -{ - if (current_function_ehc == 0) - { - rtx insn; - - current_function_ehc = gen_reg_rtx (Pmode); - - insn = gen_rtx_USE (GET_MODE (current_function_ehc), - current_function_ehc); - insn = emit_insn_before (insn, get_first_nonparm_insn ()); - - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_EH_CONTEXT, current_function_ehc, - REG_NOTES (insn)); - } - return current_function_ehc; -} - -/* 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 - DONT_USE_BUILTIN_SETJMP is defined, and an area suitable for - __builtin_setjmp/__builtin_longjmp when DONT_USE_BUILTIN_SETJMP - isn't defined. */ - -rtx -get_dynamic_handler_chain () -{ - rtx ehc, dhc, result; - - ehc = get_eh_context (); - - /* This is the offset of dynamic_handler_chain in the eh_context struct - declared in eh-common.h. If its location is change, change this offset */ - dhc = plus_constant (ehc, POINTER_SIZE / BITS_PER_UNIT); - - result = copy_to_reg (dhc); - - /* We don't want a copy of the dcc, but rather, the single dcc. */ - return gen_rtx_MEM (Pmode, result); -} - -/* 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, result; - - dhc = get_dynamic_handler_chain (); - dcc = plus_constant (dhc, POINTER_SIZE / BITS_PER_UNIT); - - result = copy_to_reg (dcc); - - /* We don't want a copy of the dcc, but rather, the single dcc. */ - return gen_rtx_MEM (Pmode, result); -} - -#ifdef DONT_USE_BUILTIN_SETJMP -/* 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. */ - -static void -jumpif_rtx (x, label) - rtx x; - rtx label; -{ - jumpif (make_tree (type_for_mode (GET_MODE (x), 0), x), label); -} -#endif - -/* 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 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, buf; - int size; - -#ifndef DONT_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. */ - buf = plus_constant (XEXP (arg, 0), GET_MODE_SIZE (Pmode)*2); - -#ifdef DONT_USE_BUILTIN_SETJMP - x = emit_library_call_value (setjmp_libfunc, NULL_RTX, 1, SImode, 1, - buf, Pmode); - /* If we come back here for a catch, transfer control to the handler. */ - jumpif_rtx (x, ehstack.top->entry->exception_handler_label); -#else - { - /* A label to continue execution for the no exception case. */ - rtx noex = gen_label_rtx(); - x = expand_builtin_setjmp (buf, NULL_RTX, noex, - ehstack.top->entry->exception_handler_label); - emit_label (noex); - } -#endif - - /* We are committed to this, so update the handler chain. */ - - emit_move_insn (dhc, force_operand (XEXP (arg, 0), NULL_RTX)); -} - -/* 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 - a using an exception region. */ - -int -expand_eh_region_start_tree (decl, cleanup) - tree decl; - tree cleanup; -{ - /* 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); - ehstack.top->entry->finalization = cleanup; - - 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; - - /* This is the old code. */ - if (! doing_eh (0)) - return; - - 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); - } - - push_eh_entry (&ehstack); - note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG); - NOTE_BLOCK_NUMBER (note) - = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label); - if (exceptions_via_longjmp) - start_dynamic_handler (); -} - -/* 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; -{ - struct eh_entry *entry; - rtx note; - int ret, r; - - if (! doing_eh (0)) - return; - - entry = pop_eh_entry (&ehstack); - - note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END); - ret = NOTE_BLOCK_NUMBER (note) - = CODE_LABEL_NUMBER (entry->exception_handler_label); - if (exceptions_via_longjmp == 0 && ! flag_new_exceptions - /* We share outer_context between regions; only emit it once. */ - && INSN_UID (entry->outer_context) == 0) - { - rtx label; - - label = gen_label_rtx (); - emit_jump (label); - - /* Emit a label marking the end of this exception region that - is used for rethrowing into the outer context. */ - emit_label (entry->outer_context); - expand_internal_throw (); - - emit_label (label); - } - - entry->finalization = handler; - - /* create region entry in final exception table */ - r = new_eh_region_entry (NOTE_BLOCK_NUMBER (note), entry->rethrow_label); - - 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 (); - - mark_block_as_not_eh_region (); - - /* Maybe do this to prevent jumping in and so on... */ - expand_end_bindings (NULL_TREE, 0, 0); - } -} - -/* End the EH region for a goto fixup. We only need them in the region-based - EH scheme. */ - -void -expand_fixup_region_start () -{ - if (! doing_eh (0) || exceptions_via_longjmp) - return; - - expand_eh_region_start (); -} - -/* End the EH region for a goto fixup. CLEANUP is the cleanup we just - expanded; to avoid running it twice if it throws, we look through the - ehqueue for a matching region and rethrow from its outer_context. */ - -void -expand_fixup_region_end (cleanup) - tree cleanup; -{ - struct eh_node *node; - int dont_issue; - - if (! doing_eh (0) || exceptions_via_longjmp) - return; - - for (node = ehstack.top; node && node->entry->finalization != cleanup; ) - node = node->chain; - if (node == 0) - for (node = ehqueue.head; node && node->entry->finalization != cleanup; ) - node = node->chain; - if (node == 0) - abort (); - - /* If the outer context label has not been issued yet, we don't want - to issue it as a part of this region, unless this is the - correct region for the outer context. If we did, then the label for - the outer context will be WITHIN the begin/end labels, - and we could get an infinte loop when it tried to rethrow, or just - generally incorrect execution following a throw. */ - - dont_issue = ((INSN_UID (node->entry->outer_context) == 0) - && (ehstack.top->entry != node->entry)); - - ehstack.top->entry->outer_context = node->entry->outer_context; - - /* Since we are rethrowing to the OUTER region, we know we don't need - a jump around sequence for this region, so we'll pretend the outer - context label has been issued by setting INSN_UID to 1, then clearing - it again afterwards. */ - - if (dont_issue) - INSN_UID (node->entry->outer_context) = 1; - - /* Just rethrow. size_zero_node is just a NOP. */ - expand_eh_region_end (size_zero_node); - - if (dont_issue) - INSN_UID (node->entry->outer_context) = 0; -} - -/* If we are using the setjmp/longjmp EH codegen method, we emit a - call to __sjthrow. - - 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); -#else - emit_library_call (throw_libfunc, 0, VOIDmode, 0); -#endif - } - emit_barrier (); -} - -/* Throw the current exception. If appropriate, this is done by jumping - to the next handler. */ - -void -expand_internal_throw () -{ - emit_throw (); -} - -/* Called from expand_exception_blocks and expand_end_catch_block to - emit any pending handlers/cleanups queued from expand_eh_region_end. */ - -void -expand_leftover_cleanups () -{ - struct eh_entry *entry; - - while ((entry = dequeue_eh_entry (&ehqueue)) != 0) - { - 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. */ - - receive_exception_label (entry->exception_handler_label); - - /* register a handler for this cleanup region */ - add_new_handler ( - find_func_region (CODE_LABEL_NUMBER (entry->exception_handler_label)), - get_new_handler (entry->exception_handler_label, NULL)); - - /* And now generate the insns for the handler. */ - expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); - - prev = get_last_insn (); - if (prev == NULL || GET_CODE (prev) != BARRIER) - /* Emit code to throw to the outer context if we fall off - the end of the handler. */ - expand_rethrow (entry->outer_context); - - do_pending_stack_adjust (); - free (entry); - } -} - -/* Called at the start of a block of try statements. */ -void -expand_start_try_stmts () -{ - if (! doing_eh (1)) - return; - - expand_eh_region_start (); -} - -/* Called to begin a catch clause. The parameter is the object which - will be passed to the runtime type check routine. */ -void -start_catch_handler (rtime) - tree rtime; -{ - rtx handler_label; - int insn_region_num; - int eh_region_entry; - - if (! doing_eh (1)) - return; - - handler_label = catchstack.top->entry->exception_handler_label; - insn_region_num = CODE_LABEL_NUMBER (handler_label); - eh_region_entry = find_func_region (insn_region_num); - - /* If we've already issued this label, pick a new one */ - if (catchstack.top->entry->label_used) - handler_label = gen_exception_label (); - else - catchstack.top->entry->label_used = 1; - - receive_exception_label (handler_label); - - add_new_handler (eh_region_entry, get_new_handler (handler_label, rtime)); - - if (flag_new_exceptions && ! exceptions_via_longjmp) - return; - - /* Under the old mechanism, as well as setjmp/longjmp, we need to - issue code to compare 'rtime' to the value in eh_info, via the - matching function in eh_info. If its is false, we branch around - the handler we are about to issue. */ - - if (rtime != NULL_TREE && rtime != CATCH_ALL_TYPE) - { - rtx call_rtx, rtime_address; - - if (catchstack.top->entry->false_label != NULL_RTX) - fatal ("Compiler Bug: Never issued previous false_label"); - catchstack.top->entry->false_label = gen_exception_label (); - - rtime_address = expand_expr (rtime, NULL_RTX, Pmode, EXPAND_INITIALIZER); -#ifdef POINTERS_EXTEND_UNSIGNED - rtime_address = convert_memory_address (Pmode, rtime_address); -#endif - rtime_address = force_reg (Pmode, rtime_address); - - /* Now issue the call, and branch around handler if needed */ - call_rtx = emit_library_call_value (eh_rtime_match_libfunc, NULL_RTX, - 0, SImode, 1, rtime_address, Pmode); - - /* Did the function return true? */ - emit_cmp_and_jump_insns (call_rtx, const0_rtx, EQ, NULL_RTX, - GET_MODE (call_rtx), 0, 0, - catchstack.top->entry->false_label); - } -} - -/* Called to end a catch clause. If we aren't using the new exception - model tabel mechanism, we need to issue the branch-around label - for the end of the catch block. */ - -void -end_catch_handler () -{ - if (! doing_eh (1)) - return; - - if (flag_new_exceptions && ! exceptions_via_longjmp) - { - emit_barrier (); - return; - } - - /* A NULL label implies the catch clause was a catch all or cleanup */ - if (catchstack.top->entry->false_label == NULL_RTX) - return; - - emit_label (catchstack.top->entry->false_label); - catchstack.top->entry->false_label = NULL_RTX; -} - -/* 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 () -{ - struct eh_entry *entry; - tree label; - rtx outer_context; - - if (! doing_eh (1)) - return; - - outer_context = ehstack.top->entry->outer_context; - - /* 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 that we will save. - This is Lresume in the documentation. */ - expand_label (label); - - /* 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 global sequence catch_clauses when we have completed all - the handlers in this handler-seq. */ - start_sequence (); - - entry = dequeue_eh_entry (&ehqueue); - for ( ; entry->finalization != integer_zero_node; - entry = dequeue_eh_entry (&ehqueue)) - { - rtx prev; - - /* Emit the label for the cleanup 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. */ - - receive_exception_label (entry->exception_handler_label); - - /* register a handler for this cleanup region */ - add_new_handler ( - find_func_region (CODE_LABEL_NUMBER (entry->exception_handler_label)), - get_new_handler (entry->exception_handler_label, NULL)); - - /* And now generate the insns for the cleanup handler. */ - expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); - - prev = get_last_insn (); - if (prev == NULL || GET_CODE (prev) != BARRIER) - /* 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. */ - expand_rethrow (entry->outer_context); - - do_pending_stack_adjust (); - free (entry); - } - - /* At this point, all the cleanups are done, and the ehqueue now has - the current exception region at its head. We dequeue it, and put it - on the catch stack. */ - - push_entry (&catchstack, entry); - - /* If we are not doing setjmp/longjmp EH, because we are reordered - out of line, we arrange to rethrow in the outer context. We need to - do this because we are not physically within the region, if any, that - logically contains this catch block. */ - if (! exceptions_via_longjmp) - { - expand_eh_region_start (); - ehstack.top->entry->outer_context = outer_context; - } - -} - -/* 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 () -{ - rtx new_catch_clause; - struct eh_entry *entry; - - if (! doing_eh (1)) - return; - - /* Dequeue the current catch clause region. */ - entry = pop_eh_entry (&catchstack); - free (entry); - - if (! exceptions_via_longjmp) - { - rtx outer_context = ehstack.top->entry->outer_context; - - /* Finish the rethrow region. size_zero_node is just a NOP. */ - expand_eh_region_end (size_zero_node); - /* New exceptions handling models will never have a fall through - of a catch clause */ - if (!flag_new_exceptions) - expand_rethrow (outer_context); - } - else - expand_rethrow (NULL_RTX); - - /* 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. */ - - /* Now we have the complete catch sequence. */ - new_catch_clause = get_insns (); - end_sequence (); - - /* This level of catch blocks is done, so set up the successful - catch jump label for the next layer of catch blocks. */ - pop_label_entry (&caught_return_label_stack); - pop_label_entry (&outer_context_label_stack); - - /* Add the new sequence of catches to the main one for this function. */ - push_to_sequence (catch_clauses); - emit_insns (new_catch_clause); - catch_clauses = get_insns (); - end_sequence (); - - /* Here we fall through into the continuation code. */ -} - -/* Rethrow from the outer context LABEL. */ - -static void -expand_rethrow (label) - rtx label; -{ - if (exceptions_via_longjmp) - emit_throw (); - else - if (flag_new_exceptions) - { - rtx insn, val; - if (label == NULL_RTX) - label = last_rethrow_symbol; - emit_library_call (rethrow_libfunc, 0, VOIDmode, 1, label, Pmode); - SYMBOL_REF_USED (label) = 1; - - /* Search backwards for the actual call insn. */ - insn = get_last_insn (); - while (GET_CODE (insn) != CALL_INSN) - insn = PREV_INSN (insn); - delete_insns_since (insn); - - /* Mark the label/symbol on the call. */ - val = GEN_INT (eh_region_from_symbol (label)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, val, - REG_NOTES (insn)); - emit_barrier (); - } - else - emit_jump (label); -} - -/* End all the pending exception regions on protect_list. The handlers - will be emitted when expand_leftover_cleanups is invoked. */ - -void -end_protect_partials () -{ - while (protect_list) - { - expand_eh_region_end (TREE_VALUE (protect_list)); - 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; -} - -/* The exception table that we build that is used for looking up and - 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 = NULL; -static int eh_table_size = 0; -static int eh_table_max_size = 0; - -/* Note the need for an exception table entry for region N. If we - 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. - (Or NOTE_INSN_EH_REGION_END sometimes) - 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) - int n; -{ -#ifndef OMIT_EH_TABLE - if (eh_table_size >= eh_table_max_size) - { - if (eh_table) - { - eh_table_max_size += eh_table_max_size>>1; - - if (eh_table_max_size < 0) - abort (); - - eh_table = (int *) xrealloc (eh_table, - eh_table_max_size * sizeof (int)); - } - else - { - eh_table_max_size = 252; - eh_table = (int *) xmalloc (eh_table_max_size * sizeof (int)); - } - } - eh_table[eh_table_size++] = n; -#endif -} - -/* 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 () -{ - if (eh_table) - return 1; - - return 0; -} - -/* Output the entry of the exception table corresponding 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) - FILE *file; - int n; -{ - char buf[256]; - rtx sym; - struct handler_info *handler = get_first_handler (n); - int index = find_func_region (n); - rtx rethrow; - - /* form and emit the rethrow label, if needed */ - rethrow = function_eh_regions[index].rethrow_label; - if (rethrow != NULL_RTX && !flag_new_exceptions) - rethrow = NULL_RTX; - if (rethrow != NULL_RTX && handler == NULL) - if (! SYMBOL_REF_USED (rethrow)) - rethrow = NULL_RTX; - - - for ( ; handler != NULL || rethrow != NULL_RTX; handler = handler->next) - { - /* rethrow label should indicate the LAST entry for a region */ - if (rethrow != NULL_RTX && (handler == NULL || handler->next == NULL)) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", n); - assemble_label(buf); - rethrow = NULL_RTX; - } - - ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - - ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - - if (handler == NULL) - assemble_integer (GEN_INT (0), POINTER_SIZE / BITS_PER_UNIT, 1); - else - { - ASM_GENERATE_INTERNAL_LABEL (buf, "L", handler->handler_number); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - } - - if (flag_new_exceptions) - { - if (handler == NULL || handler->type_info == NULL) - assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - else - if (handler->type_info == CATCH_ALL_TYPE) - assemble_integer (GEN_INT (CATCH_ALL_TYPE), - POINTER_SIZE / BITS_PER_UNIT, 1); - else - output_constant ((tree)(handler->type_info), - POINTER_SIZE / BITS_PER_UNIT); - } - putc ('\n', file); /* blank line */ - /* We only output the first label under the old scheme */ - if (! flag_new_exceptions || handler == NULL) - break; - } -} - -/* Output the exception table if we have and need one. */ - -static short language_code = 0; -static short version_code = 0; - -/* This routine will set the language code for exceptions. */ -void -set_exception_lang_code (code) - int code; -{ - language_code = code; -} - -/* This routine will set the language version code for exceptions. */ -void -set_exception_version_code (code) - int code; -{ - version_code = code; -} - - -void -output_exception_table () -{ - int i; - char buf[256]; - extern FILE *asm_out_file; - - if (! doing_eh (0) || ! eh_table) - return; - - exception_section (); - - /* Beginning marker for table. */ - assemble_align (GET_MODE_ALIGNMENT (ptr_mode)); - assemble_label ("__EXCEPTION_TABLE__"); - - if (flag_new_exceptions) - { - assemble_integer (GEN_INT (NEW_EH_RUNTIME), - POINTER_SIZE / BITS_PER_UNIT, 1); - assemble_integer (GEN_INT (language_code), 2 , 1); - assemble_integer (GEN_INT (version_code), 2 , 1); - - /* Add enough padding to make sure table aligns on a pointer boundry. */ - i = GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT - 4; - for ( ; i < 0; i = i + GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT) - ; - if (i != 0) - assemble_integer (const0_rtx, i , 1); - - /* Generate the label for offset calculations on rethrows */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0); - assemble_label(buf); - } - - for (i = 0; i < eh_table_size; ++i) - output_exception_table_entry (asm_out_file, eh_table[i]); - - free (eh_table); - clear_function_eh_region (); - - /* Ending marker for table. */ - /* Generate the label for end of table. */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow)); - assemble_label(buf); - assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - - /* for binary compatability, the old __throw checked the second - position for a -1, so we should output at least 2 -1's */ - if (! flag_new_exceptions) - assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - - putc ('\n', asm_out_file); /* blank line */ -} - -/* Emit code to get EH context. - - We have to scan thru the code to find possible EH context registers. - Inlined functions may use it too, and thus we'll have to be able - to change them too. - - This is done only if using exceptions_via_longjmp. */ - -void -emit_eh_context () -{ - rtx insn; - rtx ehc = 0; - - if (! doing_eh (0)) - return; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == USE) - { - rtx reg = find_reg_note (insn, REG_EH_CONTEXT, 0); - if (reg) - { - rtx insns; - - start_sequence (); - - /* If this is the first use insn, emit the call here. This - will always be at the top of our function, because if - expand_inline_function notices a REG_EH_CONTEXT note, it - adds a use insn to this function as well. */ - if (ehc == 0) - ehc = call_get_eh_context (); - - emit_move_insn (XEXP (reg, 0), ehc); - insns = get_insns (); - end_sequence (); - - emit_insns_before (insns, insn); - - /* At -O0, we must make the context register stay alive so - that the stupid.c register allocator doesn't get confused. */ - if (obey_regdecls != 0) - { - insns = gen_rtx_USE (GET_MODE (XEXP (reg,0)), XEXP (reg,0)); - emit_insn_before (insns, get_last_insn ()); - } - } - } -} - -/* 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 () -{ - rtx insn; - - exception_handler_labels = NULL_RTX; - - /* If we aren't doing exception handling, there isn't much to check. */ - if (! doing_eh (0)) - return; - - /* For each start of a region, add its label to the list. */ - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - struct handler_info* ptr; - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - ptr = get_first_handler (NOTE_BLOCK_NUMBER (insn)); - for ( ; ptr; ptr = ptr->next) - { - /* make sure label isn't in the list already */ - rtx x; - for (x = exception_handler_labels; x; x = XEXP (x, 1)) - if (XEXP (x, 0) == ptr->handler_label) - break; - if (! x) - exception_handler_labels = gen_rtx_EXPR_LIST (VOIDmode, - ptr->handler_label, exception_handler_labels); - } - } - } -} - -/* Return a value of 1 if the parameter label number is an exception handler - label. Return 0 otherwise. */ - -int -is_exception_handler_label (lab) - int lab; -{ - rtx x; - for (x = exception_handler_labels ; x ; x = XEXP (x, 1)) - if (lab == CODE_LABEL_NUMBER (XEXP (x, 0))) - return 1; - return 0; -} - -/* 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 () -{ - rtx insn, insn2; - - /* If we aren't doing exception handling, there isn't much to check. */ - if (! doing_eh (0)) - return; - - /* Make sure there is no more than 1 copy of a label */ - for (insn = exception_handler_labels; insn; insn = XEXP (insn, 1)) - { - int count = 0; - for (insn2 = exception_handler_labels; insn2; insn2 = XEXP (insn2, 1)) - if (XEXP (insn, 0) == XEXP (insn2, 0)) - count++; - if (count != 1) - warning ("Counted %d copies of EH region %d in list.\n", count, - CODE_LABEL_NUMBER (insn)); - } - -} - -/* 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, and saves and restores the - exception handling data structures for the start/end of a nested - function. */ - -/* Toplevel initialization for EH things. */ - -void -init_eh () -{ - first_rethrow_symbol = create_rethrow_ref (0); - final_rethrow = gen_exception_label (); - last_rethrow_symbol = create_rethrow_ref (CODE_LABEL_NUMBER (final_rethrow)); -} - -/* Initialize the per-function EH information. */ - -void -init_eh_for_function () -{ - ehstack.top = 0; - catchstack.top = 0; - ehqueue.head = ehqueue.tail = 0; - catch_clauses = NULL_RTX; - false_label_stack = 0; - caught_return_label_stack = 0; - protect_list = NULL_TREE; - current_function_ehc = NULL_RTX; - eh_return_context = NULL_RTX; - eh_return_stack_adjust = NULL_RTX; - eh_return_handler = NULL_RTX; - eh_return_stub_label = NULL_RTX; -} - -/* 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; -{ - if (p == NULL) - abort (); - - p->ehstack = ehstack; - p->catchstack = catchstack; - 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->ehc = current_function_ehc; - p->eh_return_stub_label = eh_return_stub_label; - - init_eh_for_function (); -} - -/* 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; -{ - if (p == NULL) - abort (); - - 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; - catchstack = p->catchstack; - current_function_ehc = p->ehc; - eh_return_stub_label = p->eh_return_stub_label; -} - -/* This section is for the exception handling specific optimization - pass. First are the internal routines, and then the main - optimization pass. */ - -/* Determine if the given INSN can throw an exception. */ - -static int -can_throw (insn) - rtx insn; -{ - /* Calls can always potentially throw exceptions. */ - if (GET_CODE (insn) == CALL_INSN) - return 1; - - 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 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) - rtx insn; - int n; - int *delete_outer; -{ - rtx start = insn; - - /* Assume we can delete the region. */ - int delete = 1; - - int r = find_func_region (n); - /* Can't delete something which is rethrown to. */ - if (SYMBOL_REF_USED((function_eh_regions[r].rethrow_label))) - delete = 0; - - if (insn == NULL_RTX - || GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG - || NOTE_BLOCK_NUMBER (insn) != n - || delete_outer == NULL) - abort (); - - insn = NEXT_INSN (insn); - - /* Look for the matching end. */ - while (! (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) - { - /* If anything can throw, we can't remove the region. */ - if (delete && can_throw (insn)) - { - delete = 0; - } - - /* Watch out for and handle nested regions. */ - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &delete); - } - - insn = NEXT_INSN (insn); - } - - /* The _BEG/_END NOTEs must match and nest. */ - if (NOTE_BLOCK_NUMBER (insn) != n) - abort (); - - /* If anything in this exception region can throw, we can throw. */ - if (! delete) - *delete_outer = 0; - else - { - /* Delete the start and end of the region. */ - delete_insn (start); - delete_insn (insn); - -/* We no longer removed labels here, since flow will now remove any - handler which cannot be called any more. */ - -#if 0 - /* Only do this part if we have built the exception handler - labels. */ - if (exception_handler_labels) - { - rtx x, *prev = &exception_handler_labels; - - /* Find it in the list of handlers. */ - for (x = exception_handler_labels; x; x = XEXP (x, 1)) - { - rtx label = XEXP (x, 0); - if (CODE_LABEL_NUMBER (label) == n) - { - /* If we are the last reference to the handler, - delete it. */ - if (--LABEL_NUSES (label) == 0) - delete_insn (label); - - if (optimize) - { - /* Remove it from the list of exception handler - labels, if we are optimizing. If we are not, then - leave it in the list, as we are not really going to - remove the region. */ - *prev = XEXP (x, 1); - XEXP (x, 1) = 0; - XEXP (x, 0) = 0; - } - - break; - } - prev = &XEXP (x, 1); - } - } -#endif - } - return insn; -} - -/* Perform various interesting optimizations for exception handling - code. - - 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 () -{ - rtx insn; - int n; - - /* 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); - } - } -} - -/* Various hooks for the DWARF 2 __throw routine. */ - -/* Do any necessary initialization to access arbitrary stack frames. - On the SPARC, this means flushing the register windows. */ - -void -expand_builtin_unwind_init () -{ - /* Set this so all the registers get saved in our frame; we need to be - able to copy the saved values for any registers from frames we unwind. */ - current_function_has_nonlocal_label = 1; - -#ifdef SETUP_FRAME_ADDRESSES - SETUP_FRAME_ADDRESSES (); -#endif -} - -/* Given a value extracted from the return address register or stack slot, - return the actual address encoded in that value. */ - -rtx -expand_builtin_extract_return_addr (addr_tree) - tree addr_tree; -{ - rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); - return eh_outer_context (addr); -} - -/* Given an actual address in addr_tree, do any necessary encoding - and return the value to be stored in the return address register or - stack slot so the epilogue will return to that address. */ - -rtx -expand_builtin_frob_return_addr (addr_tree) - tree addr_tree; -{ - rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); -#ifdef RETURN_ADDR_OFFSET - addr = plus_constant (addr, -RETURN_ADDR_OFFSET); -#endif - return addr; -} - -/* Choose three registers for communication between the main body of - __throw and the epilogue (or eh stub) and the exception handler. - We must do this with hard registers because the epilogue itself - will be generated after reload, at which point we may not reference - pseudos at all. - - The first passes the exception context to the handler. For this - we use the return value register for a void*. - - The second holds the stack pointer value to be restored. For - this we use the static chain register if it exists and is different - from the previous, otherwise some arbitrary call-clobbered register. - - The third holds the address of the handler itself. Here we use - some arbitrary call-clobbered register. */ - -static void -eh_regs (pcontext, psp, pra, outgoing) - rtx *pcontext, *psp, *pra; - int outgoing; -{ - rtx rcontext, rsp, rra; - int i; - -#ifdef FUNCTION_OUTGOING_VALUE - if (outgoing) - rcontext = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node), - current_function_decl); - else -#endif - rcontext = FUNCTION_VALUE (build_pointer_type (void_type_node), - current_function_decl); - -#ifdef STATIC_CHAIN_REGNUM - if (outgoing) - rsp = static_chain_incoming_rtx; - else - rsp = static_chain_rtx; - if (REGNO (rsp) == REGNO (rcontext)) -#endif /* STATIC_CHAIN_REGNUM */ - rsp = NULL_RTX; - - if (rsp == NULL_RTX) - { - for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) - if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (rcontext)) - break; - if (i == FIRST_PSEUDO_REGISTER) - abort(); - - rsp = gen_rtx_REG (Pmode, i); - } - - for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) - if (call_used_regs[i] && ! fixed_regs[i] - && i != REGNO (rcontext) && i != REGNO (rsp)) - break; - if (i == FIRST_PSEUDO_REGISTER) - abort(); - - rra = gen_rtx_REG (Pmode, i); - - *pcontext = rcontext; - *psp = rsp; - *pra = rra; -} - -/* Retrieve the register which contains the pointer to the eh_context - structure set the __throw. */ - -rtx -get_reg_for_handler () -{ - rtx reg1; - reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node), - current_function_decl); - return reg1; -} - -/* Set up the epilogue with the magic bits we'll need to return to the - exception handler. */ - -void -expand_builtin_eh_return (context, stack, handler) - tree context, stack, handler; -{ - if (eh_return_context) - error("Duplicate call to __builtin_eh_return"); - - eh_return_context - = copy_to_reg (expand_expr (context, NULL_RTX, VOIDmode, 0)); - eh_return_stack_adjust - = copy_to_reg (expand_expr (stack, NULL_RTX, VOIDmode, 0)); - eh_return_handler - = copy_to_reg (expand_expr (handler, NULL_RTX, VOIDmode, 0)); -} - -void -expand_eh_return () -{ - rtx reg1, reg2, reg3; - rtx stub_start, after_stub; - rtx ra, tmp; - - if (!eh_return_context) - return; - - current_function_cannot_inline = N_("function uses __builtin_eh_return"); - - eh_regs (®1, ®2, ®3, 1); -#ifdef POINTERS_EXTEND_UNSIGNED - eh_return_context = convert_memory_address (Pmode, eh_return_context); - eh_return_stack_adjust = - convert_memory_address (Pmode, eh_return_stack_adjust); - eh_return_handler = convert_memory_address (Pmode, eh_return_handler); -#endif - emit_move_insn (reg1, eh_return_context); - emit_move_insn (reg2, eh_return_stack_adjust); - emit_move_insn (reg3, eh_return_handler); - - /* Talk directly to the target's epilogue code when possible. */ - -#ifdef HAVE_eh_epilogue - if (HAVE_eh_epilogue) - { - emit_insn (gen_eh_epilogue (reg1, reg2, reg3)); - return; - } -#endif - - /* Otherwise, use the same stub technique we had before. */ - - eh_return_stub_label = stub_start = gen_label_rtx (); - after_stub = gen_label_rtx (); - - /* Set the return address to the stub label. */ - - ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0, hard_frame_pointer_rtx); - if (GET_CODE (ra) == REG && REGNO (ra) >= FIRST_PSEUDO_REGISTER) - abort(); - - tmp = memory_address (Pmode, gen_rtx_LABEL_REF (Pmode, stub_start)); -#ifdef RETURN_ADDR_OFFSET - tmp = plus_constant (tmp, -RETURN_ADDR_OFFSET); -#endif - tmp = force_operand (tmp, ra); - if (tmp != ra) - emit_move_insn (ra, tmp); - - /* Indicate that the registers are in fact used. */ - emit_insn (gen_rtx_USE (VOIDmode, reg1)); - emit_insn (gen_rtx_USE (VOIDmode, reg2)); - emit_insn (gen_rtx_USE (VOIDmode, reg3)); - if (GET_CODE (ra) == REG) - emit_insn (gen_rtx_USE (VOIDmode, ra)); - - /* Generate the stub. */ - - emit_jump (after_stub); - emit_label (stub_start); - - eh_regs (®1, ®2, ®3, 0); - adjust_stack (reg2); - emit_indirect_jump (reg3); - - emit_label (after_stub); -} - - -/* This contains the code required to verify whether arbitrary instructions - are in the same exception region. */ - -static int *insn_eh_region = (int *)0; -static int maximum_uid; - -static void -set_insn_eh_region (first, region_num) - rtx *first; - int region_num; -{ - rtx insn; - int rnum; - - for (insn = *first; insn; insn = NEXT_INSN (insn)) - { - if ((GET_CODE (insn) == NOTE) && - (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)) - { - rnum = NOTE_BLOCK_NUMBER (insn); - insn_eh_region[INSN_UID (insn)] = rnum; - insn = NEXT_INSN (insn); - set_insn_eh_region (&insn, rnum); - /* Upon return, insn points to the EH_REGION_END of nested region */ - continue; - } - insn_eh_region[INSN_UID (insn)] = region_num; - if ((GET_CODE (insn) == NOTE) && - (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) - break; - } - *first = insn; -} - -/* Free the insn table, an make sure it cannot be used again. */ - -void -free_insn_eh_region () -{ - if (!doing_eh (0)) - return; - - if (insn_eh_region) - { - free (insn_eh_region); - insn_eh_region = (int *)0; - } -} - -/* Initialize the table. max_uid must be calculated and handed into - this routine. If it is unavailable, passing a value of 0 will - cause this routine to calculate it as well. */ - -void -init_insn_eh_region (first, max_uid) - rtx first; - int max_uid; -{ - rtx insn; - - if (!doing_eh (0)) - return; - - if (insn_eh_region) - free_insn_eh_region(); - - if (max_uid == 0) - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > max_uid) /* find largest UID */ - max_uid = INSN_UID (insn); - - maximum_uid = max_uid; - insn_eh_region = (int *) malloc ((max_uid + 1) * sizeof (int)); - insn = first; - set_insn_eh_region (&insn, 0); -} - - -/* Check whether 2 instructions are within the same region. */ - -int -in_same_eh_region (insn1, insn2) - rtx insn1, insn2; -{ - int ret, uid1, uid2; - - /* If no exceptions, instructions are always in same region. */ - if (!doing_eh (0)) - return 1; - - /* If the table isn't allocated, assume the worst. */ - if (!insn_eh_region) - return 0; - - uid1 = INSN_UID (insn1); - uid2 = INSN_UID (insn2); - - /* if instructions have been allocated beyond the end, either - the table is out of date, or this is a late addition, or - something... Assume the worst. */ - if (uid1 > maximum_uid || uid2 > maximum_uid) - return 0; - - ret = (insn_eh_region[uid1] == insn_eh_region[uid2]); - return ret; -} - |