summaryrefslogtreecommitdiffstats
path: root/contrib/gcc/except.c
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>1999-08-26 09:30:50 +0000
committerobrien <obrien@FreeBSD.org>1999-08-26 09:30:50 +0000
commit0bedf4fb30066e5e1d4342a1d3914dae7d37cba7 (patch)
tree68d8110b41afd0ebbf39167b1a4918eea667a7c5 /contrib/gcc/except.c
parentd4db5fb866b7ad5216abd5047774a3973b9901a9 (diff)
downloadFreeBSD-src-0bedf4fb30066e5e1d4342a1d3914dae7d37cba7.zip
FreeBSD-src-0bedf4fb30066e5e1d4342a1d3914dae7d37cba7.tar.gz
Virgin import of gcc from EGCS 1.1.2
Diffstat (limited to 'contrib/gcc/except.c')
-rw-r--r--contrib/gcc/except.c2694
1 files changed, 2694 insertions, 0 deletions
diff --git a/contrib/gcc/except.c b/contrib/gcc/except.c
new file mode 100644
index 0000000..ccd5a6b
--- /dev/null
+++ b/contrib/gcc/except.c
@@ -0,0 +1,2694 @@
+/* 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"
+
+/* 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;
+
+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 *, 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. */
+
+/* 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;
+
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ lab = gen_label_rtx ();
+ pop_obstacks ();
+ 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));
+
+ entry->outer_context = gen_label_rtx ();
+ entry->finalization = NULL_TREE;
+ entry->label_used = 0;
+ entry->exception_handler_label = gen_exception_label ();
+
+ 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 */
+ 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. The number
+ returned is an number which uniquely identifies this exception range. */
+
+int
+new_eh_region_entry (note_eh_region)
+ int note_eh_region;
+{
+ 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;
+ 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->next != NULL; last = last->next)
+ ;
+ 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->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. */
+
+int
+duplicate_handlers (old_note_eh_region, new_note_eh_region)
+ int old_note_eh_region, new_note_eh_region;
+{
+ struct handler_info *ptr, *new_ptr;
+ int new_region, region;
+
+ region = find_func_region (old_note_eh_region);
+ if (region == -1)
+ error ("Cannot duplicate non-existant exception region.");
+
+ if (find_func_region (new_note_eh_region) != -1)
+ error ("Cannot duplicate EH region because new note region already exists");
+
+ new_region = new_eh_region_entry (new_note_eh_region);
+ ptr = function_eh_regions[region].handlers;
+
+ for ( ; ptr; ptr = ptr->next)
+ {
+ new_ptr = get_new_handler (ptr->handler_label, ptr->type_info);
+ add_new_handler (new_region, new_ptr);
+ }
+
+ return new_region;
+}
+
+
+/* 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, XEXP (arg, 0));
+}
+
+/* Start an exception handling region for the given cleanup action.
+ All instructions emitted after this point are considered to be part
+ of the region until expand_eh_region_end is invoked. CLEANUP is
+ the cleanup action to perform. The return value is true if the
+ exception region was optimized away. If that case,
+ expand_eh_region_end does not need to be called for this cleanup,
+ nor should it be.
+
+ This routine notices one particular common case in C++ code
+ generation, and optimizes it so as to not need the exception
+ region. It works by creating a dynamic cleanup action, instead of
+ 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;
+
+ if (! doing_eh (0))
+ return;
+
+ entry = pop_eh_entry (&ehstack);
+
+ note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END);
+ NOTE_BLOCK_NUMBER (note)
+ = CODE_LABEL_NUMBER (entry->exception_handler_label);
+ if (exceptions_via_longjmp == 0
+ /* 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 */
+ new_eh_region_entry (NOTE_BLOCK_NUMBER (note));
+
+ 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));
+}
+
+/* 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;
+ }
+
+ /* We also have to start the handler if we aren't using the new model. */
+ if (! flag_new_exceptions)
+ start_catch_handler (NULL);
+}
+
+/* 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, outer_context = NULL_RTX;
+ 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)
+ {
+ 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);
+ }
+
+ /* Code to throw out to outer context, if we fall off end of catch
+ handlers. This is rethrow (Lresume, same id, same obj) in the
+ documentation. We use Lresume because we know that it will throw
+ to the correct context.
+
+ In other words, if the catch handler doesn't exit or return, we
+ do a "throw" (using the address of Lresume as the point being
+ thrown from) so that the outer EH region can then try to process
+ the exception. */
+ expand_rethrow (outer_context);
+
+ /* 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
+ 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;
+
+ handler = get_first_handler (n);
+
+ for ( ; handler != NULL; handler = handler->next)
+ {
+ 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);
+
+ assemble_integer (handler->handler_label,
+ POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ if (flag_new_exceptions)
+ {
+ if (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 */
+ }
+}
+
+/* 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)
+ short code;
+{
+ language_code = code;
+}
+
+/* This routine will set the language version code for exceptions. */
+void set_exception_version_code (code)
+ short code;
+{
+ version_code = code;
+}
+
+
+void
+output_exception_table ()
+{
+ int i;
+ 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);
+ }
+
+ 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. */
+ 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);
+ }
+ }
+}
+
+/* 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 ()
+{
+}
+
+/* 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;
+}
+
+/* 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;
+
+ 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;
+}
+
+/* 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;
+
+ 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;
+}
+
+/* Given an actual address in addr_tree, set the return address register up
+ so the epilogue will return to that address. If the return address is
+ not in a register, do nothing. */
+
+void
+expand_builtin_set_return_addr_reg (addr_tree)
+ tree addr_tree;
+{
+ rtx tmp;
+ rtx ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0, hard_frame_pointer_rtx);
+
+ if (GET_CODE (ra) != REG || REGNO (ra) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ tmp = force_operand (expand_builtin_frob_return_addr (addr_tree), ra);
+ if (tmp != ra)
+ emit_move_insn (ra, tmp);
+}
+
+/* Choose two registers for communication between the main body of
+ __throw and the stub for adjusting the stack pointer. The first register
+ is used to pass the address of the exception handler; the second register
+ is used to pass the stack pointer offset.
+
+ For register 1 we use the return value register for a void *.
+ For register 2 we use the static chain register if it exists and is
+ different from register 1, otherwise some arbitrary call-clobbered
+ register. */
+
+static void
+eh_regs (r1, r2, outgoing)
+ rtx *r1, *r2;
+ int outgoing;
+{
+ rtx reg1, reg2;
+
+#ifdef FUNCTION_OUTGOING_VALUE
+ if (outgoing)
+ reg1 = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node),
+ current_function_decl);
+ else
+#endif
+ reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node),
+ current_function_decl);
+
+#ifdef STATIC_CHAIN_REGNUM
+ if (outgoing)
+ reg2 = static_chain_incoming_rtx;
+ else
+ reg2 = static_chain_rtx;
+ if (REGNO (reg2) == REGNO (reg1))
+#endif /* STATIC_CHAIN_REGNUM */
+ reg2 = NULL_RTX;
+
+ if (reg2 == NULL_RTX)
+ {
+ int i;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
+ if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (reg1))
+ {
+ reg2 = gen_rtx_REG (Pmode, i);
+ break;
+ }
+
+ if (reg2 == NULL_RTX)
+ abort ();
+ }
+
+ *r1 = reg1;
+ *r2 = reg2;
+}
+
+
+/* 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;
+}
+
+
+/* Emit inside of __throw a stub which adjusts the stack pointer and jumps
+ to the exception handler. __throw will set up the necessary values
+ and then return to the stub. */
+
+rtx
+expand_builtin_eh_stub_old ()
+{
+ rtx stub_start = gen_label_rtx ();
+ rtx after_stub = gen_label_rtx ();
+ rtx handler, offset;
+
+ emit_jump (after_stub);
+ emit_label (stub_start);
+
+ eh_regs (&handler, &offset, 0);
+
+ adjust_stack (offset);
+ emit_indirect_jump (handler);
+ emit_label (after_stub);
+ return gen_rtx_LABEL_REF (Pmode, stub_start);
+}
+
+rtx
+expand_builtin_eh_stub ()
+{
+ rtx stub_start = gen_label_rtx ();
+ rtx after_stub = gen_label_rtx ();
+ rtx handler, offset;
+ rtx temp;
+
+ emit_jump (after_stub);
+ emit_label (stub_start);
+
+ eh_regs (&handler, &offset, 0);
+
+ adjust_stack (offset);
+
+ /* Handler is in fact a pointer to the _eh_context structure, we need
+ to pick out the handler field (first element), and jump to there,
+ leaving the pointer to _eh_conext in the same hardware register. */
+
+ temp = gen_rtx_MEM (Pmode, handler);
+ MEM_IN_STRUCT_P (temp) = 1;
+ RTX_UNCHANGING_P (temp) = 1;
+ emit_move_insn (offset, temp);
+ emit_insn (gen_rtx_USE (Pmode, handler));
+
+ emit_indirect_jump (offset);
+
+ emit_label (after_stub);
+ return gen_rtx_LABEL_REF (Pmode, stub_start);
+}
+
+/* Set up the registers for passing the handler address and stack offset
+ to the stub above. */
+
+void
+expand_builtin_set_eh_regs (handler, offset)
+ tree handler, offset;
+{
+ rtx reg1, reg2;
+
+ eh_regs (&reg1, &reg2, 1);
+
+ store_expr (offset, reg2, 0);
+ store_expr (handler, reg1, 0);
+
+ /* These will be used by the stub. */
+ emit_insn (gen_rtx_USE (VOIDmode, reg1));
+ emit_insn (gen_rtx_USE (VOIDmode, reg2));
+}
+
+
+
+/* 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;
+}
+
OpenPOWER on IntegriCloud