aboutsummaryrefslogtreecommitdiff
path: root/gcc/except.c
diff options
context:
space:
mode:
authorMike Stump <mrs@gcc.gnu.org>1996-07-23 20:20:04 +0000
committerMike Stump <mrs@gcc.gnu.org>1996-07-23 20:20:04 +0000
commit4956d07c7e28164b075454eddcd136dcf0b028c6 (patch)
treefef312ead7dc5b8e7329c569f02a31f62b9fc268 /gcc/except.c
parent1ef08c6373cde5339b3d4db95057ee148ea659cb (diff)
downloadgcc-4956d07c7e28164b075454eddcd136dcf0b028c6.zip
gcc-4956d07c7e28164b075454eddcd136dcf0b028c6.tar.gz
gcc-4956d07c7e28164b075454eddcd136dcf0b028c6.tar.bz2
Initial revision
From-SVN: r12551
Diffstat (limited to 'gcc/except.c')
-rw-r--r--gcc/except.c1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/gcc/except.c b/gcc/except.c
new file mode 100644
index 0000000..aee437b
--- /dev/null
+++ b/gcc/except.c
@@ -0,0 +1,1139 @@
+/* Implements exceptiom handling.
+ Copyright (C) 1989, 92-95, 1996 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. */
+
+
+/* This file contains the exception handling code for the compiler.
+
+ Exception handling is a mechanism by which complex flows of control
+ can be designated. The central concepts are the `exception region',
+ the associated `exception handler' for that region and the concept of
+ throwing an exception, and the context of the throw.
+
+ Restrictions are, the regions must be non-overlapping, they can be
+ nested, and there can be zero or more per function. For each
+ region, there is one associated handler. Regions only ever
+ surround possible context points of throws. Regions with no such
+ context points can be optimized away, as they are trivial, and it
+ is not possible for the associated handler to ever be used during a
+ throw.
+
+ Semantics are, when an exception is thrown, control is transferred
+ to a handler, and the code of the exception handler is executed.
+ As control is transferred, the machine state (stack pointer, all
+ callee saved registers and possibly the frame pointer) is restored.
+
+ The handler that is selected by a throw, is the handler associated
+ with the smallest (most nested) region that contains the context of
+ the throw, if such a region exists. If no region exists, the
+ search for a handler continues in the function that called the
+ function that contains the current context of the throw, with the
+ context of the throw then becoming that point in the code that
+ contains the call instruction.
+
+
+ One can add to the basic model the concepts of thrown exception
+ type, and thrown exception value. Semantics are as above, except a
+ further check is done when finding a handler for the thrown
+ exception to see if the given handler can handle the thrown
+ exception based upon the exception object's type and possibly its
+ value. A common optimization is when two regions are identical,
+ the handlers are combined into just one handler so the first check
+ of the resulting handler is for the inner (nested) region's
+ handler, and the second one is for the outer region's handler. To
+ separate these two notions of handlers, we can call the subhandlers
+ `catch blocks', and use the name `handler' to refer to the
+ combination of the two. Currently, this layer of functionality is
+ managed by the various front ends.
+
+
+ To mark the start of a exception handling region,
+ expand_eh_region_start () is called. To mark the end, and
+ associate a handler for the region expand_eh_region_end () is used.
+ The front end can use this interface, if useful. The back end
+ creates exception regions with these routines. Another interface
+ the front end can use, is TARGET_EXPR. TARGET_EXPR gives an
+ unwind-protect style interface a la emacs.
+
+
+ In this implementation, regions do not span more than one function.
+
+ In order to help with the task of finding the associated handler for
+ a region, an exception table is built which associates handlers
+ with regions. A 3-tuple, containing a reference to the start, the
+ end and the handler is sufficient for the exception table.
+
+ In order to help with the task of restoring callee saved registers
+ and performing other associated function exit actions, function
+ `unwinders' can be generated within those function for which a
+ generic function unwinder called __unwind_function () cannot work.
+ Whether the generic __unwind_function can work is machine dependent
+ and possibly function dependent. The macro DOESNT_NEEED_UNWINDER
+ decides if the current function being compiled needs an unwinder or
+ not.
+
+ The default is for unwinders to be used, as the default generic
+ function unwinder only calls abort (). The compiler-generated per
+ function function unwinders simply modify the context of thrown
+ exception to be that of the call site, and then arrange for control
+ to be transferred to __throw instead of the function's caller on
+ return, and then return. */
+
+
+#include "config.h"
+#include <stdio.h>
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "except.h"
+#include "function.h"
+#include "insn-flags.h"
+#include "expr.h"
+#include "insn-codes.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+
+/* List of labels use for exception handlers. Created by
+ find_exception_handler_labels for the optimization passes. */
+
+rtx exception_handler_labels;
+
+/* Nonzero means that throw was used. Used for now, because __throw
+ is emitted statically in each file. */
+
+int throw_used;
+
+/* A stack used for keeping track of the currectly active exception
+ handling region. As exceptions regions are 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
+ end regions, entries are poped. */
+
+struct eh_stack ehstack;
+
+/* A queue used for tracking which exception regions have closed, but
+ whose handlers have not yet been expanded. As we end regions, we
+ enqueue the entry onto this queue. Entries are dequeue from the
+ queue during expand_leftover_cleanups and expand_start_all_catch,
+ and the handlers for regions are expanded in groups in an effort to
+ group all the handlers together in the same region of program space
+ to improve page performance. 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. */
+
+struct eh_queue ehqueue;
+
+/* Insns for the catch clauses. */
+
+rtx catch_clauses;
+
+/* A list of actions for handlers for regions that are not yet
+ closed. */
+
+tree protect_list;
+
+/* Stacks to keep track of various labels. */
+
+/* Keeps track of the label to resume to, should one want to resume
+ the normal control flow out of a handler. Also used to rethrow
+ exceptions caught in handlers, as if they were physically emitted
+ inline. */
+
+struct label_node *caught_return_label_stack = NULL;
+
+/* A spare data area for the front end's own use. */
+
+struct label_node *false_label_stack = NULL;
+
+/* The rtx for the saved PC value. */
+
+rtx eh_saved_pc_rtx;
+
+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;
+}
+
+/* Copy an entry. */
+
+static struct eh_entry *
+copy_eh_entry (entry)
+ struct eh_entry *entry;
+{
+ struct eh_entry *newentry;
+
+ newentry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry));
+ bcopy ((char *) entry, (char *) newentry, sizeof (struct eh_entry));
+
+ return newentry;
+}
+
+/* Push an entry onto the given STACK. */
+
+static rtx
+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->start_label = gen_label_rtx ();
+ entry->end_label = gen_label_rtx ();
+ entry->exception_handler_label = gen_label_rtx ();
+ entry->finalization = NULL_TREE;
+
+ node->entry = entry;
+ node->chain = stack->top;
+ stack->top = node;
+
+ return entry->start_label;
+}
+
+/* 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;
+}
+
+/* Routine to see if exception exception handling is turned on.
+ DO_WARN is non-zero if we want to inform the user that exception
+ handling is turned off. */
+
+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 the return address in ADDR, compute the new pc to throw.
+ This has to work for the current frame of the current function, and
+ the one above it in the case of throw. */
+
+rtx
+eh_outer_context (addr)
+ rtx addr;
+{
+ /* First mask out any unwanted bits. */
+#ifdef MASK_RETURN_ADDR
+ emit_insn (gen_rtx (SET, Pmode,
+ addr,
+ gen_rtx (AND, Pmode,
+ addr, MASK_RETURN_ADDR)));
+#endif
+
+ /* Then subtract out enough to get into the prior region. If this
+ is defined, assume we don't need to subtract anything, as it is
+ already within the region. */
+#if ! defined (RETURN_ADDR_OFFSET)
+ addr = plus_constant (addr, -1);
+#endif
+
+ return addr;
+}
+
+/* Output a note marking the start of an exception handling region. */
+
+void
+expand_eh_region_start ()
+{
+ rtx note;
+
+ /* This is the old code. */
+ if (! doing_eh (0))
+ return;
+
+#if 0
+ /* Maybe do this to prevent jumping in and so on... */
+ pushlevel (0);
+#endif
+
+ note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG);
+ emit_label (push_eh_entry (&ehstack));
+ NOTE_BLOCK_NUMBER (note)
+ = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
+}
+
+/* Output a note marking the end of an exception handling region.
+ HANDLER is the the handler for the exception region. */
+
+void
+expand_eh_region_end (handler)
+ tree handler;
+{
+ rtx note;
+
+ struct eh_entry *entry;
+
+ 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);
+
+ emit_label (entry->end_label);
+
+ /* Put in something that takes up space, as otherwise the end
+ address for the EH region could have the exact same address as
+ the outer region, causing us to miss the fact that resuming
+ exception handling with this PC value would be inside the outer
+ region. */
+ emit_insn (gen_nop ());
+
+ entry->finalization = handler;
+
+ enqueue_eh_entry (&ehqueue, entry);
+
+
+#if 0
+ /* Makebe do this to prevent jumping in and so on... */
+ poplevel (1, 0, 0);
+#endif
+}
+
+/* Emit a call to __throw and note that we threw something. */
+
+static void
+emit_throw ()
+{
+#ifdef JUMP_TO_THROW
+ emit_indirect_jump (throw_libfunc);
+#else
+ emit_library_call (throw_libfunc, 0, VOIDmode, 0);
+#endif
+ throw_used = 1;
+ emit_barrier ();
+}
+
+/* An internal throw with an indirect CONTEXT we want to throw from. */
+
+void
+expand_internal_throw_indirect (context)
+ rtx context;
+{
+ emit_move_insn (eh_saved_pc_rtx, context);
+ emit_throw ();
+}
+
+/* An internal throw with a direct CONTEXT we want to throw from. The
+ context should be a label. */
+
+void
+expand_internal_throw (context)
+ rtx context;
+{
+ expand_internal_throw_indirect (gen_rtx (LABEL_REF, Pmode, context));
+}
+
+/* Called from expand_exception_blocks and expand_end_catch_block to
+ expand any pending handlers. */
+
+void
+expand_leftover_cleanups ()
+{
+ struct eh_entry *entry;
+
+ while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
+ {
+ rtx prev;
+
+ emit_label (entry->exception_handler_label);
+
+ expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+
+ prev = get_last_insn ();
+ if (! (prev && GET_CODE (prev) == BARRIER))
+ {
+ /* The below can be optimized away, and we could just fall into the
+ next EH handler, if we are certain they are nested. */
+ /* Code to throw out to outer context, if we fall off end of the
+ handler. */
+ expand_internal_throw (entry->end_label);
+ }
+
+ /* leftover try block, opps. */
+ if (entry->finalization == integer_zero_node)
+ abort ();
+
+ free (entry);
+ }
+}
+
+/* Generate RTL for the start of all the catch blocks. Used for
+ arranging for the exception handling code to be placed farther out
+ of line than normal. */
+
+void
+expand_start_all_catch ()
+{
+ struct eh_entry *entry;
+ tree label;
+
+ if (! doing_eh (1))
+ return;
+
+ emit_line_note (input_filename, lineno);
+ label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ /* The label for the exception handling block we will save. This is
+ Lresume, in the documention. */
+ expand_label (label);
+
+ /* Put in something that takes up space, as otherwise the end
+ address for the EH region could have the exact same address as
+ the outer region, causing us to miss the fact that resuming
+ exception handling with this PC value would be inside the outer
+ region. */
+ emit_insn (gen_nop ());
+
+ 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 gloabl sequence catch_clauses, when we have completed all
+ the handlers in this handler-seq. */
+ start_sequence ();
+
+ while (1)
+ {
+ rtx prev;
+
+ entry = dequeue_eh_entry (&ehqueue);
+ emit_label (entry->exception_handler_label);
+
+ expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+
+ /* When we get down to the matching entry, stop. */
+ if (entry->finalization == integer_zero_node)
+ break;
+
+ prev = get_last_insn ();
+ if (! (prev && GET_CODE (prev) == BARRIER))
+ {
+ /* The below can be optimized away, and we could just fall into the
+ next EH handler, if we are certain they are nested. */
+ /* Code to throw out to outer context, if we fall off end of the
+ handler. */
+ expand_internal_throw (entry->end_label);
+ }
+
+ free (entry);
+ }
+}
+
+/* Generate RTL for the end of all the catch blocks. */
+
+void
+expand_end_all_catch ()
+{
+ rtx new_catch_clause;
+
+ if (! doing_eh (1))
+ return;
+
+ /* 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. */
+ expand_internal_throw (DECL_RTL (top_label_entry (&caught_return_label_stack)));
+
+ /* 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);
+
+ /* 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. */
+}
+
+/* End all the pending exception regions from protect_list that have
+ been started, but not yet completed. */
+
+void
+end_protect_partials ()
+{
+ while (protect_list)
+ {
+ expand_eh_region_end (TREE_VALUE (protect_list));
+ protect_list = TREE_CHAIN (protect_list);
+ }
+}
+
+/* The exception table that we build that is used for looking up and
+ dispatching exceptions, it's size, and it's maximum size before we
+ have to extend it. */
+static int *eh_table;
+static int eh_table_size;
+static int eh_table_max_size;
+
+/* Note the need for an exception table entry for region N. If we
+ don't need to output an explicit exception table, avoid all the
+ extra work. Called during final_scan_insn time. */
+
+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 ();
+
+ if ((eh_table = (int *) realloc (eh_table, eh_table_max_size))
+ == 0)
+ fatal ("virtual memory exhausted");
+ }
+ else
+ {
+ eh_table_max_size = 252;
+ eh_table = (int *) xmalloc (eh_table_max_size * sizeof (int));
+ }
+ }
+ eh_table[eh_table_size++] = n;
+#endif
+}
+
+/* Conditional to test to see if we need to output an exception table.
+ Note, 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 an entry N for the exception table to the specified FILE. */
+
+static void
+output_exception_table_entry (file, n)
+ FILE *file;
+ int n;
+{
+ char buf[256];
+ rtx sym;
+
+ 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);
+
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", n);
+ sym = gen_rtx (SYMBOL_REF, Pmode, buf);
+ assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ putc ('\n', file); /* blank line */
+}
+
+/* Output the exception table if we have one and need one. */
+
+void
+output_exception_table ()
+{
+ int i;
+ extern FILE *asm_out_file;
+
+ if (! doing_eh (0))
+ return;
+
+ exception_section ();
+
+ /* Beginning marker for table. */
+ assemble_align (GET_MODE_ALIGNMENT (ptr_mode));
+ assemble_label ("__EXCEPTION_TABLE__");
+
+ assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ putc ('\n', asm_out_file); /* blank line */
+
+ for (i = 0; i < eh_table_size; ++i)
+ output_exception_table_entry (asm_out_file, eh_table[i]);
+
+ free (eh_table);
+
+ /* Ending marker for table. */
+ assemble_label ("__EXCEPTION_END__");
+ assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ putc ('\n', asm_out_file); /* blank line */
+}
+
+/* Generate code to initialize the exception table at program startup
+ time. */
+
+void
+register_exception_table ()
+{
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__register_exceptions"), 0,
+ VOIDmode, 1,
+ gen_rtx (SYMBOL_REF, Pmode, "__EXCEPTION_TABLE__"),
+ Pmode);
+}
+
+/* Emit the RTL for the start of the per function unwinder for the
+ current function. */
+
+void
+start_eh_unwinder ()
+{
+#ifdef DOESNT_NEED_UNWINDER
+ if (DOESNT_NEED_UNWINDER)
+ return;
+#endif
+
+ expand_eh_region_start ();
+}
+
+/* Emit the RTL for the end of the per function unwinder for the
+ current function. */
+
+void
+end_eh_unwinder ()
+{
+ tree expr;
+ rtx return_val_rtx, ret_val, label, end, insns;
+
+ if (! doing_eh (0))
+ return;
+
+#ifdef DOESNT_NEED_UNWINDER
+ if (DOESNT_NEED_UNWINDER)
+ return;
+#endif
+
+ expr = make_node (RTL_EXPR);
+ TREE_TYPE (expr) = void_type_node;
+ RTL_EXPR_RTL (expr) = const0_rtx;
+ TREE_SIDE_EFFECTS (expr) = 1;
+ start_sequence_for_rtl_expr (expr);
+
+ ret_val = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0, hard_frame_pointer_rtx);
+ return_val_rtx = copy_to_reg (ret_val);
+
+ return_val_rtx = eh_outer_context (return_val_rtx);
+
+ emit_move_insn (eh_saved_pc_rtx, return_val_rtx);
+
+#ifdef JUMP_TO_THROW
+ emit_move_insn (ret_val, throw_libfunc);
+#else
+ label = gen_label_rtx ();
+ emit_move_insn (ret_val, gen_rtx (LABEL_REF, Pmode, label));
+#endif
+
+#ifdef RETURN_ADDR_OFFSET
+ return_val_rtx = plus_constant (ret_val, -RETURN_ADDR_OFFSET);
+ if (return_val_rtx != ret_val)
+ emit_move_insn (ret_val, return_val_rtx);
+#endif
+
+ end = gen_label_rtx ();
+ emit_jump (end);
+
+ RTL_EXPR_SEQUENCE (expr) = get_insns ();
+ end_sequence ();
+ expand_eh_region_end (expr);
+
+ emit_jump (end);
+
+#ifndef JUMP_TO_THROW
+ emit_label (label);
+ emit_throw ();
+#endif
+
+ expand_leftover_cleanups ();
+
+ emit_label (end);
+}
+
+/* Emit the RTL for the per function unwinder for the current
+ function, if needed. Called after all the code that needs unwind
+ protection is output. */
+
+void
+emit_unwinder ()
+{
+ rtx insns;
+
+ start_sequence ();
+ start_eh_unwinder ();
+ insns = get_insns ();
+ end_sequence ();
+
+ if (insns)
+ emit_insns_after (insns, get_insns ());
+
+ end_eh_unwinder ();
+}
+
+/* Scan the current insns and build a list of handler labels. 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;
+ int max_labelno = max_label_num ();
+ int min_labelno = get_first_label_num ();
+ rtx *labels;
+
+ exception_handler_labels = NULL_RTX;
+
+ /* If we aren't doing exception handling, there isn't much to check. */
+ if (! doing_eh (0))
+ return;
+
+ /* First we generate a handy reference to each label. */
+
+ labels = (rtx *) alloca ((max_labelno - min_labelno) * sizeof (rtx));
+ labels -= min_labelno;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ if (CODE_LABEL_NUMBER (insn) >= min_labelno
+ && CODE_LABEL_NUMBER (insn) < max_labelno)
+ labels[CODE_LABEL_NUMBER (insn)] = insn;
+ }
+
+ /* Then for each start of a region, we add its label to the list. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+ {
+ rtx label = NULL_RTX;
+
+ if (NOTE_BLOCK_NUMBER (insn) >= min_labelno
+ && NOTE_BLOCK_NUMBER (insn) < max_labelno)
+ {
+ label = labels[NOTE_BLOCK_NUMBER (insn)];
+
+ if (label)
+ exception_handler_labels
+ = gen_rtx (EXPR_LIST, VOIDmode,
+ label, exception_handler_labels);
+ else
+ warning ("didn't find handler for EH region %d",
+ NOTE_BLOCK_NUMBER (insn));
+ }
+ else
+ warning ("mismatched EH region %d", NOTE_BLOCK_NUMBER (insn));
+ }
+ }
+}
+
+/* Do some 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, handler;
+
+ /* If we aren't doing exception handling, there isn't much to check. */
+ if (! doing_eh (0))
+ return;
+
+ for (handler = exception_handler_labels;
+ handler;
+ handler = XEXP (handler, 1))
+ {
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ if (CODE_LABEL_NUMBER (insn)
+ == CODE_LABEL_NUMBER (XEXP (handler, 0)))
+ {
+ if (insn != XEXP (handler, 0))
+ warning ("mismatched handler %d",
+ CODE_LABEL_NUMBER (insn));
+ break;
+ }
+ }
+ }
+ if (insn == NULL_RTX)
+ warning ("handler not found %d",
+ CODE_LABEL_NUMBER (XEXP (handler, 0)));
+ }
+
+ /* Now go through, and make sure that for each region we have, that we
+ have the corresponding label. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE
+ && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG ||
+ NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
+ {
+ for (handler = exception_handler_labels;
+ handler;
+ handler = XEXP (handler, 1))
+ {
+ if (CODE_LABEL_NUMBER (XEXP (handler, 0))
+ == NOTE_BLOCK_NUMBER (insn))
+ break;
+ }
+ if (handler == NULL_RTX)
+ warning ("region exists, no handler %d",
+ NOTE_BLOCK_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, saves and restores the
+ exception handling data structures for the start/end of a nested
+ function. */
+
+/* Toplevel initialization for EH things. */
+
+void
+init_eh ()
+{
+ eh_saved_pc_rtx = gen_rtx (MEM, ptr_mode,
+ gen_rtx (SYMBOL_REF, Pmode, "__eh_pc"));
+}
+
+/* Initialize various EH things. */
+
+void
+init_eh_for_function ()
+{
+ ehstack.top = 0;
+ ehqueue.head = ehqueue.tail = 0;
+ catch_clauses = NULL_RTX;
+ false_label_stack = 0;
+ caught_return_label_stack = 0;
+ protect_list = NULL_TREE;
+}
+
+/* Save various EH things for the current function into the save area
+ denoted by P. */
+
+void
+save_eh_status (p)
+ struct function *p;
+{
+ p->ehstack = ehstack;
+ 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;
+
+ init_eh ();
+}
+
+/* Restore various EH things for the current function from the save
+ area denoted by P. */
+
+void
+restore_eh_status (p)
+ struct function *p;
+{
+ 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;
+}
+
+/* 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;
+{
+ /* The only things that can possibly throw are calls. */
+ if (GET_CODE (insn) == CALL_INSN)
+ return 1;
+
+#ifdef ASYNCH_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;
+#endif
+
+ return 0;
+}
+
+/* Scan a region, looking for a matching end, and decide if the region
+ can be removed. 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. */
+
+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;
+
+ 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 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);
+
+ /* 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);
+ }
+ }
+ }
+ return insn;
+}
+
+/* Perform various interesting optimizations for exception handling
+ code.
+
+ We find empty exception regions, and remove them. The jump
+ optimization code will remove the handler if nothing else uses it. */
+
+void
+exception_optimize ()
+{
+ rtx insn, regions = NULL_RTX;
+ int n;
+
+ /* First 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)
+ {
+ insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &n);
+ }
+ }
+}