summaryrefslogtreecommitdiff
path: root/contrib/gcc/except.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gcc/except.c')
-rw-r--r--contrib/gcc/except.c2967
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 (&reg1, &reg2, &reg3, 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 (&reg1, &reg2, &reg3, 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;
-}
-