/* Implements exception handling.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Mike Stump <mrs@cygnus.com>.
This file is part of GCC.
struct eh_region_u_fixup {
tree cleanup_exp;
struct eh_region *real_region;
+ bool resolved;
} GTY ((tag ("ERT_FIXUP"))) fixup;
} GTY ((desc ("%0.type"))) u;
{
tree f_jbuf, f_per, f_lsda, f_prev, f_cs, f_data, tmp;
- sjlj_fc_type_node = (*lang_hooks.types.make_type) (RECORD_TYPE);
+ sjlj_fc_type_node = lang_hooks.types.make_type (RECORD_TYPE);
f_prev = build_decl (FIELD_DECL, get_identifier ("__prev"),
build_pointer_type (sjlj_fc_type_node));
DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node;
tmp = build_index_type (build_int_2 (4 - 1, 0));
- tmp = build_array_type ((*lang_hooks.types.type_for_mode) (word_mode, 1),
+ tmp = build_array_type (lang_hooks.types.type_for_mode (word_mode, 1),
tmp);
f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp);
DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node;
tmp = build_int_2 (FIRST_PSEUDO_REGISTER + 2 - 1, 0);
#endif
#else
- /* This is 2 for builtin_setjmp, plus whatever the target requires
- via STACK_SAVEAREA_MODE (SAVE_NONLOCAL). */
- tmp = build_int_2 ((GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL))
- / GET_MODE_SIZE (Pmode)) + 2 - 1, 0);
+ /* builtin_setjmp takes a pointer to 5 words. */
+ tmp = build_int_2 (5 * BITS_PER_WORD / POINTER_SIZE - 1, 0);
#endif
tmp = build_index_type (tmp);
tmp = build_array_type (ptr_type_node, tmp);
emit_jump (region->u.try.continue_label);
}
-/* Begin a catch clause. TYPE is the type caught, a list of such types, or
- null if this is a catch-all clause. Providing a type list enables to
- associate the catch region with potentially several exception types, which
- is useful e.g. for Ada. */
+/* Begin a catch clause. TYPE is the type caught, a list of such
+ types, (in the case of Java) an ADDR_EXPR which points to the
+ runtime type to match, or null if this is a catch-all
+ clause. Providing a type list enables to associate the catch region
+ with potentially several exception types, which is useful e.g. for
+ Ada. */
void
expand_start_catch (tree type_or_list)
}
static void
+resolve_one_fixup_region (struct eh_region *fixup)
+{
+ struct eh_region *cleanup, *real;
+ int j, n;
+
+ n = cfun->eh->last_region_number;
+ cleanup = 0;
+
+ for (j = 1; j <= n; ++j)
+ {
+ cleanup = cfun->eh->region_array[j];
+ if (cleanup && cleanup->type == ERT_CLEANUP
+ && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp)
+ break;
+ }
+ if (j > n)
+ abort ();
+
+ real = cleanup->outer;
+ if (real && real->type == ERT_FIXUP)
+ {
+ if (!real->u.fixup.resolved)
+ resolve_one_fixup_region (real);
+ real = real->u.fixup.real_region;
+ }
+
+ fixup->u.fixup.real_region = real;
+ fixup->u.fixup.resolved = true;
+}
+
+static void
resolve_fixup_regions (void)
{
- int i, j, n = cfun->eh->last_region_number;
+ int i, n = cfun->eh->last_region_number;
for (i = 1; i <= n; ++i)
{
struct eh_region *fixup = cfun->eh->region_array[i];
- struct eh_region *cleanup = 0;
- if (! fixup || fixup->type != ERT_FIXUP)
+ if (!fixup || fixup->type != ERT_FIXUP || fixup->u.fixup.resolved)
continue;
- for (j = 1; j <= n; ++j)
- {
- cleanup = cfun->eh->region_array[j];
- if (cleanup && cleanup->type == ERT_CLEANUP
- && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp)
- break;
- }
- if (j > n)
- abort ();
-
- fixup->u.fixup.real_region = cleanup->outer;
+ resolve_one_fixup_region (fixup);
}
}
}
for (insn = insns; insn; insn = NEXT_INSN (insn))
- reachable[uid_region_num[INSN_UID (insn)]] = true;
+ {
+ reachable[uid_region_num[INSN_UID (insn)]] = true;
+
+ if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ for (i = 0; i < 3; i++)
+ {
+ rtx sub = XEXP (PATTERN (insn), i);
+ for (; sub ; sub = NEXT_INSN (sub))
+ reachable[uid_region_num[INSN_UID (sub)]] = true;
+ }
+ }
for (i = cfun->eh->last_region_number; i > 0; --i)
{
t2r_hash (const void *pentry)
{
tree entry = (tree) pentry;
- return TYPE_HASH (TREE_PURPOSE (entry));
+ return TREE_HASH (TREE_PURPOSE (entry));
}
static void
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
- TYPE_HASH (type), INSERT);
+ TREE_HASH (type), INSERT);
if (*slot == NULL)
{
tree runtime = (*lang_eh_runtime_type) (type);
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
- TYPE_HASH (type), NO_INSERT);
+ TREE_HASH (type), NO_INSERT);
/* We should have always inserted the data earlier. */
return TREE_VALUE (*slot);
ttypes_filter_hash (const void *pentry)
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
- return TYPE_HASH (entry->t);
+ return TREE_HASH (entry->t);
}
/* Compare ENTRY with DATA (both struct ttypes_filter) for a @TTypes
tree list;
for (list = entry->t; list ; list = TREE_CHAIN (list))
- h = (h << 5) + (h >> 27) + TYPE_HASH (TREE_VALUE (list));
+ h = (h << 5) + (h >> 27) + TREE_HASH (TREE_VALUE (list));
return h;
}
-/* Add TYPE to cfun->eh->ttype_data, using TYPES_HASH to speed
- up the search. Return the filter value to be used. */
+/* Add TYPE (which may be NULL) to cfun->eh->ttype_data, using TYPES_HASH
+ to speed up the search. Return the filter value to be used. */
static int
add_ttypes_entry (htab_t ttypes_hash, tree type)
struct ttypes_filter **slot, *n;
slot = (struct ttypes_filter **)
- htab_find_slot_with_hash (ttypes_hash, type, TYPE_HASH (type), INSERT);
+ htab_find_slot_with_hash (ttypes_hash, type, TREE_HASH (type), INSERT);
if ((n = *slot) == NULL)
{
htab_delete (ehspec);
}
+/* Emit SEQ into basic block just before INSN (that is assumed to be
+ first instruction of some existing BB and return the newly
+ produced block. */
+static basic_block
+emit_to_new_bb_before (rtx seq, rtx insn)
+{
+ rtx last;
+ basic_block bb;
+ edge e;
+
+ /* If there happens to be an fallthru edge (possibly created by cleanup_cfg
+ call), we don't want it to go into newly created landing pad or other EH
+ construct. */
+ for (e = BLOCK_FOR_INSN (insn)->pred; e; e = e->pred_next)
+ if (e->flags & EDGE_FALLTHRU)
+ force_nonfallthru (e);
+ last = emit_insn_before (seq, insn);
+ if (GET_CODE (last) == BARRIER)
+ last = PREV_INSN (last);
+ bb = create_basic_block (seq, last, BLOCK_FOR_INSN (insn)->prev_bb);
+ update_bb_for_insn (bb);
+ bb->flags |= BB_SUPERBLOCK;
+ return bb;
+}
+
/* Generate the code to actually handle exceptions, which will follow the
landing pads. */
seq = get_insns ();
end_sequence ();
- emit_insn_before (seq, region->u.try.catch->label);
+ emit_to_new_bb_before (seq, region->u.try.catch->label);
+
break;
case ERT_ALLOWED_EXCEPTIONS:
seq = get_insns ();
end_sequence ();
- emit_insn_before (seq, region->label);
+ emit_to_new_bb_before (seq, region->label);
break;
case ERT_CLEANUP:
struct eh_region *region = cfun->eh->region_array[i];
struct eh_region *outer;
rtx seq;
+ rtx barrier;
/* Mind we don't process a region more than once. */
if (!region || region->region_number != i)
start_sequence ();
if (outer)
- emit_jump (outer->post_landing_pad);
+ {
+ edge e;
+ basic_block src, dest;
+
+ emit_jump (outer->post_landing_pad);
+ src = BLOCK_FOR_INSN (region->resume);
+ dest = BLOCK_FOR_INSN (outer->post_landing_pad);
+ while (src->succ)
+ remove_edge (src->succ);
+ e = make_edge (src, dest, 0);
+ e->probability = REG_BR_PROB_BASE;
+ e->count = src->count;
+ }
else
emit_library_call (unwind_resume_libfunc, LCT_THROW,
VOIDmode, 1, cfun->eh->exc_ptr, ptr_mode);
seq = get_insns ();
end_sequence ();
- emit_insn_before (seq, region->resume);
+ barrier = emit_insn_before (seq, region->resume);
+ /* Avoid duplicate barrier. */
+ if (GET_CODE (barrier) != BARRIER)
+ abort ();
+ delete_insn (barrier);
delete_insn (region->resume);
}
}
{
struct eh_region *region = cfun->eh->region_array[i];
rtx seq;
+ basic_block bb;
bool clobbers_hard_regs = false;
+ edge e;
/* Mind we don't process a region more than once. */
if (!region || region->region_number != i)
seq = get_insns ();
end_sequence ();
- emit_insn_before (seq, region->post_landing_pad);
+ bb = emit_to_new_bb_before (seq, region->post_landing_pad);
+ e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+ e->count = bb->count;
+ e->probability = REG_BR_PROB_BASE;
}
}
for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin))
if (GET_CODE (fn_begin) == NOTE
- && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+ && (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG
+ || NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK))
break;
- emit_insn_after (seq, fn_begin);
+ if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+ insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ);
+ else
+ {
+ rtx last = BB_END (ENTRY_BLOCK_PTR->succ->dest);
+ for (; ; fn_begin = NEXT_INSN (fn_begin))
+ if ((GET_CODE (fn_begin) == NOTE
+ && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+ || fn_begin == last)
+ break;
+ emit_insn_after (seq, fn_begin);
+ }
}
/* Call back from expand_function_end to know where we should put
sjlj_emit_function_exit (void)
{
rtx seq;
+ edge e;
start_sequence ();
post-dominates all can_throw_internal instructions. This is
the last possible moment. */
- emit_insn_after (seq, cfun->eh->sjlj_exit_after);
+ for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+ if (e->flags & EDGE_FALLTHRU)
+ break;
+ if (e)
+ {
+ rtx insn;
+
+ /* Figure out whether the place we are supposed to insert libcall
+ is inside the last basic block or after it. In the other case
+ we need to emit to edge. */
+ if (e->src->next_bb != EXIT_BLOCK_PTR)
+ abort ();
+ for (insn = NEXT_INSN (BB_END (e->src)); insn; insn = NEXT_INSN (insn))
+ if (insn == cfun->eh->sjlj_exit_after)
+ break;
+ if (insn)
+ insert_insn_on_edge (seq, e);
+ else
+ {
+ insn = cfun->eh->sjlj_exit_after;
+ if (GET_CODE (insn) == CODE_LABEL)
+ insn = NEXT_INSN (insn);
+ emit_insn_after (seq, insn);
+ }
+ }
}
static void
{
int i, first_reachable;
rtx mem, dispatch, seq, fc;
+ rtx before;
+ basic_block bb;
+ edge e;
fc = cfun->eh->sjlj_fc;
seq = get_insns ();
end_sequence ();
- emit_insn_before (seq, (cfun->eh->region_array[first_reachable]
- ->post_landing_pad));
+ before = cfun->eh->region_array[first_reachable]->post_landing_pad;
+
+ bb = emit_to_new_bb_before (seq, before);
+ e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+ e->count = bb->count;
+ e->probability = REG_BR_PROB_BASE;
}
static void
void
finish_eh_generation (void)
{
+ basic_block bb;
+
/* Nothing to do if no regions created. */
if (cfun->eh->region_tree == NULL)
return;
/* We've totally changed the CFG. Start over. */
find_exception_handler_labels ();
- rebuild_jump_labels (get_insns ());
- find_basic_blocks (get_insns (), max_reg_num (), 0);
+ break_superblocks ();
+ if (USING_SJLJ_EXCEPTIONS)
+ commit_edge_insertions ();
+ FOR_EACH_BB (bb)
+ {
+ edge e, next;
+ bool eh = false;
+ for (e = bb->succ; e; e = next)
+ {
+ next = e->succ_next;
+ if (e->flags & EDGE_EH)
+ {
+ remove_edge (e);
+ eh = true;
+ }
+ }
+ if (eh)
+ make_eh_edge (NULL, bb, BB_END (bb));
+ }
cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
}
\f
for_each_eh_label (void (*callback) (rtx))
{
htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
- (void *)callback);
+ (void *) &callback);
}
static int
for_each_eh_label_1 (void **pentry, void *data)
{
struct ehl_map_entry *entry = *(struct ehl_map_entry **)pentry;
- void (*callback) (rtx) = (void (*) (rtx)) data;
+ void (*callback) (rtx) = *(void (**) (rtx)) data;
(*callback) (entry->label);
return 1;
emit_label (around_label);
}
+
+/* Convert a ptr_mode address ADDR_TREE to a Pmode address controlled by
+ POINTERS_EXTEND_UNSIGNED and return it. */
+
+rtx
+expand_builtin_extend_pointer (tree addr_tree)
+{
+ rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
+ int extend;
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ extend = POINTERS_EXTEND_UNSIGNED;
+#else
+ /* The previous EH code did an unsigned extend by default, so we do this also
+ for consistency. */
+ extend = 1;
+#endif
+
+ return convert_modes (word_mode, ptr_mode, addr, extend);
+}
\f
/* In the following functions, we represent entries in the action table
as 1-based indices. Special cases are:
/* An exception specification adds its filter to the
beginning of the chain. */
next = collect_one_action_chain (ar_hash, region->outer);
- return add_action_record (ar_hash, region->u.allowed.filter,
- next < 0 ? 0 : next);
+
+ /* If there is no next action, terminate the chain. */
+ if (next == -1)
+ next = 0;
+ /* If all outer actions are cleanups or must_not_throw,
+ we'll have no action record for it, since we had wanted
+ to encode these states in the call-site record directly.
+ Add a cleanup action to the chain to catch these. */
+ else if (next <= 0)
+ next = add_action_record (ar_hash, 0, 0);
+
+ return add_action_record (ar_hash, region->u.allowed.filter, next);
case ERT_MUST_NOT_THROW:
/* A must-not-throw region with no inner handlers or cleanups
/* Note that varasm still thinks we're in the function's code section.
The ".endp" directive that will immediately follow will take us back. */
#else
- (*targetm.asm_out.exception_section) ();
+ targetm.asm_out.exception_section ();
#endif
have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0
assemble_align (tt_format_size * BITS_PER_UNIT);
}
- (*targetm.asm_out.internal_label) (asm_out_file, "LLSDA",
+ targetm.asm_out.internal_label (asm_out_file, "LLSDA",
current_function_funcdef_no);
/* The LSDA header. */