/* Implements exception handling.
- Copyright (C) 1989, 92-96, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1989, 92-97, 1998 Free Software Foundation, Inc.
Contributed by Mike Stump <mrs@cygnus.com>.
This file is part of GNU CC.
On targets that support crtstuff.c, the unwind information
is stored in a section named .eh_frame and the information for the
entire shared object or program is registered with a call to
- __register_frame. On other targets, the information for each
+ __register_frame_info. On other targets, the information for each
translation unit is registered from the file generated by collect2.
- __register_frame is defined in frame.c, and is responsible for
+ __register_frame_info is defined in frame.c, and is responsible for
recording all of the unwind regions into one list (which is kept in a
static variable named unwind_table_list).
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 "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "insn-config.h"
#include "recog.h"
#include "output.h"
+#include "toplev.h"
/* One to use setjmp/longjmp method of generating code for exception
handling. */
rtx exception_handler_labels;
-/* Nonzero means that __throw was invoked.
-
- This is used by the C++ frontend to know if code needs to be emitted
- for __throw or not. */
-
-int throw_used;
-
/* The EH context. Nonzero if the function has already
fetched a pointer to the EH context for exception handling. */
struct label_node *false_label_stack = NULL;
-rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
+static void push_eh_entry PROTO((struct eh_stack *));
+static struct eh_entry * pop_eh_entry PROTO((struct eh_stack *));
+static void enqueue_eh_entry PROTO((struct eh_queue *, struct eh_entry *));
+static struct eh_entry * dequeue_eh_entry PROTO((struct eh_queue *));
+static rtx call_get_eh_context PROTO((void));
+static void start_dynamic_cleanup PROTO((tree, tree));
+static void start_dynamic_handler PROTO((void));
static void expand_rethrow PROTO((rtx));
+static void output_exception_table_entry PROTO((FILE *, int));
+static int can_throw PROTO((rtx));
+static rtx scan_region PROTO((rtx, int, int *));
+static void eh_regs PROTO((rtx *, rtx *, int));
+static void set_insn_eh_region PROTO((rtx *, int));
+rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
\f
/* Various support routines to manipulate the various data structures
used by the exception handling code. */
return (*stack)->u.tlabel;
}
-/* Make a copy of ENTRY using xmalloc to allocate the space. */
-
-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 a new eh_node entry onto STACK. */
static void
return tempentry;
}
\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.
pop_obstacks ();
}
-/* Emit code to get EH context to current function. Should only be used
- by emit_eh_context. */
+/* Emit code to get EH context to current function. */
static rtx
call_get_eh_context ()
{
static tree fn;
tree expr;
- rtx ehc, reg, insns;
if (fn == NULL_TREE)
{
expr, NULL_TREE, NULL_TREE);
TREE_SIDE_EFFECTS (expr) = 1;
- start_sequence ();
- ehc = expand_expr (expr, NULL_RTX, VOIDmode, 0);
- reg = copy_to_reg (ehc);
-
- insns = get_insns ();
- end_sequence ();
-
- emit_insns_before (insns, get_first_nonparm_insn ());
-
- return reg;
+ return copy_to_reg (expand_expr (expr, NULL_RTX, VOIDmode, 0));
}
/* Get a reference to the EH context.
current_function_ehc = gen_reg_rtx (Pmode);
- insn = gen_rtx (USE,
- GET_MODE (current_function_ehc),
- current_function_ehc);
+ insn = gen_rtx_USE (GET_MODE (current_function_ehc),
+ current_function_ehc);
insn = emit_insn_before (insn, get_first_nonparm_insn ());
REG_NOTES (insn)
- = gen_rtx (EXPR_LIST,
- REG_EH_CONTEXT, current_function_ehc,
- REG_NOTES (insn));
+ = gen_rtx_EXPR_LIST (REG_EH_CONTEXT, current_function_ehc,
+ REG_NOTES (insn));
}
return current_function_ehc;
}
result = copy_to_reg (dhc);
/* We don't want a copy of the dcc, but rather, the single dcc. */
- return gen_rtx (MEM, Pmode, result);
+ return gen_rtx_MEM (Pmode, result);
}
/* Get a reference to the dynamic cleanup chain. It points to the
result = copy_to_reg (dcc);
/* We don't want a copy of the dcc, but rather, the single dcc. */
- return gen_rtx (MEM, Pmode, result);
+ return gen_rtx_MEM (Pmode, result);
}
/* Generate code to evaluate X and jump to LABEL if the value is nonzero.
tree func;
tree arg;
{
- rtx dhc, dcc;
+ rtx dcc;
rtx new_func, new_arg;
rtx x, buf;
int size;
/* Store func and arg into the cleanup list element. */
- new_func = gen_rtx (MEM, Pmode, plus_constant (XEXP (buf, 0),
- GET_MODE_SIZE (Pmode)));
- new_arg = gen_rtx (MEM, Pmode, plus_constant (XEXP (buf, 0),
- GET_MODE_SIZE (Pmode)*2));
+ new_func = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0),
+ GET_MODE_SIZE (Pmode)));
+ new_arg = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0),
+ GET_MODE_SIZE (Pmode)*2));
x = expand_expr (func, new_func, Pmode, 0);
if (x != new_func)
emit_move_insn (new_func, x);
/* Store dhc into the first word of the newly allocated buffer. */
dhc = get_dynamic_handler_chain ();
- dcc = gen_rtx (MEM, Pmode, plus_constant (XEXP (arg, 0),
- GET_MODE_SIZE (Pmode)));
+ dcc = gen_rtx_MEM (Pmode, plus_constant (XEXP (arg, 0),
+ GET_MODE_SIZE (Pmode)));
emit_move_insn (arg, dhc);
/* Zero out the start of the cleanup chain. */
#ifdef DONT_USE_BUILTIN_SETJMP
x = emit_library_call_value (setjmp_libfunc, NULL_RTX, 1, SImode, 1,
buf, Pmode);
+ /* If we come back here for a catch, transfer control to the handler. */
+ jumpif_rtx (x, ehstack.top->entry->exception_handler_label);
#else
- x = expand_builtin_setjmp (buf, NULL_RTX);
+ {
+ /* A label to continue execution for the no exception case. */
+ rtx noex = gen_label_rtx();
+ x = expand_builtin_setjmp (buf, NULL_RTX, noex,
+ ehstack.top->entry->exception_handler_label);
+ emit_label (noex);
+ }
#endif
- /* 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));
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)
tree decl;
tree cleanup;
{
- rtx note;
-
/* This is the old code. */
if (! doing_eh (0))
return 0;
#else
emit_library_call (throw_libfunc, 0, VOIDmode, 0);
#endif
- throw_used = 1;
}
emit_barrier ();
}
return 0;
}
-/* 1 if we need a static constructor to register EH table info. */
-
-int
-register_exception_table_p ()
-{
-#if defined (DWARF2_UNWIND_INFO)
- return 0;
-#endif
-
- return exception_table_p ();
-}
-
-/* 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
rtx sym;
ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n);
- sym = gen_rtx (SYMBOL_REF, Pmode, buf);
+ 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);
+ 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);
+ sym = gen_rtx_SYMBOL_REF (Pmode, buf);
assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
putc ('\n', file); /* blank line */
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);
-}
\f
/* Emit code to get EH context.
{
rtx insns;
- /* If this is the first use insn, emit the call. */
+ start_sequence ();
+
+ /* If this is the first use insn, emit the call here. This
+ will always be at the top of our function, because if
+ expand_inline_function notices a REG_EH_CONTEXT note, it
+ adds a use insn to this function as well. */
if (ehc == 0)
ehc = call_get_eh_context ();
- start_sequence ();
emit_move_insn (XEXP (reg, 0), ehc);
insns = get_insns ();
end_sequence ();
if (label)
exception_handler_labels
- = gen_rtx (EXPR_LIST, VOIDmode,
- 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));
== NOTE_BLOCK_NUMBER (insn))
break;
}
- if (handler == NULL_RTX)
+ if (handler == NULL_RTX && !flag_syntax_only)
warning ("region exists, no handler %d",
NOTE_BLOCK_NUMBER (insn));
}
void
init_eh ()
{
- /* Generate rtl to reference the variable in which the PC of the
- current context is saved. */
- tree type = build_pointer_type (make_node (VOID_TYPE));
}
/* Initialize the per-function EH information. */
p->protect_list = protect_list;
p->ehc = current_function_ehc;
- init_eh ();
+ init_eh_for_function ();
}
/* Restore the per-function EH info saved into the area denoted by P.
void
exception_optimize ()
{
- rtx insn, regions = NULL_RTX;
+ rtx insn;
int n;
/* Remove empty regions. */
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);
+ reg2 = gen_rtx_REG (Pmode, i);
break;
}
{
rtx stub_start = gen_label_rtx ();
rtx after_stub = gen_label_rtx ();
- rtx handler, offset, temp;
+ rtx handler, offset;
emit_jump (after_stub);
emit_label (stub_start);
emit_indirect_jump (handler);
emit_label (after_stub);
- return gen_rtx (LABEL_REF, Pmode, stub_start);
+ return gen_rtx_LABEL_REF (Pmode, stub_start);
}
/* Set up the registers for passing the handler address and stack offset
store_expr (handler, reg1, 0);
/* These will be used by the stub. */
- emit_insn (gen_rtx (USE, VOIDmode, reg1));
- emit_insn (gen_rtx (USE, VOIDmode, reg2));
+ emit_insn (gen_rtx_USE (VOIDmode, reg1));
+ emit_insn (gen_rtx_USE (VOIDmode, reg2));
}
+
+\f
+
+/* This contains the code required to verify whether arbitrary instructions
+ are in the same exception region. */
+
+static int *insn_eh_region = (int *)0;
+static int maximum_uid;
+
+static void
+set_insn_eh_region (first, region_num)
+ rtx *first;
+ int region_num;
+{
+ rtx insn;
+ int rnum;
+
+ for (insn = *first; insn; insn = NEXT_INSN (insn))
+ {
+ if ((GET_CODE (insn) == NOTE) &&
+ (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG))
+ {
+ rnum = NOTE_BLOCK_NUMBER (insn);
+ insn_eh_region[INSN_UID (insn)] = rnum;
+ insn = NEXT_INSN (insn);
+ set_insn_eh_region (&insn, rnum);
+ /* Upon return, insn points to the EH_REGION_END of nested region */
+ continue;
+ }
+ insn_eh_region[INSN_UID (insn)] = region_num;
+ if ((GET_CODE (insn) == NOTE) &&
+ (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
+ break;
+ }
+ *first = insn;
+}
+
+/* Free the insn table, an make sure it cannot be used again. */
+
+void free_insn_eh_region ()
+{
+ if (!doing_eh (0))
+ return;
+
+ if (insn_eh_region)
+ {
+ free (insn_eh_region);
+ insn_eh_region = (int *)0;
+ }
+}
+
+/* Initialize the table. max_uid must be calculated and handed into
+ this routine. If it is unavailable, passing a value of 0 will
+ cause this routine to calculate it as well. */
+
+void init_insn_eh_region (first, max_uid)
+ rtx first;
+ int max_uid;
+{
+ rtx insn;
+
+ if (!doing_eh (0))
+ return;
+
+ if (insn_eh_region)
+ free_insn_eh_region();
+
+ if (max_uid == 0)
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) > max_uid) /* find largest UID */
+ max_uid = INSN_UID (insn);
+
+ maximum_uid = max_uid;
+ insn_eh_region = (int *) malloc ((max_uid + 1) * sizeof (int));
+ insn = first;
+ set_insn_eh_region (&insn, 0);
+}
+
+
+/* Check whether 2 instructions are within the same region. */
+
+int in_same_eh_region(insn1, insn2)
+ rtx insn1,insn2;
+{
+ int ret, uid1, uid2;
+
+ /* If no exceptions, instructions are always in same region. */
+ if (!doing_eh (0))
+ return 1;
+
+ /* If the table isn't allocated, assume the worst. */
+ if (!insn_eh_region)
+ return 0;
+
+ uid1 = INSN_UID (insn1);
+ uid2 = INSN_UID (insn2);
+
+ /* if instructions have been allocated beyond the end, either
+ the table is out of date, or this is a late addition, or
+ something... Assume the worst. */
+ if (uid1 > maximum_uid || uid2 > maximum_uid)
+ return 0;
+
+ ret = (insn_eh_region[uid1] == insn_eh_region[uid2]);
+ return ret;
+}
+