/* Implements exception handling.
- Copyright (C) 1989, 92-96, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000 Free Software Foundation, Inc.
Contributed by Mike Stump <mrs@cygnus.com>.
This file is part of GNU CC.
throw. On machines that have unwind info support, __throw is generated
by code in libgcc2.c, otherwise __throw is generated on a
per-object-file basis for each source file compiled with
- -fexceptions by the the C++ frontend. Before __throw is invoked,
+ -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.
#include "config.h"
#include "defaults.h"
-#include <stdio.h>
+#include "eh-common.h"
+#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "insn-config.h"
#include "recog.h"
#include "output.h"
-
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
+#include "toplev.h"
+#include "intl.h"
+#include "obstack.h"
+#include "ggc.h"
+#include "tm_p.h"
/* One to use setjmp/longjmp method of generating code for exception
handling. */
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;
-
-/* 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,
+/* Keeps track of the label used as the context of a throw to rethrow an
+ exception to the outer exception region. */
- 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 label_node *outer_context_label_stack = NULL;
-static struct eh_queue ehqueue;
+/* Pseudos used to hold exception return data in the interim between
+ __builtin_eh_return and the end of the function. */
-/* Insns for all of the exception handlers for the current function.
- They are currently emitted by the frontend code. */
+static rtx eh_return_context;
+static rtx eh_return_stack_adjust;
+static rtx eh_return_handler;
-rtx catch_clauses;
+/* 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. */
-/* 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 rtx first_rethrow_symbol = NULL_RTX;
+static rtx final_rethrow = NULL_RTX;
+static rtx last_rethrow_symbol = NULL_RTX;
-static tree protect_list;
-/* Stacks to keep track of various labels. */
+/* Prototypes for local functions. */
-/* 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). */
+static void push_eh_entry PARAMS ((struct eh_stack *));
+static struct eh_entry * pop_eh_entry PARAMS ((struct eh_stack *));
+static void enqueue_eh_entry PARAMS ((struct eh_queue *, struct eh_entry *));
+static struct eh_entry * dequeue_eh_entry PARAMS ((struct eh_queue *));
+static rtx call_get_eh_context PARAMS ((void));
+static void start_dynamic_cleanup PARAMS ((tree, tree));
+static void start_dynamic_handler PARAMS ((void));
+static void expand_rethrow PARAMS ((rtx));
+static void output_exception_table_entry PARAMS ((FILE *, int));
+static rtx scan_region PARAMS ((rtx, int, int *));
+static void eh_regs PARAMS ((rtx *, rtx *, rtx *, int));
+static void set_insn_eh_region PARAMS ((rtx *, int));
+#ifdef DONT_USE_BUILTIN_SETJMP
+static void jumpif_rtx PARAMS ((rtx, rtx));
+#endif
+static void find_exception_handler_labels_1 PARAMS ((rtx));
+static void mark_eh_node PARAMS ((struct eh_node *));
+static void mark_eh_stack PARAMS ((struct eh_stack *));
+static void mark_eh_queue PARAMS ((struct eh_queue *));
+static void mark_tree_label_node PARAMS ((struct label_node *));
+static void mark_func_eh_entry PARAMS ((void *));
+static rtx create_rethrow_ref PARAMS ((int));
+static void push_entry PARAMS ((struct eh_stack *, struct eh_entry*));
+static void receive_exception_label PARAMS ((rtx));
+static int new_eh_region_entry PARAMS ((int, rtx));
+static int find_func_region PARAMS ((int));
+static int find_func_region_from_symbol PARAMS ((rtx));
+static void clear_function_eh_region PARAMS ((void));
+static void process_nestinfo PARAMS ((int, eh_nesting_info *, int *));
+rtx expand_builtin_return_addr PARAMS ((enum built_in_function, int, rtx));
+static void emit_cleanup_handler PARAMS ((struct eh_entry *));
+static int eh_region_from_symbol PARAMS ((rtx));
-struct label_node *caught_return_label_stack = NULL;
+\f
+/* Various support routines to manipulate the various data structures
+ used by the exception handling code. */
-/* Keeps track of the label used as the context of a throw to rethrow an
- exception to the outer exception region. */
+extern struct obstack permanent_obstack;
-struct label_node *outer_context_label_stack = NULL;
+/* Generate a SYMBOL_REF for rethrow to use */
-/* A random data area for the front end's own use. */
+static rtx
+create_rethrow_ref (region_num)
+ int region_num;
+{
+ rtx def;
+ char *ptr;
+ char buf[60];
-struct label_node *false_label_stack = NULL;
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
-rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
-static void expand_rethrow PROTO((rtx));
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", region_num);
+ ptr = ggc_alloc_string (buf, -1);
+ def = gen_rtx_SYMBOL_REF (Pmode, ptr);
+ SYMBOL_REF_NEED_ADJUST (def) = 1;
-\f
-/* Various support routines to manipulate the various data structures
- used by the exception handling code. */
+ pop_obstacks ();
+ return def;
+}
/* Push a label entry onto the given STACK. */
return (*stack)->u.tlabel;
}
+/* Get an exception label. */
+
+rtx
+gen_exception_label ()
+{
+ rtx lab;
+ lab = gen_label_rtx ();
+ return lab;
+}
+
/* Push a new eh_node entry onto STACK. */
static void
struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node));
struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry));
- entry->outer_context = gen_label_rtx ();
- entry->exception_handler_label = gen_label_rtx ();
+ 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;
+ entry->goto_entry_p = 0;
+
+ 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;
node->chain = NULL;
if (queue->head == NULL)
- {
- queue->head = node;
- }
+ queue->head = node;
else
- {
- queue->tail->chain = node;
- }
+ queue->tail->chain = node;
queue->tail = node;
}
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. */
+ int rethrow_ref; /* Is rethrow_label referenced? */
+ int emitted; /* 1 if this entry has been emitted in assembly file. */
+ 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. 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 *) xmalloc (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 *)
+ xrealloc (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;
+ function_eh_regions[current_func_eh_entry].emitted = 0;
+
+ 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;
+
+ /* If find_func_region returns -1, callers might attempt to pass us
+ this region number. If that happens, something has gone wrong;
+ -1 is never a valid region. */
+ if (region == -1)
+ abort ();
+
+ 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 **) xmalloc (max_ptr * sizeof (void *));
+
+ 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 **) xrealloc (ptr, max_ptr * sizeof (void *));
+ }
+ ptr[n_ptr] = val;
+ n_ptr++;
+ }
+ }
+ }
+
+ if (n_ptr == 0)
+ {
+ free (ptr);
+ ptr = NULL;
+ }
+ *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 *) xmalloc (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. */
+
+static 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;
+{
+ int r = find_func_region (region);
+ if (r == -1)
+ abort ();
+ return function_eh_regions[r].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);
+ }
+ if (function_eh_regions)
+ 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. */
+
+static 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;
+}
+
+/* Like find_func_region, but using the rethrow symbol for the region
+ rather than the region number itself. */
+
+static int
+find_func_region_from_symbol (sym)
+ rtx sym;
+{
+ return find_func_region (eh_region_from_symbol (sym));
+}
+
+/* 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;
+
+ if (! flag_new_exceptions)
+ return sym;
+
+ 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. */
+ function_eh_regions[x].rethrow_ref = 1;
+ }
+ return function_eh_regions[x].rethrow_label;
+ }
+ return sym;
+}
+
+/* Returns nonzero if the rethrow label for REGION is referenced
+ somewhere (i.e. we rethrow out of REGION or some other region
+ masquerading as REGION). */
+
+int
+rethrow_used (region)
+ int region;
+{
+ if (flag_new_exceptions)
+ {
+ int ret = function_eh_regions[find_func_region (region)].rethrow_ref;
+ return ret;
+ }
+ return 0;
+}
+
\f
-/* Routine to see if exception exception handling is turned on.
+/* 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.
with __terminate. */
handler = protect_with_terminate (handler);
- protect_list = tree_cons (NULL_TREE, handler, protect_list);
+ /* For backwards compatibility, we allow callers to omit calls to
+ begin_protect_partials for the outermost region. So, we must
+ explicitly do so here. */
+ if (!protect_list)
+ begin_protect_partials ();
+
+ /* Add this entry to the front of the list. */
+ TREE_VALUE (protect_list)
+ = tree_cons (NULL_TREE, handler, TREE_VALUE (protect_list));
pop_obstacks ();
}
make_decl_rtl (fn, NULL_PTR, 1);
assemble_external (fn);
pop_obstacks ();
+
+ ggc_add_tree_root (&fn, 1);
}
expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
rtx ehc, dhc, result;
ehc = get_eh_context ();
- dhc = ehc;
+
+ /* 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);
rtx dhc, dcc, result;
dhc = get_dynamic_handler_chain ();
- dcc = plus_constant (dhc, GET_MODE_SIZE (Pmode));
+ dcc = plus_constant (dhc, POINTER_SIZE / BITS_PER_UNIT);
result = copy_to_reg (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. */
-void
+static void
jumpif_rtx (x, label)
rtx x;
rtx label;
{
jumpif (make_tree (type_for_mode (GET_MODE (x), 0), x), label);
}
-
-/* Generate code to evaluate X and jump to LABEL if the value is zero.
- LABEL is an rtx of code CODE_LABEL, in this function. */
-
-void
-jumpifnot_rtx (x, label)
- rtx x;
- rtx label;
-{
- jumpifnot (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
/* Update the cleanup chain. */
- emit_move_insn (dcc, XEXP (buf, 0));
+ x = force_operand (XEXP (buf, 0), dcc);
+ if (x != dcc)
+ emit_move_insn (dcc, x);
}
/* Emit RTL to start a dynamic handler on the EH runtime dynamic
#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;
+ builtin setjmp/longjmp, see expand_builtin, case BUILT_IN_LONGJMP. */
+ /* We use 2 words here before calling expand_builtin_setjmp.
+ expand_builtin_setjmp uses 2 words, and then calls emit_stack_save.
+ emit_stack_save needs space of size STACK_SAVEAREA_MODE (SAVE_NONLOCAL).
+ Subtract one, because the assign_stack_local call below adds 1. */
+ size = (2 + 2 + (GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL))
+ / GET_MODE_SIZE (Pmode))
+ - 1);
#else
#ifdef JMP_BUF_SIZE
size = JMP_BUF_SIZE;
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,
+ x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_CONST,
+ TYPE_MODE (integer_type_node), 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);
/* We are committed to this, so update the handler chain. */
- emit_move_insn (dhc, XEXP (arg, 0));
+ emit_move_insn (dhc, force_operand (XEXP (arg, 0), NULL_RTX));
}
/* Start an exception handling region for the given cleanup action.
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
- of a using an exception region. */
+ a using an exception region. */
int
expand_eh_region_start_tree (decl, cleanup)
/* 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
+ && compare_tree_int (TREE_VALUE (args), 2) == 0
/* Make sure there are no other arguments. */
&& TREE_CHAIN (args) == NULL_TREE)
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);
+ /* We need a new block to record the start and end of the
+ dynamic handler chain. We also want to prevent jumping into
+ a try block. */
+ expand_start_bindings (2);
- /* But we don't need or want a new temporary level. */
- pop_temp_slots ();
+ /* 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 ();
+ /* 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 ();
+ if (exceptions_via_longjmp)
+ {
/* 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)
+ NOTE_EH_HANDLER (note)
= CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
if (exceptions_via_longjmp)
start_dynamic_handler ();
tree handler;
{
struct eh_entry *entry;
+ struct eh_node *node;
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);
- NOTE_BLOCK_NUMBER (note)
+ ret = NOTE_EH_HANDLER (note)
= CODE_LABEL_NUMBER (entry->exception_handler_label);
- if (exceptions_via_longjmp == 0
+ if (exceptions_via_longjmp == 0 && ! flag_new_exceptions
/* We share outer_context between regions; only emit it once. */
&& INSN_UID (entry->outer_context) == 0)
{
entry->finalization = handler;
- enqueue_eh_entry (&ehqueue, entry);
+ /* create region entry in final exception table */
+ r = new_eh_region_entry (NOTE_EH_HANDLER (note), entry->rethrow_label);
- /* If we have already started ending the bindings, don't recurse.
- This only happens when exceptions_via_longjmp is true. */
+ enqueue_eh_entry (ehqueue, entry);
+
+ /* If we have already started ending the bindings, don't recurse. */
if (is_eh_region ())
{
/* Because we don't need or want a new temporary level and
mark_block_as_not_eh_region ();
- /* Maybe do this to prevent jumping in and so on... */
expand_end_bindings (NULL_TREE, 0, 0);
}
+
+ /* Go through the goto handlers in the queue, emitting their
+ handlers if we now have enough information to do so. */
+ for (node = ehqueue->head; node; node = node->chain)
+ if (node->entry->goto_entry_p
+ && node->entry->outer_context == entry->rethrow_label)
+ emit_cleanup_handler (node->entry);
+
+ /* We can't emit handlers for goto entries until their scopes are
+ complete because we don't know where they need to rethrow to,
+ yet. */
+ if (entry->finalization != integer_zero_node
+ && (!entry->goto_entry_p
+ || find_func_region_from_symbol (entry->outer_context) != -1))
+ emit_cleanup_handler (entry);
}
/* End the EH region for a goto fixup. We only need them in the region-based
return;
expand_eh_region_start ();
+ /* Mark this entry as the entry for a goto. */
+ ehstack.top->entry->goto_entry_p = 1;
}
/* End the EH region for a goto fixup. CLEANUP is the cleanup we just
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; )
+ 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. */
+
+ if (flag_new_exceptions)
+ dont_issue = 0;
+ else
+ 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. */
+ call to __sjthrow. Otherwise, we emit a call to __throw. */
void
emit_throw ()
{
struct eh_entry *entry;
- while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
+ for (entry = dequeue_eh_entry (ehqueue);
+ entry;
+ entry = dequeue_eh_entry (ehqueue))
{
- rtx prev;
-
- /* A leftover try block. Shouldn't be one here. */
+ /* 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. */
- emit_label (entry->exception_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
-
- /* 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);
}
}
expand_eh_region_start ();
}
-/* 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 ()
+/* 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;
{
- struct eh_entry *entry;
- tree label;
- rtx outer_context;
+ rtx handler_label;
+ int insn_region_num;
+ int eh_region_entry;
if (! doing_eh (1))
return;
- outer_context = ehstack.top->entry->outer_context;
+ 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);
- /* End the try block. */
- expand_eh_region_end (integer_zero_node);
+ /* 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;
- emit_line_note (input_filename, lineno);
- label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ receive_exception_label (handler_label);
- /* 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);
+ add_new_handler (eh_region_entry, get_new_handler (handler_label, rtime));
- /* 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 ();
+ if (flag_new_exceptions && ! exceptions_via_longjmp)
+ return;
- while (1)
- {
- rtx prev;
+ /* 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. */
- entry = dequeue_eh_entry (&ehqueue);
- /* Emit the label for the exception handler for this region, and
- expand the code for the handler.
+ if (rtime != NULL_TREE && rtime != CATCH_ALL_TYPE)
+ {
+ rtx call_rtx, rtime_address;
- 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. */
- emit_label (entry->exception_handler_label);
+ if (catchstack.top->entry->false_label != NULL_RTX)
+ {
+ error ("Never issued previous false_label");
+ abort ();
+ }
+ catchstack.top->entry->false_label = gen_exception_label ();
-#ifdef HAVE_exception_receiver
- if (! exceptions_via_longjmp)
- if (HAVE_exception_receiver)
- emit_insn (gen_exception_receiver ());
+ 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,
+ LCT_NORMAL,
+ TYPE_MODE (integer_type_node),
+ 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);
+ }
+}
-#ifdef HAVE_nonlocal_goto_receiver
- if (! exceptions_via_longjmp)
- if (HAVE_nonlocal_goto_receiver)
- emit_insn (gen_nonlocal_goto_receiver ());
-#endif
+/* 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. */
- /* When we get down to the matching entry for this try block, stop. */
- if (entry->finalization == integer_zero_node)
- {
- /* Don't forget to free this entry. */
- free (entry);
- break;
- }
+void
+end_catch_handler ()
+{
+ if (! doing_eh (1))
+ return;
- /* And now generate the insns for the handler. */
- expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+ 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;
- 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);
+ emit_label (catchstack.top->entry->false_label);
+ catchstack.top->entry->false_label = NULL_RTX;
+}
- do_pending_stack_adjust ();
- free (entry);
- }
+/* Save away the current ehqueue. */
+
+void
+push_ehqueue ()
+{
+ struct eh_queue *q;
+ q = (struct eh_queue *) xcalloc (1, sizeof (struct eh_queue));
+ q->next = ehqueue;
+ ehqueue = q;
+}
+
+/* Restore a previously pushed ehqueue. */
+
+void
+pop_ehqueue ()
+{
+ struct eh_queue *q;
+ expand_leftover_cleanups ();
+ q = ehqueue->next;
+ free (ehqueue);
+ ehqueue = q;
+}
+
+/* Emit the handler specified by ENTRY. */
+
+static void
+emit_cleanup_handler (entry)
+ struct eh_entry *entry;
+{
+ rtx prev;
+ rtx handler_insns;
+
+ /* Since the cleanup could itself contain try-catch blocks, we
+ squirrel away the current queue and replace it when we are done
+ with this function. */
+ push_ehqueue ();
+
+ /* Put these handler instructions in a sequence. */
+ do_pending_stack_adjust ();
+ start_sequence ();
+
+ /* 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);
+
+ /* Finish this sequence. */
+ do_pending_stack_adjust ();
+ handler_insns = get_insns ();
+ end_sequence ();
+
+ /* And add it to the CATCH_CLAUSES. */
+ push_to_full_sequence (catch_clauses, catch_clauses_last);
+ emit_insns (handler_insns);
+ end_full_sequence (&catch_clauses, &catch_clauses_last);
+
+ /* Now we've left the handler. */
+ pop_ehqueue ();
+}
+
+/* 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 ();
+
+ /* Throw away entries in the queue that we won't need anymore. We
+ need entries for regions that have ended but to which there might
+ still be gotos pending. */
+ for (entry = dequeue_eh_entry (ehqueue);
+ entry->finalization != integer_zero_node;
+ entry = dequeue_eh_entry (ehqueue))
+ 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
expand_eh_region_start ();
ehstack.top->entry->outer_context = outer_context;
}
+
}
/* Finish up the catch block. At this point all the insns for the
void
expand_end_all_catch ()
{
- rtx new_catch_clause, outer_context = NULL_RTX;
+ 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)
{
- outer_context = ehstack.top->entry->outer_context;
+ 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
do a "throw" (using the address of Lresume as the point being
thrown from) so that the outer EH region can then try to process
the exception. */
- expand_rethrow (outer_context);
/* Now we have the complete catch sequence. */
new_catch_clause = get_insns ();
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);
+ push_to_full_sequence (catch_clauses, catch_clauses_last);
emit_insns (new_catch_clause);
- catch_clauses = get_insns ();
- end_sequence ();
+ end_full_sequence (&catch_clauses, &catch_clauses_last);
/* Here we fall through into the continuation code. */
}
if (exceptions_via_longjmp)
emit_throw ();
else
- emit_jump (label);
+ if (flag_new_exceptions)
+ {
+ rtx insn;
+ int region;
+ if (label == NULL_RTX)
+ label = last_rethrow_symbol;
+ emit_library_call (rethrow_libfunc, 0, VOIDmode, 1, label, Pmode);
+ region = find_func_region (eh_region_from_symbol (label));
+ /* If the region is -1, it doesn't exist yet. We shouldn't be
+ trying to rethrow there yet. */
+ if (region == -1)
+ abort ();
+ function_eh_regions[region].rethrow_ref = 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. */
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, label,
+ REG_NOTES (insn));
+ emit_barrier ();
+ }
+ else
+ emit_jump (label);
+}
+
+/* Begin a region that will contain entries created with
+ add_partial_entry. */
+
+void
+begin_protect_partials ()
+{
+ /* Put the entry on the function obstack. */
+ push_obstacks_nochange ();
+ resume_temporary_allocation ();
+
+ /* Push room for a new list. */
+ protect_list = tree_cons (NULL_TREE, NULL_TREE, protect_list);
+
+ /* We're done with the function obstack now. */
+ pop_obstacks ();
}
/* End all the pending exception regions on protect_list. The handlers
void
end_protect_partials ()
{
- while (protect_list)
- {
- expand_eh_region_end (TREE_VALUE (protect_list));
- protect_list = TREE_CHAIN (protect_list);
- }
+ tree t;
+
+ /* For backwards compatibility, we allow callers to omit the call to
+ begin_protect_partials for the outermost region. So,
+ PROTECT_LIST may be NULL. */
+ if (!protect_list)
+ return;
+
+ /* End all the exception regions. */
+ for (t = TREE_VALUE (protect_list); t; t = TREE_CHAIN (t))
+ expand_eh_region_end (TREE_VALUE (t));
+
+ /* Pop the topmost entry. */
+ protect_list = TREE_CHAIN (protect_list);
+
}
/* Arrange for __terminate to be called if there is an unhandled throw
handler for the region. This is added by add_eh_table_entry and
used by output_exception_table_entry. */
-static int *eh_table;
-static int eh_table_size;
-static int eh_table_max_size;
+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.
- N is the NOTE_BLOCK_NUMBER of the note, which comes from the code
+ (Or NOTE_INSN_EH_REGION_END sometimes)
+ N is the NOTE_EH_HANDLER of the note, which comes from the code
label number of the exception handler for the region. */
void
}
}
eh_table[eh_table_size++] = n;
+
+ if (flag_new_exceptions)
+ {
+ /* We will output the exception table late in the compilation. That
+ references type_info objects which should have already been output
+ by that time. We explicitly mark those objects as being
+ referenced now so we know to emit them. */
+ struct handler_info *handler = get_first_handler (n);
+
+ for (; handler; handler = handler->next)
+ if (handler->type_info && handler->type_info != CATCH_ALL_TYPE)
+ {
+ tree tinfo = (tree)handler->type_info;
+
+ tinfo = TREE_OPERAND (tinfo, 0);
+ TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;
+ }
+ }
#endif
}
return 0;
}
-/* Output the entry of the exception table corresponding to to the
+/* 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
{
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 */
+ if (flag_new_exceptions
+ && (handler || function_eh_regions[index].rethrow_ref))
+ rethrow = function_eh_regions[index].rethrow_label;
+ else
+ 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);
+ if (function_eh_regions[index].emitted)
+ return;
+ function_eh_regions[index].emitted = 1;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n);
- sym = gen_rtx_SYMBOL_REF (Pmode, buf);
- assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
+ 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_eh_label(buf);
+ rethrow = NULL_RTX;
+ }
- ASM_GENERATE_INTERNAL_LABEL (buf, "L", n);
- sym = gen_rtx_SYMBOL_REF (Pmode, buf);
- assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n);
+ sym = gen_rtx_SYMBOL_REF (Pmode, buf);
+ assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
- putc ('\n', file); /* blank line */
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n);
+ sym = gen_rtx_SYMBOL_REF (Pmode, buf);
+ assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ if (handler == NULL)
+ assemble_eh_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_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
+ }
+
+ if (flag_new_exceptions)
+ {
+ if (handler == NULL || handler->type_info == NULL)
+ assemble_eh_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ else
+ if (handler->type_info == CATCH_ALL_TYPE)
+ assemble_eh_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
-output_exception_table ()
+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;
+}
+
+/* Free the EH table structures. */
+void
+free_exception_table ()
+{
+ if (eh_table)
+ free (eh_table);
+ clear_function_eh_region ();
+}
+
+/* Output the common content of an exception table. */
+void
+output_exception_table_data ()
{
int i;
+ char buf[256];
+ extern FILE *asm_out_file;
+
+ if (flag_new_exceptions)
+ {
+ assemble_eh_integer (GEN_INT (NEW_EH_RUNTIME),
+ POINTER_SIZE / BITS_PER_UNIT, 1);
+ assemble_eh_integer (GEN_INT (language_code), 2 , 1);
+ assemble_eh_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_eh_integer (const0_rtx, i , 1);
+
+ /* Generate the label for offset calculations on rethrows. */
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0);
+ assemble_eh_label(buf);
+ }
+
+ for (i = 0; i < eh_table_size; ++i)
+ output_exception_table_entry (asm_out_file, eh_table[i]);
+
+}
+
+/* Output an exception table for the entire compilation unit. */
+void
+output_exception_table ()
+{
+ char buf[256];
extern FILE *asm_out_file;
if (! doing_eh (0) || ! eh_table)
exception_section ();
/* Beginning marker for table. */
- assemble_align (GET_MODE_ALIGNMENT (ptr_mode));
- assemble_label ("__EXCEPTION_TABLE__");
+ assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode));
+ assemble_eh_label ("__EXCEPTION_TABLE__");
- for (i = 0; i < eh_table_size; ++i)
- output_exception_table_entry (asm_out_file, eh_table[i]);
-
- free (eh_table);
+ output_exception_table_data ();
/* Ending marker for table. */
- 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);
+ /* Generate the label for end of table. */
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow));
+ assemble_eh_label(buf);
+ assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ /* For binary compatibility, the old __throw checked the second
+ position for a -1, so we should output at least 2 -1's */
+ if (! flag_new_exceptions)
+ assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+
putc ('\n', asm_out_file); /* blank line */
}
+
+/* Used by the ia64 unwind format to output data for an individual
+ function. */
+void
+output_function_exception_table ()
+{
+ extern FILE *asm_out_file;
+
+ if (! doing_eh (0) || ! eh_table)
+ return;
+
+#ifdef HANDLER_SECTION
+ HANDLER_SECTION;
+#endif
+
+ output_exception_table_data ();
+
+ /* Ending marker for table. */
+ assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ putc ('\n', asm_out_file); /* blank line */
+}
+
\f
/* Emit code to get EH context.
}
}
+/* Scan the insn chain F and build a list of handler labels. The
+ resulting list is placed in the global variable exception_handler_labels. */
+
+static void
+find_exception_handler_labels_1 (f)
+ rtx f;
+{
+ rtx insn;
+
+ /* For each start of a region, add its label to the list. */
+
+ for (insn = f; 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_EH_HANDLER (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);
+ }
+ }
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ find_exception_handler_labels_1 (XEXP (PATTERN (insn), 0));
+ find_exception_handler_labels_1 (XEXP (PATTERN (insn), 1));
+ find_exception_handler_labels_1 (XEXP (PATTERN (insn), 2));
+ }
+ }
+}
+
/* 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;
- 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;
- /* Generate a handy reference to each label. */
-
- /* We call xmalloc here instead of alloca; we did the latter in the past,
- but found that it can sometimes end up being asked to allocate space
- for more than 1 million labels. */
- labels = (rtx *) xmalloc ((max_labelno - min_labelno) * sizeof (rtx));
- bzero ((char *) labels, (max_labelno - min_labelno) * sizeof (rtx));
-
- /* Arrange for labels to be indexed directly by CODE_LABEL_NUMBER. */
- 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;
- }
+ find_exception_handler_labels_1 (get_insns ());
+}
- /* For each start of a region, add its label to the list. */
+/* Return a value of 1 if the parameter label number is an exception handler
+ label. Return 0 otherwise. */
- 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));
- }
- }
-
- free (labels + min_labelno);
+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.
void
check_exception_handler_labels ()
{
- rtx insn, handler;
+ rtx insn, insn2;
/* If we aren't doing exception handling, there isn't much to check. */
if (! doing_eh (0))
return;
- /* Ensure that the CODE_LABEL_NUMBER for the CODE_LABEL entry point
- in each handler corresponds to the CODE_LABEL_NUMBER of the
- handler. */
+ /* 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));
+ }
+
+}
+
+/* Mark the children of NODE for GC. */
- for (handler = exception_handler_labels;
- handler;
- handler = XEXP (handler, 1))
+static void
+mark_eh_node (node)
+ struct eh_node *node;
+{
+ while (node)
{
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (node->entry)
{
- 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;
- }
- }
+ ggc_mark_rtx (node->entry->outer_context);
+ ggc_mark_rtx (node->entry->exception_handler_label);
+ ggc_mark_tree (node->entry->finalization);
+ ggc_mark_rtx (node->entry->false_label);
+ ggc_mark_rtx (node->entry->rethrow_label);
}
- if (insn == NULL_RTX)
- warning ("handler not found %d",
- CODE_LABEL_NUMBER (XEXP (handler, 0)));
+ node = node ->chain;
}
+}
- /* Now go through and make sure that for each region there is a
- corresponding label. */
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+/* Mark S for GC. */
+
+static void
+mark_eh_stack (s)
+ struct eh_stack *s;
+{
+ if (s)
+ mark_eh_node (s->top);
+}
+
+/* Mark Q for GC. */
+
+static void
+mark_eh_queue (q)
+ struct eh_queue *q;
+{
+ while (q)
{
- if (GET_CODE (insn) == NOTE
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
+ mark_eh_node (q->head);
+ q = q->next;
+ }
+}
+
+/* Mark NODE for GC. A label_node contains a union containing either
+ a tree or an rtx. This label_node will contain a tree. */
+
+static void
+mark_tree_label_node (node)
+ struct label_node *node;
+{
+ while (node)
+ {
+ ggc_mark_tree (node->u.tlabel);
+ node = node->chain;
+ }
+}
+
+/* Mark EH for GC. */
+
+void
+mark_eh_status (eh)
+ struct eh_status *eh;
+{
+ if (eh == 0)
+ return;
+
+ mark_eh_stack (&eh->x_ehstack);
+ mark_eh_stack (&eh->x_catchstack);
+ mark_eh_queue (eh->x_ehqueue);
+ ggc_mark_rtx (eh->x_catch_clauses);
+
+ if (lang_mark_false_label_stack)
+ (*lang_mark_false_label_stack) (eh->x_false_label_stack);
+ mark_tree_label_node (eh->x_caught_return_label_stack);
+
+ ggc_mark_tree (eh->x_protect_list);
+ ggc_mark_rtx (eh->ehc);
+ ggc_mark_rtx (eh->x_eh_return_stub_label);
+}
+
+/* Mark ARG (which is really a struct func_eh_entry**) for GC. */
+
+static void
+mark_func_eh_entry (arg)
+ void *arg;
+{
+ struct func_eh_entry *fee;
+ struct handler_info *h;
+ int i;
+
+ fee = *((struct func_eh_entry **) arg);
+
+ for (i = 0; i < current_func_eh_entry; ++i)
+ {
+ ggc_mark_rtx (fee->rethrow_label);
+ for (h = fee->handlers; h; h = h->next)
{
- 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 && !flag_syntax_only)
- warning ("region exists, no handler %d",
- NOTE_BLOCK_NUMBER (insn));
+ ggc_mark_rtx (h->handler_label);
+ if (h->type_info != CATCH_ALL_TYPE)
+ ggc_mark_tree ((tree) h->type_info);
}
+
+ /* Skip to the next entry in the array. */
+ ++fee;
}
}
-\f
+
/* 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
void
init_eh ()
{
- /* Generate rtl to reference the variable in which the PC of the
- current context is saved. */
- (void) build_pointer_type (make_node (VOID_TYPE));
+ first_rethrow_symbol = create_rethrow_ref (0);
+ final_rethrow = gen_exception_label ();
+ last_rethrow_symbol = create_rethrow_ref (CODE_LABEL_NUMBER (final_rethrow));
+
+ ggc_add_rtx_root (&exception_handler_labels, 1);
+ ggc_add_rtx_root (&eh_return_context, 1);
+ ggc_add_rtx_root (&eh_return_stack_adjust, 1);
+ ggc_add_rtx_root (&eh_return_handler, 1);
+ ggc_add_rtx_root (&first_rethrow_symbol, 1);
+ ggc_add_rtx_root (&final_rethrow, 1);
+ ggc_add_rtx_root (&last_rethrow_symbol, 1);
+ ggc_add_root (&function_eh_regions, 1, sizeof (function_eh_regions),
+ mark_func_eh_entry);
}
-
+
/* Initialize the per-function EH information. */
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;
- current_function_ehc = NULL_RTX;
+ cfun->eh = (struct eh_status *) xcalloc (1, sizeof (struct eh_status));
+ ehqueue = (struct eh_queue *) xcalloc (1, sizeof (struct eh_queue));
+ eh_return_context = NULL_RTX;
+ eh_return_stack_adjust = NULL_RTX;
+ eh_return_handler = 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;
+free_eh_status (f)
+ struct function *f;
{
- if (p == NULL)
- abort ();
-
- 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;
- p->ehc = current_function_ehc;
-
- init_eh ();
-}
-
-/* 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;
- current_function_ehc = p->ehc;
+ free (f->eh->x_ehqueue);
+ free (f->eh);
+ f->eh = NULL;
}
\f
/* This section is for the exception handling specific optimization
- pass. First are the internal routines, and then the main
- optimization pass. */
+ pass. */
/* Determine if the given INSN can throw an exception. */
-static int
+int
can_throw (insn)
rtx insn;
{
- /* Calls can always potentially throw exceptions. */
+ if (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+
+ /* Calls can always potentially throw exceptions, unless they have
+ a REG_EH_REGION note with a value of 0 or less. */
if (GET_CODE (insn) == CALL_INSN)
- return 1;
+ {
+ rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+ if (!note || INTVAL (XEXP (note, 0)) > 0)
+ return 1;
+ }
if (asynchronous_exceptions)
{
return 0;
}
+/* Return nonzero if nothing in this function can throw. */
+
+int
+nothrow_function_p ()
+{
+ rtx insn;
+
+ if (! flag_exceptions)
+ return 1;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (can_throw (insn))
+ return 0;
+ for (insn = current_function_epilogue_delay_list; insn;
+ insn = XEXP (insn, 1))
+ if (can_throw (insn))
+ return 0;
+
+ return 1;
+}
+
/* 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
/* Assume we can delete the region. */
int delete = 1;
+ /* Can't delete something which is rethrown from. */
+ if (rethrow_used (n))
+ delete = 0;
+
if (insn == NULL_RTX
|| GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
- || NOTE_BLOCK_NUMBER (insn) != n
+ || NOTE_EH_HANDLER (insn) != n
|| delete_outer == NULL)
abort ();
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
{
- insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &delete);
+ insn = scan_region (insn, NOTE_EH_HANDLER (insn), &delete);
}
insn = NEXT_INSN (insn);
}
/* The _BEG/_END NOTEs must match and nest. */
- if (NOTE_BLOCK_NUMBER (insn) != n)
+ if (NOTE_EH_HANDLER (insn) != n)
abort ();
/* If anything in this exception region can throw, we can throw. */
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)
prev = &XEXP (x, 1);
}
}
+#endif
}
return insn;
}
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);
+ insn = scan_region (insn, NOTE_EH_HANDLER (insn), &n);
}
}
}
+
+/* This function determines whether the rethrow labels for any of the
+ exception regions in the current function are used or not, and set
+ the reference flag according. */
+
+void
+update_rethrow_references ()
+{
+ rtx insn;
+ int x, region;
+ int *saw_region, *saw_rethrow;
+
+ if (!flag_new_exceptions)
+ return;
+
+ saw_region = (int *) xcalloc (current_func_eh_entry, sizeof (int));
+ saw_rethrow = (int *) xcalloc (current_func_eh_entry, sizeof (int));
+
+ /* Determine what regions exist, and whether there are any rethrows
+ from those regions or not. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX);
+ if (note)
+ {
+ region = eh_region_from_symbol (XEXP (note, 0));
+ region = find_func_region (region);
+ saw_rethrow[region] = 1;
+ }
+ }
+ else
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+ {
+ region = find_func_region (NOTE_EH_HANDLER (insn));
+ saw_region[region] = 1;
+ }
+ }
+
+ /* For any regions we did see, set the referenced flag. */
+ for (x = 0; x < current_func_eh_entry; x++)
+ if (saw_region[x])
+ function_eh_regions[x].rethrow_ref = saw_rethrow[x];
+
+ /* Clean up. */
+ free (saw_region);
+ free (saw_rethrow);
+}
\f
/* Various hooks for the DWARF 2 __throw routine. */
return addr;
}
-/* Given an actual address in addr_tree, set the return address register up
- so the epilogue will return to that address. If the return address is
- not in a register, do nothing. */
-
-void
-expand_builtin_set_return_addr_reg (addr_tree)
- tree addr_tree;
-{
- rtx tmp;
- rtx ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0, hard_frame_pointer_rtx);
+/* 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.
- if (GET_CODE (ra) != REG || REGNO (ra) >= FIRST_PSEUDO_REGISTER)
- return;
-
- tmp = force_operand (expand_builtin_frob_return_addr (addr_tree), ra);
- if (tmp != ra)
- emit_move_insn (ra, tmp);
-}
+ The first passes the exception context to the handler. For this
+ we use the return value register for a void*.
-/* Choose two registers for communication between the main body of
- __throw and the stub for adjusting the stack pointer. The first register
- is used to pass the address of the exception handler; the second register
- is used to pass the stack pointer offset.
+ The second holds the stack pointer value to be restored. For this
+ we use the static chain register if it exists, is different from
+ the previous, and is call-clobbered; otherwise some arbitrary
+ call-clobbered register.
- For register 1 we use the return value register for a void *.
- For register 2 we use the static chain register if it exists and is
- different from register 1, otherwise some arbitrary call-clobbered
- register. */
+ The third holds the address of the handler itself. Here we use
+ some arbitrary call-clobbered register. */
static void
-eh_regs (r1, r2, outgoing)
- rtx *r1, *r2;
- int outgoing;
+eh_regs (pcontext, psp, pra, outgoing)
+ rtx *pcontext, *psp, *pra;
+ int outgoing ATTRIBUTE_UNUSED;
{
- rtx reg1, reg2;
+ rtx rcontext, rsp, rra;
+ unsigned int i;
#ifdef FUNCTION_OUTGOING_VALUE
if (outgoing)
- reg1 = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node),
- current_function_decl);
+ rcontext = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node),
+ current_function_decl);
else
#endif
- reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node),
- current_function_decl);
+ rcontext = FUNCTION_VALUE (build_pointer_type (void_type_node),
+ current_function_decl);
#ifdef STATIC_CHAIN_REGNUM
if (outgoing)
- reg2 = static_chain_incoming_rtx;
+ rsp = static_chain_incoming_rtx;
else
- reg2 = static_chain_rtx;
- if (REGNO (reg2) == REGNO (reg1))
+ rsp = static_chain_rtx;
+ if (REGNO (rsp) == REGNO (rcontext)
+ || ! call_used_regs [REGNO (rsp)])
#endif /* STATIC_CHAIN_REGNUM */
- reg2 = NULL_RTX;
+ rsp = NULL_RTX;
- if (reg2 == NULL_RTX)
+ if (rsp == NULL_RTX)
{
- int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
- if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (reg1))
- {
- reg2 = gen_rtx_REG (Pmode, i);
- break;
- }
+ if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (rcontext))
+ break;
+ if (i == FIRST_PSEUDO_REGISTER)
+ abort();
- if (reg2 == NULL_RTX)
- abort ();
+ rsp = gen_rtx_REG (Pmode, i);
}
- *r1 = reg1;
- *r2 = reg2;
-}
+ 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();
-/* Emit inside of __throw a stub which adjusts the stack pointer and jumps
- to the exception handler. __throw will set up the necessary values
- and then return to the stub. */
+ rra = gen_rtx_REG (Pmode, i);
-rtx
-expand_builtin_eh_stub ()
-{
- rtx stub_start = gen_label_rtx ();
- rtx after_stub = gen_label_rtx ();
- rtx handler, offset;
+ *pcontext = rcontext;
+ *psp = rsp;
+ *pra = rra;
+}
- emit_jump (after_stub);
- emit_label (stub_start);
+/* Retrieve the register which contains the pointer to the eh_context
+ structure set the __throw. */
- eh_regs (&handler, &offset, 0);
+#if 0
+rtx
+get_reg_for_handler ()
+{
+ rtx reg1;
+ reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node),
+ current_function_decl);
+ return reg1;
+}
+#endif
- adjust_stack (offset);
- emit_indirect_jump (handler);
+/* Set up the epilogue with the magic bits we'll need to return to the
+ exception handler. */
- emit_label (after_stub);
- return gen_rtx_LABEL_REF (Pmode, stub_start);
+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));
}
-/* Set up the registers for passing the handler address and stack offset
- to the stub above. */
-
void
-expand_builtin_set_eh_regs (handler, offset)
- tree handler, offset;
+expand_eh_return ()
{
- rtx reg1, reg2;
+ rtx reg1, reg2, reg3;
+ rtx stub_start, after_stub;
+ rtx ra, tmp;
- eh_regs (®1, ®2, 1);
+ if (!eh_return_context)
+ return;
- store_expr (offset, reg2, 0);
- store_expr (handler, reg1, 0);
+ current_function_cannot_inline = N_("function uses __builtin_eh_return");
+
+ eh_regs (®1, ®2, ®3, 1);
+#ifdef POINTERS_EXTEND_UNSIGNED
+ eh_return_context = convert_memory_address (Pmode, eh_return_context);
+ eh_return_stack_adjust =
+ convert_memory_address (Pmode, eh_return_stack_adjust);
+ eh_return_handler = convert_memory_address (Pmode, eh_return_handler);
+#endif
+ emit_move_insn (reg1, eh_return_context);
+ emit_move_insn (reg2, eh_return_stack_adjust);
+ emit_move_insn (reg3, eh_return_handler);
+
+ /* Talk directly to the target's epilogue code when possible. */
+
+#ifdef HAVE_eh_epilogue
+ if (HAVE_eh_epilogue)
+ {
+ emit_insn (gen_eh_epilogue (reg1, reg2, reg3));
+ return;
+ }
+#endif
+
+ /* Otherwise, use the same stub technique we had before. */
+
+ eh_return_stub_label = stub_start = gen_label_rtx ();
+ after_stub = gen_label_rtx ();
+
+ /* Set the return address to the stub label. */
+
+ ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0, hard_frame_pointer_rtx);
+ if (GET_CODE (ra) == REG && REGNO (ra) >= FIRST_PSEUDO_REGISTER)
+ abort();
+
+ tmp = memory_address (Pmode, gen_rtx_LABEL_REF (Pmode, stub_start));
+#ifdef RETURN_ADDR_OFFSET
+ tmp = plus_constant (tmp, -RETURN_ADDR_OFFSET);
+#endif
+ tmp = force_operand (tmp, ra);
+ if (tmp != ra)
+ emit_move_insn (ra, tmp);
- /* These will be used by the stub. */
+ /* Indicate that the registers are in fact used. */
emit_insn (gen_rtx_USE (VOIDmode, reg1));
emit_insn (gen_rtx_USE (VOIDmode, reg2));
-}
+ emit_insn (gen_rtx_USE (VOIDmode, reg3));
+ if (GET_CODE (ra) == REG)
+ emit_insn (gen_rtx_USE (VOIDmode, ra));
+
+ /* Generate the stub. */
+
+ emit_jump (after_stub);
+ emit_label (stub_start);
+
+ eh_regs (®1, ®2, ®3, 0);
+ adjust_stack (reg2);
+ emit_indirect_jump (reg3);
+ emit_label (after_stub);
+}
\f
/* This contains the code required to verify whether arbitrary instructions
static int *insn_eh_region = (int *)0;
static int maximum_uid;
-static void set_insn_eh_region (first, region_num)
+static void
+set_insn_eh_region (first, region_num)
rtx *first;
int region_num;
{
for (insn = *first; insn; insn = NEXT_INSN (insn))
{
- if ((GET_CODE (insn) == NOTE) &&
- (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG))
+ if ((GET_CODE (insn) == NOTE)
+ && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG))
{
- rnum = NOTE_BLOCK_NUMBER (insn);
+ rnum = NOTE_EH_HANDLER (insn);
insn_eh_region[INSN_UID (insn)] = rnum;
insn = NEXT_INSN (insn);
set_insn_eh_region (&insn, rnum);
/* Free the insn table, an make sure it cannot be used again. */
-void free_insn_eh_region ()
+void
+free_insn_eh_region ()
{
if (!doing_eh (0))
return;
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)
+void
+init_insn_eh_region (first, max_uid)
rtx first;
int max_uid;
{
max_uid = INSN_UID (insn);
maximum_uid = max_uid;
- insn_eh_region = (int *) malloc ((max_uid + 1) * sizeof (int));
+ insn_eh_region = (int *) xmalloc ((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
+in_same_eh_region (insn1, insn2)
+ rtx insn1, insn2;
{
int ret, uid1, uid2;
ret = (insn_eh_region[uid1] == insn_eh_region[uid2]);
return ret;
}
+\f
+/* This function will initialize the handler list for a specified block.
+ It may recursively call itself if the outer block hasn't been processed
+ yet. At some point in the future we can trim out handlers which we
+ know cannot be called. (ie, if a block has an INT type handler,
+ control will never be passed to an outer INT type handler). */
+
+static void
+process_nestinfo (block, info, nested_eh_region)
+ int block;
+ eh_nesting_info *info;
+ int *nested_eh_region;
+{
+ handler_info *ptr, *last_ptr = NULL;
+ int x, y, count = 0;
+ int extra = 0;
+ handler_info **extra_handlers = 0;
+ int index = info->region_index[block];
+
+ /* If we've already processed this block, simply return. */
+ if (info->num_handlers[index] > 0)
+ return;
+
+ for (ptr = get_first_handler (block); ptr; last_ptr = ptr, ptr = ptr->next)
+ count++;
+
+ /* pick up any information from the next outer region. It will already
+ contain a summary of itself and all outer regions to it. */
+
+ if (nested_eh_region [block] != 0)
+ {
+ int nested_index = info->region_index[nested_eh_region[block]];
+ process_nestinfo (nested_eh_region[block], info, nested_eh_region);
+ extra = info->num_handlers[nested_index];
+ extra_handlers = info->handlers[nested_index];
+ info->outer_index[index] = nested_index;
+ }
+
+ /* If the last handler is either a CATCH_ALL or a cleanup, then we
+ won't use the outer ones since we know control will not go past the
+ catch-all or cleanup. */
+
+ if (last_ptr != NULL && (last_ptr->type_info == NULL
+ || last_ptr->type_info == CATCH_ALL_TYPE))
+ extra = 0;
+
+ info->num_handlers[index] = count + extra;
+ info->handlers[index] = (handler_info **) xmalloc ((count + extra)
+ * sizeof (handler_info **));
+
+ /* First put all our handlers into the list. */
+ ptr = get_first_handler (block);
+ for (x = 0; x < count; x++)
+ {
+ info->handlers[index][x] = ptr;
+ ptr = ptr->next;
+ }
+
+ /* Now add all the outer region handlers, if they aren't they same as
+ one of the types in the current block. We won't worry about
+ derived types yet, we'll just look for the exact type. */
+ for (y =0, x = 0; x < extra ; x++)
+ {
+ int i, ok;
+ ok = 1;
+ /* Check to see if we have a type duplication. */
+ for (i = 0; i < count; i++)
+ if (info->handlers[index][i]->type_info == extra_handlers[x]->type_info)
+ {
+ ok = 0;
+ /* Record one less handler. */
+ (info->num_handlers[index])--;
+ break;
+ }
+ if (ok)
+ {
+ info->handlers[index][y + count] = extra_handlers[x];
+ y++;
+ }
+ }
+}
+
+/* This function will allocate and initialize an eh_nesting_info structure.
+ It returns a pointer to the completed data structure. If there are
+ no exception regions, a NULL value is returned. */
+
+eh_nesting_info *
+init_eh_nesting_info ()
+{
+ int *nested_eh_region;
+ int region_count = 0;
+ rtx eh_note = NULL_RTX;
+ eh_nesting_info *info;
+ rtx insn;
+ int x;
+
+ if (! flag_exceptions)
+ return 0;
+
+ info = (eh_nesting_info *) xmalloc (sizeof (eh_nesting_info));
+ info->region_index = (int *) xcalloc ((max_label_num () + 1), sizeof (int));
+ nested_eh_region = (int *) xcalloc (max_label_num () + 1, sizeof (int));
+
+ /* Create the nested_eh_region list. If indexed with a block number, it
+ returns the block number of the next outermost region, if any.
+ We can count the number of regions and initialize the region_index
+ vector at the same time. */
+ for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+ {
+ int block = NOTE_EH_HANDLER (insn);
+ region_count++;
+ info->region_index[block] = region_count;
+ if (eh_note)
+ nested_eh_region [block] =
+ NOTE_EH_HANDLER (XEXP (eh_note, 0));
+ else
+ nested_eh_region [block] = 0;
+ eh_note = gen_rtx_EXPR_LIST (VOIDmode, insn, eh_note);
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
+ eh_note = XEXP (eh_note, 1);
+ }
+ }
+
+ /* If there are no regions, wrap it up now. */
+ if (region_count == 0)
+ {
+ free (info->region_index);
+ free (info);
+ free (nested_eh_region);
+ return NULL;
+ }
+
+ region_count++;
+ info->handlers = (handler_info ***) xcalloc (region_count,
+ sizeof (handler_info ***));
+ info->num_handlers = (int *) xcalloc (region_count, sizeof (int));
+ info->outer_index = (int *) xcalloc (region_count, sizeof (int));
+
+ /* Now initialize the handler lists for all exception blocks. */
+ for (x = 0; x <= max_label_num (); x++)
+ {
+ if (info->region_index[x] != 0)
+ process_nestinfo (x, info, nested_eh_region);
+ }
+ info->region_count = region_count;
+
+ /* Clean up. */
+ free (nested_eh_region);
+
+ return info;
+}
+
+
+/* This function is used to retreive the vector of handlers which
+ can be reached by a given insn in a given exception region.
+ BLOCK is the exception block the insn is in.
+ INFO is the eh_nesting_info structure.
+ INSN is the (optional) insn within the block. If insn is not NULL_RTX,
+ it may contain reg notes which modify its throwing behavior, and
+ these will be obeyed. If NULL_RTX is passed, then we simply return the
+ handlers for block.
+ HANDLERS is the address of a pointer to a vector of handler_info pointers.
+ Upon return, this will have the handlers which can be reached by block.
+ This function returns the number of elements in the handlers vector. */
+
+int
+reachable_handlers (block, info, insn, handlers)
+ int block;
+ eh_nesting_info *info;
+ rtx insn ;
+ handler_info ***handlers;
+{
+ int index = 0;
+ *handlers = NULL;
+
+ if (info == NULL)
+ return 0;
+ if (block > 0)
+ index = info->region_index[block];
+
+ if (insn && GET_CODE (insn) == CALL_INSN)
+ {
+ /* RETHROWs specify a region number from which we are going to rethrow.
+ This means we won't pass control to handlers in the specified
+ region, but rather any region OUTSIDE the specified region.
+ We accomplish this by setting block to the outer_index of the
+ specified region. */
+ rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX);
+ if (note)
+ {
+ index = eh_region_from_symbol (XEXP (note, 0));
+ index = info->region_index[index];
+ if (index)
+ index = info->outer_index[index];
+ }
+ else
+ {
+ /* If there is no rethrow, we look for a REG_EH_REGION, and
+ we'll throw from that block. A value of 0 or less
+ indicates that this insn cannot throw. */
+ note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+ if (note)
+ {
+ int b = INTVAL (XEXP (note, 0));
+ if (b <= 0)
+ index = 0;
+ else
+ index = info->region_index[b];
+ }
+ }
+ }
+ /* If we reach this point, and index is 0, there is no throw. */
+ if (index == 0)
+ return 0;
+
+ *handlers = info->handlers[index];
+ return info->num_handlers[index];
+}
+
+
+/* This function will free all memory associated with the eh_nesting info. */
+
+void
+free_eh_nesting_info (info)
+ eh_nesting_info *info;
+{
+ int x;
+ if (info != NULL)
+ {
+ if (info->region_index)
+ free (info->region_index);
+ if (info->num_handlers)
+ free (info->num_handlers);
+ if (info->outer_index)
+ free (info->outer_index);
+ if (info->handlers)
+ {
+ for (x = 0; x < info->region_count; x++)
+ if (info->handlers[x])
+ free (info->handlers[x]);
+ free (info->handlers);
+ }
+ free (info);
+ }
+}