#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "function.h"
#include "expr.h"
#include "libfuncs.h"
#include "insn-config.h"
#include "except.h"
#include "integrate.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
#include "dwarf2asm.h"
#include "dwarf2out.h"
#include "dwarf2.h"
#include "toplev.h"
#include "hashtab.h"
#include "intl.h"
#include "ggc.h"
#include "tm_p.h"
#include "target.h"
#include "langhooks.h"
#ifndef EH_RETURN_STACKADJ_RTX
#define EH_RETURN_STACKADJ_RTX 0
#endif
#ifndef EH_RETURN_HANDLER_RTX
#define EH_RETURN_HANDLER_RTX 0
#endif
#ifndef EH_RETURN_DATA_REGNO
#define EH_RETURN_DATA_REGNO(N) INVALID_REGNUM
#endif
int flag_non_call_exceptions;
tree (*lang_protect_cleanup_actions) PARAMS ((void));
int (*lang_eh_type_covers) PARAMS ((tree a, tree b));
tree (*lang_eh_runtime_type) PARAMS ((tree));
struct ehl_map_entry GTY(())
{
rtx label;
struct eh_region *region;
};
static int call_site_base;
static GTY ((param_is (union tree_node)))
htab_t type_to_runtime_map;
static GTY(()) tree sjlj_fc_type_node;
static int sjlj_fc_call_site_ofs;
static int sjlj_fc_data_ofs;
static int sjlj_fc_personality_ofs;
static int sjlj_fc_lsda_ofs;
static int sjlj_fc_jbuf_ofs;
struct eh_region GTY(())
{
struct eh_region *outer;
struct eh_region *inner;
struct eh_region *next_peer;
int region_number;
bitmap aka;
enum eh_region_type
{
ERT_UNKNOWN = 0,
ERT_CLEANUP,
ERT_TRY,
ERT_CATCH,
ERT_ALLOWED_EXCEPTIONS,
ERT_MUST_NOT_THROW,
ERT_THROW,
ERT_FIXUP
} type;
union eh_region_u {
struct eh_region_u_try {
struct eh_region *catch;
struct eh_region *last_catch;
struct eh_region *prev_try;
rtx continue_label;
} GTY ((tag ("ERT_TRY"))) try;
struct eh_region_u_catch {
struct eh_region *next_catch;
struct eh_region *prev_catch;
tree type_list;
tree filter_list;
} GTY ((tag ("ERT_CATCH"))) catch;
struct eh_region_u_allowed {
tree type_list;
int filter;
} GTY ((tag ("ERT_ALLOWED_EXCEPTIONS"))) allowed;
struct eh_region_u_throw {
tree type;
} GTY ((tag ("ERT_THROW"))) throw;
struct eh_region_u_cleanup {
tree exp;
} GTY ((tag ("ERT_CLEANUP"))) cleanup;
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;
rtx label;
rtx landing_pad;
rtx post_landing_pad;
rtx resume;
};
struct call_site_record GTY(())
{
rtx landing_pad;
int action;
};
struct eh_status GTY(())
{
struct eh_region *region_tree;
struct eh_region ** GTY ((length ("%h.last_region_number"))) region_array;
struct eh_region *cur_region;
struct eh_region *try_region;
rtx filter;
rtx exc_ptr;
int built_landing_pads;
int last_region_number;
varray_type ttype_data;
varray_type ehspec_data;
varray_type action_record_data;
htab_t GTY ((param_is (struct ehl_map_entry))) exception_handler_label_map;
struct call_site_record * GTY ((length ("%h.call_site_data_used")))
call_site_data;
int call_site_data_used;
int call_site_data_size;
rtx ehr_stackadj;
rtx ehr_handler;
rtx ehr_label;
rtx sjlj_fc;
rtx sjlj_exit_after;
};
static int t2r_eq PARAMS ((const PTR,
const PTR));
static hashval_t t2r_hash PARAMS ((const PTR));
static void add_type_for_runtime PARAMS ((tree));
static tree lookup_type_for_runtime PARAMS ((tree));
static struct eh_region *expand_eh_region_end PARAMS ((void));
static rtx get_exception_filter PARAMS ((struct function *));
static void collect_eh_region_array PARAMS ((void));
static void resolve_fixup_regions PARAMS ((void));
static void remove_fixup_regions PARAMS ((void));
static void remove_unreachable_regions PARAMS ((rtx));
static void convert_from_eh_region_ranges_1 PARAMS ((rtx *, int *, int));
static struct eh_region *duplicate_eh_region_1 PARAMS ((struct eh_region *,
struct inline_remap *));
static void duplicate_eh_region_2 PARAMS ((struct eh_region *,
struct eh_region **));
static int ttypes_filter_eq PARAMS ((const PTR,
const PTR));
static hashval_t ttypes_filter_hash PARAMS ((const PTR));
static int ehspec_filter_eq PARAMS ((const PTR,
const PTR));
static hashval_t ehspec_filter_hash PARAMS ((const PTR));
static int add_ttypes_entry PARAMS ((htab_t, tree));
static int add_ehspec_entry PARAMS ((htab_t, htab_t,
tree));
static void assign_filter_values PARAMS ((void));
static void build_post_landing_pads PARAMS ((void));
static void connect_post_landing_pads PARAMS ((void));
static void dw2_build_landing_pads PARAMS ((void));
struct sjlj_lp_info;
static bool sjlj_find_directly_reachable_regions
PARAMS ((struct sjlj_lp_info *));
static void sjlj_assign_call_site_values
PARAMS ((rtx, struct sjlj_lp_info *));
static void sjlj_mark_call_sites
PARAMS ((struct sjlj_lp_info *));
static void sjlj_emit_function_enter PARAMS ((rtx));
static void sjlj_emit_function_exit PARAMS ((void));
static void sjlj_emit_dispatch_table
PARAMS ((rtx, struct sjlj_lp_info *));
static void sjlj_build_landing_pads PARAMS ((void));
static hashval_t ehl_hash PARAMS ((const PTR));
static int ehl_eq PARAMS ((const PTR,
const PTR));
static void add_ehl_entry PARAMS ((rtx,
struct eh_region *));
static void remove_exception_handler_label PARAMS ((rtx));
static void remove_eh_handler PARAMS ((struct eh_region *));
static int for_each_eh_label_1 PARAMS ((PTR *, PTR));
struct reachable_info;
enum reachable_code
{
RNL_NOT_CAUGHT,
RNL_MAYBE_CAUGHT,
RNL_CAUGHT,
RNL_BLOCKED
};
static int check_handled PARAMS ((tree, tree));
static void add_reachable_handler
PARAMS ((struct reachable_info *, struct eh_region *,
struct eh_region *));
static enum reachable_code reachable_next_level
PARAMS ((struct eh_region *, tree, struct reachable_info *));
static int action_record_eq PARAMS ((const PTR,
const PTR));
static hashval_t action_record_hash PARAMS ((const PTR));
static int add_action_record PARAMS ((htab_t, int, int));
static int collect_one_action_chain PARAMS ((htab_t,
struct eh_region *));
static int add_call_site PARAMS ((rtx, int));
static void push_uleb128 PARAMS ((varray_type *,
unsigned int));
static void push_sleb128 PARAMS ((varray_type *, int));
#ifndef HAVE_AS_LEB128
static int dw2_size_of_call_site_table PARAMS ((void));
static int sjlj_size_of_call_site_table PARAMS ((void));
#endif
static void dw2_output_call_site_table PARAMS ((void));
static void sjlj_output_call_site_table PARAMS ((void));
int
doing_eh (do_warn)
int do_warn;
{
if (! flag_exceptions)
{
static int warned = 0;
if (! warned && do_warn)
{
error ("exception handling disabled, use -fexceptions to enable");
warned = 1;
}
return 0;
}
return 1;
}
void
init_eh ()
{
if (! flag_exceptions)
return;
type_to_runtime_map = htab_create_ggc (31, t2r_hash, t2r_eq, NULL);
if (USING_SJLJ_EXCEPTIONS)
{
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);
f_prev = build_decl (FIELD_DECL, get_identifier ("__prev"),
build_pointer_type (sjlj_fc_type_node));
DECL_FIELD_CONTEXT (f_prev) = sjlj_fc_type_node;
f_cs = build_decl (FIELD_DECL, get_identifier ("__call_site"),
integer_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);
f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp);
DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node;
f_per = build_decl (FIELD_DECL, get_identifier ("__personality"),
ptr_type_node);
DECL_FIELD_CONTEXT (f_per) = sjlj_fc_type_node;
f_lsda = build_decl (FIELD_DECL, get_identifier ("__lsda"),
ptr_type_node);
DECL_FIELD_CONTEXT (f_lsda) = sjlj_fc_type_node;
#ifdef DONT_USE_BUILTIN_SETJMP
#ifdef JMP_BUF_SIZE
tmp = build_int_2 (JMP_BUF_SIZE - 1, 0);
#else
tmp = build_int_2 (FIRST_PSEUDO_REGISTER + 2 - 1, 0);
#endif
#else
tmp = build_int_2 ((GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL))
/ GET_MODE_SIZE (Pmode)) + 2 - 1, 0);
#endif
tmp = build_index_type (tmp);
tmp = build_array_type (ptr_type_node, tmp);
f_jbuf = build_decl (FIELD_DECL, get_identifier ("__jbuf"), tmp);
#ifdef DONT_USE_BUILTIN_SETJMP
DECL_ALIGN (f_jbuf) = BIGGEST_ALIGNMENT;
DECL_USER_ALIGN (f_jbuf) = 1;
#endif
DECL_FIELD_CONTEXT (f_jbuf) = sjlj_fc_type_node;
TYPE_FIELDS (sjlj_fc_type_node) = f_prev;
TREE_CHAIN (f_prev) = f_cs;
TREE_CHAIN (f_cs) = f_data;
TREE_CHAIN (f_data) = f_per;
TREE_CHAIN (f_per) = f_lsda;
TREE_CHAIN (f_lsda) = f_jbuf;
layout_type (sjlj_fc_type_node);
sjlj_fc_call_site_ofs
= (tree_low_cst (DECL_FIELD_OFFSET (f_cs), 1)
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f_cs), 1) / BITS_PER_UNIT);
sjlj_fc_data_ofs
= (tree_low_cst (DECL_FIELD_OFFSET (f_data), 1)
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f_data), 1) / BITS_PER_UNIT);
sjlj_fc_personality_ofs
= (tree_low_cst (DECL_FIELD_OFFSET (f_per), 1)
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f_per), 1) / BITS_PER_UNIT);
sjlj_fc_lsda_ofs
= (tree_low_cst (DECL_FIELD_OFFSET (f_lsda), 1)
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f_lsda), 1) / BITS_PER_UNIT);
sjlj_fc_jbuf_ofs
= (tree_low_cst (DECL_FIELD_OFFSET (f_jbuf), 1)
+ tree_low_cst (DECL_FIELD_BIT_OFFSET (f_jbuf), 1) / BITS_PER_UNIT);
}
}
void
init_eh_for_function ()
{
cfun->eh = (struct eh_status *)
ggc_alloc_cleared (sizeof (struct eh_status));
}
void
expand_eh_region_start ()
{
struct eh_region *new_region;
struct eh_region *cur_region;
rtx note;
if (! doing_eh (0))
return;
new_region = (struct eh_region *) ggc_alloc_cleared (sizeof (*new_region));
cur_region = cfun->eh->cur_region;
new_region->outer = cur_region;
if (cur_region)
{
new_region->next_peer = cur_region->inner;
cur_region->inner = new_region;
}
else
{
new_region->next_peer = cfun->eh->region_tree;
cfun->eh->region_tree = new_region;
}
cfun->eh->cur_region = new_region;
new_region->region_number = ++cfun->eh->last_region_number;
note = emit_note (NULL, NOTE_INSN_EH_REGION_BEG);
NOTE_EH_HANDLER (note) = new_region->region_number;
}
static struct eh_region *
expand_eh_region_end ()
{
struct eh_region *cur_region = cfun->eh->cur_region;
rtx note;
note = emit_note (NULL, NOTE_INSN_EH_REGION_END);
NOTE_EH_HANDLER (note) = cur_region->region_number;
cfun->eh->cur_region = cur_region->outer;
return cur_region;
}
void
expand_eh_region_end_cleanup (handler)
tree handler;
{
struct eh_region *region;
tree protect_cleanup_actions;
rtx around_label;
rtx data_save[2];
if (! doing_eh (0))
return;
region = expand_eh_region_end ();
region->type = ERT_CLEANUP;
region->label = gen_label_rtx ();
region->u.cleanup.exp = handler;
around_label = gen_label_rtx ();
emit_jump (around_label);
emit_label (region->label);
protect_cleanup_actions
= (lang_protect_cleanup_actions
? (*lang_protect_cleanup_actions) ()
: NULL_TREE);
if (protect_cleanup_actions)
expand_eh_region_start ();
data_save[0] = gen_reg_rtx (ptr_mode);
emit_move_insn (data_save[0], get_exception_pointer (cfun));
data_save[1] = gen_reg_rtx (word_mode);
emit_move_insn (data_save[1], get_exception_filter (cfun));
expand_expr (handler, const0_rtx, VOIDmode, 0);
emit_move_insn (cfun->eh->exc_ptr, data_save[0]);
emit_move_insn (cfun->eh->filter, data_save[1]);
if (protect_cleanup_actions)
expand_eh_region_end_must_not_throw (protect_cleanup_actions);
do_pending_stack_adjust ();
region->resume
= emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
emit_barrier ();
emit_label (around_label);
}
void
expand_start_all_catch ()
{
struct eh_region *region;
if (! doing_eh (1))
return;
region = expand_eh_region_end ();
region->type = ERT_TRY;
region->u.try.prev_try = cfun->eh->try_region;
region->u.try.continue_label = gen_label_rtx ();
cfun->eh->try_region = region;
emit_jump (region->u.try.continue_label);
}
void
expand_start_catch (type_or_list)
tree type_or_list;
{
struct eh_region *t, *c, *l;
tree type_list;
if (! doing_eh (0))
return;
type_list = type_or_list;
if (type_or_list)
{
tree type_node;
if (TREE_CODE (type_or_list) != TREE_LIST)
type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE);
type_node = type_list;
for (; type_node; type_node = TREE_CHAIN (type_node))
add_type_for_runtime (TREE_VALUE (type_node));
}
expand_eh_region_start ();
t = cfun->eh->try_region;
c = cfun->eh->cur_region;
c->type = ERT_CATCH;
c->u.catch.type_list = type_list;
c->label = gen_label_rtx ();
l = t->u.try.last_catch;
c->u.catch.prev_catch = l;
if (l)
l->u.catch.next_catch = c;
else
t->u.try.catch = c;
t->u.try.last_catch = c;
emit_label (c->label);
}
void
expand_end_catch ()
{
struct eh_region *try_region, *catch_region;
if (! doing_eh (0))
return;
catch_region = expand_eh_region_end ();
try_region = cfun->eh->try_region;
emit_jump (try_region->u.try.continue_label);
}
void
expand_end_all_catch ()
{
struct eh_region *try_region;
if (! doing_eh (0))
return;
try_region = cfun->eh->try_region;
cfun->eh->try_region = try_region->u.try.prev_try;
emit_label (try_region->u.try.continue_label);
}
void
expand_eh_region_end_allowed (allowed, failure)
tree allowed, failure;
{
struct eh_region *region;
rtx around_label;
if (! doing_eh (0))
return;
region = expand_eh_region_end ();
region->type = ERT_ALLOWED_EXCEPTIONS;
region->u.allowed.type_list = allowed;
region->label = gen_label_rtx ();
for (; allowed ; allowed = TREE_CHAIN (allowed))
add_type_for_runtime (TREE_VALUE (allowed));
around_label = gen_label_rtx ();
emit_jump (around_label);
emit_label (region->label);
expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
do_pending_stack_adjust ();
emit_label (around_label);
}
void
expand_eh_region_end_must_not_throw (failure)
tree failure;
{
struct eh_region *region;
rtx around_label;
if (! doing_eh (0))
return;
region = expand_eh_region_end ();
region->type = ERT_MUST_NOT_THROW;
region->label = gen_label_rtx ();
around_label = gen_label_rtx ();
emit_jump (around_label);
emit_label (region->label);
expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
emit_label (around_label);
}
void
expand_eh_region_end_throw (type)
tree type;
{
struct eh_region *region;
if (! doing_eh (0))
return;
region = expand_eh_region_end ();
region->type = ERT_THROW;
region->u.throw.type = type;
}
void
expand_eh_region_end_fixup (handler)
tree handler;
{
struct eh_region *fixup;
if (! doing_eh (0))
return;
fixup = expand_eh_region_end ();
fixup->type = ERT_FIXUP;
fixup->u.fixup.cleanup_exp = handler;
}
rtx
get_exception_pointer (fun)
struct function *fun;
{
rtx exc_ptr = fun->eh->exc_ptr;
if (fun == cfun && ! exc_ptr)
{
exc_ptr = gen_reg_rtx (ptr_mode);
fun->eh->exc_ptr = exc_ptr;
}
return exc_ptr;
}
static rtx
get_exception_filter (fun)
struct function *fun;
{
rtx filter = fun->eh->filter;
if (fun == cfun && ! filter)
{
filter = gen_reg_rtx (word_mode);
fun->eh->filter = filter;
}
return filter;
}
static void
collect_eh_region_array ()
{
struct eh_region **array, *i;
i = cfun->eh->region_tree;
if (! i)
return;
array = ggc_alloc_cleared ((cfun->eh->last_region_number + 1)
* sizeof (*array));
cfun->eh->region_array = array;
while (1)
{
array[i->region_number] = i;
if (i->inner)
i = i->inner;
else if (i->next_peer)
i = i->next_peer;
else
{
do {
i = i->outer;
if (i == NULL)
return;
} while (i->next_peer == NULL);
i = i->next_peer;
}
}
}
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 ()
{
int i, n = cfun->eh->last_region_number;
for (i = 1; i <= n; ++i)
{
struct eh_region *fixup = cfun->eh->region_array[i];
if (!fixup || fixup->type != ERT_FIXUP || fixup->u.fixup.resolved)
continue;
resolve_one_fixup_region (fixup);
}
}
static void
remove_fixup_regions ()
{
int i;
rtx insn, note;
struct eh_region *fixup;
for (insn = get_insns(); insn ; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& (note = find_reg_note (insn, REG_EH_REGION, NULL))
&& INTVAL (XEXP (note, 0)) > 0
&& (fixup = cfun->eh->region_array[INTVAL (XEXP (note, 0))])
&& fixup->type == ERT_FIXUP)
{
if (fixup->u.fixup.real_region)
XEXP (note, 0) = GEN_INT (fixup->u.fixup.real_region->region_number);
else
remove_note (insn, note);
}
for (i = cfun->eh->last_region_number; i > 0; --i)
{
fixup = cfun->eh->region_array[i];
if (! fixup)
continue;
if (fixup->type == ERT_CLEANUP)
fixup->u.cleanup.exp = NULL_TREE;
if (fixup->type != ERT_FIXUP)
continue;
if (fixup->inner)
{
struct eh_region *parent, *p, **pp;
parent = fixup->u.fixup.real_region;
for (p = fixup->inner; ; p = p->next_peer)
{
p->outer = parent;
if (! p->next_peer)
break;
}
if (parent)
pp = &parent->inner;
else
pp = &cfun->eh->region_tree;
p->next_peer = *pp;
*pp = fixup->inner;
fixup->inner = NULL;
}
remove_eh_handler (fixup);
}
}
static void
remove_unreachable_regions (insns)
rtx insns;
{
int i, *uid_region_num;
bool *reachable;
struct eh_region *r;
rtx insn;
uid_region_num = xcalloc (get_max_uid (), sizeof(int));
reachable = xcalloc (cfun->eh->last_region_number + 1, sizeof(bool));
for (i = cfun->eh->last_region_number; i > 0; --i)
{
r = cfun->eh->region_array[i];
if (!r || r->region_number != i)
continue;
if (r->resume)
{
if (uid_region_num[INSN_UID (r->resume)])
abort ();
uid_region_num[INSN_UID (r->resume)] = i;
}
if (r->label)
{
if (uid_region_num[INSN_UID (r->label)])
abort ();
uid_region_num[INSN_UID (r->label)] = i;
}
if (r->type == ERT_TRY && r->u.try.continue_label)
{
if (uid_region_num[INSN_UID (r->u.try.continue_label)])
abort ();
uid_region_num[INSN_UID (r->u.try.continue_label)] = i;
}
}
for (insn = insns; insn; insn = NEXT_INSN (insn))
reachable[uid_region_num[INSN_UID (insn)]] = true;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
r = cfun->eh->region_array[i];
if (r && r->region_number == i && !reachable[i])
{
if (r->type == ERT_THROW
&& r->outer
&& reachable[r->outer->region_number])
continue;
remove_eh_handler (r);
}
}
free (reachable);
free (uid_region_num);
}
static void
convert_from_eh_region_ranges_1 (pinsns, orig_sp, cur)
rtx *pinsns;
int *orig_sp;
int cur;
{
int *sp = orig_sp;
rtx insn, next;
for (insn = *pinsns; insn ; insn = next)
{
next = NEXT_INSN (insn);
if (GET_CODE (insn) == NOTE)
{
int kind = NOTE_LINE_NUMBER (insn);
if (kind == NOTE_INSN_EH_REGION_BEG
|| kind == NOTE_INSN_EH_REGION_END)
{
if (kind == NOTE_INSN_EH_REGION_BEG)
{
struct eh_region *r;
*sp++ = cur;
cur = NOTE_EH_HANDLER (insn);
r = cfun->eh->region_array[cur];
if (r->type == ERT_FIXUP)
{
r = r->u.fixup.real_region;
cur = r ? r->region_number : 0;
}
else if (r->type == ERT_CATCH)
{
r = r->outer;
cur = r ? r->region_number : 0;
}
}
else
cur = *--sp;
if (insn == *pinsns)
*pinsns = next;
remove_insn (insn);
continue;
}
}
else if (INSN_P (insn))
{
if (cur > 0
&& ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
&& (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& GET_CODE (PATTERN (insn)) != CLOBBER
&& GET_CODE (PATTERN (insn)) != USE
&& may_trap_p (PATTERN (insn)))))
{
REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (cur),
REG_NOTES (insn));
}
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
{
convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 0),
sp, cur);
convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 1),
sp, cur);
convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 2),
sp, cur);
}
}
}
if (sp != orig_sp)
abort ();
}
void
convert_from_eh_region_ranges ()
{
int *stack;
rtx insns;
collect_eh_region_array ();
resolve_fixup_regions ();
stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1));
insns = get_insns ();
convert_from_eh_region_ranges_1 (&insns, stack, 0);
free (stack);
remove_fixup_regions ();
remove_unreachable_regions (insns);
}
static void
add_ehl_entry (label, region)
rtx label;
struct eh_region *region;
{
struct ehl_map_entry **slot, *entry;
LABEL_PRESERVE_P (label) = 1;
entry = (struct ehl_map_entry *) ggc_alloc (sizeof (*entry));
entry->label = label;
entry->region = region;
slot = (struct ehl_map_entry **)
htab_find_slot (cfun->eh->exception_handler_label_map, entry, INSERT);
if (*slot && !cfun->eh->built_landing_pads)
abort ();
*slot = entry;
}
void
find_exception_handler_labels ()
{
int i;
if (cfun->eh->exception_handler_label_map)
htab_empty (cfun->eh->exception_handler_label_map);
else
{
cfun->eh->exception_handler_label_map
= htab_create_ggc (cfun->eh->last_region_number * 3 / 2,
ehl_hash, ehl_eq, NULL);
}
if (cfun->eh->region_tree == NULL)
return;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region = cfun->eh->region_array[i];
rtx lab;
if (! region || region->region_number != i)
continue;
if (cfun->eh->built_landing_pads)
lab = region->landing_pad;
else
lab = region->label;
if (lab)
add_ehl_entry (lab, region);
}
if (USING_SJLJ_EXCEPTIONS && ! cfun->eh->built_landing_pads)
add_ehl_entry (return_label, NULL);
}
bool
current_function_has_exception_handlers ()
{
int i;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region = cfun->eh->region_array[i];
if (! region || region->region_number != i)
continue;
if (region->type != ERT_THROW)
return true;
}
return false;
}
static struct eh_region *
duplicate_eh_region_1 (o, map)
struct eh_region *o;
struct inline_remap *map;
{
struct eh_region *n
= (struct eh_region *) ggc_alloc_cleared (sizeof (struct eh_region));
n->region_number = o->region_number + cfun->eh->last_region_number;
n->type = o->type;
switch (n->type)
{
case ERT_CLEANUP:
case ERT_MUST_NOT_THROW:
break;
case ERT_TRY:
if (o->u.try.continue_label)
n->u.try.continue_label
= get_label_from_map (map,
CODE_LABEL_NUMBER (o->u.try.continue_label));
break;
case ERT_CATCH:
n->u.catch.type_list = o->u.catch.type_list;
break;
case ERT_ALLOWED_EXCEPTIONS:
n->u.allowed.type_list = o->u.allowed.type_list;
break;
case ERT_THROW:
n->u.throw.type = o->u.throw.type;
default:
abort ();
}
if (o->label)
n->label = get_label_from_map (map, CODE_LABEL_NUMBER (o->label));
if (o->resume)
{
n->resume = map->insn_map[INSN_UID (o->resume)];
if (n->resume == NULL)
abort ();
}
return n;
}
static void
duplicate_eh_region_2 (o, n_array)
struct eh_region *o;
struct eh_region **n_array;
{
struct eh_region *n = n_array[o->region_number];
switch (n->type)
{
case ERT_TRY:
n->u.try.catch = n_array[o->u.try.catch->region_number];
n->u.try.last_catch = n_array[o->u.try.last_catch->region_number];
break;
case ERT_CATCH:
if (o->u.catch.next_catch)
n->u.catch.next_catch = n_array[o->u.catch.next_catch->region_number];
if (o->u.catch.prev_catch)
n->u.catch.prev_catch = n_array[o->u.catch.prev_catch->region_number];
break;
default:
break;
}
if (o->outer)
n->outer = n_array[o->outer->region_number];
if (o->inner)
n->inner = n_array[o->inner->region_number];
if (o->next_peer)
n->next_peer = n_array[o->next_peer->region_number];
}
int
duplicate_eh_regions (ifun, map)
struct function *ifun;
struct inline_remap *map;
{
int ifun_last_region_number = ifun->eh->last_region_number;
struct eh_region **n_array, *root, *cur;
int i;
if (ifun_last_region_number == 0)
return 0;
n_array = xcalloc (ifun_last_region_number + 1, sizeof (*n_array));
for (i = 1; i <= ifun_last_region_number; ++i)
{
cur = ifun->eh->region_array[i];
if (!cur || cur->region_number != i)
continue;
n_array[i] = duplicate_eh_region_1 (cur, map);
}
for (i = 1; i <= ifun_last_region_number; ++i)
{
cur = ifun->eh->region_array[i];
if (!cur || cur->region_number != i)
continue;
duplicate_eh_region_2 (cur, n_array);
}
root = n_array[ifun->eh->region_tree->region_number];
cur = cfun->eh->cur_region;
if (cur)
{
struct eh_region *p = cur->inner;
if (p)
{
while (p->next_peer)
p = p->next_peer;
p->next_peer = root;
}
else
cur->inner = root;
for (i = 1; i <= ifun_last_region_number; ++i)
if (n_array[i] && n_array[i]->outer == NULL)
n_array[i]->outer = cur;
}
else
{
struct eh_region *p = cfun->eh->region_tree;
if (p)
{
while (p->next_peer)
p = p->next_peer;
p->next_peer = root;
}
else
cfun->eh->region_tree = root;
}
free (n_array);
i = cfun->eh->last_region_number;
cfun->eh->last_region_number = i + ifun_last_region_number;
return i;
}
static int
t2r_eq (pentry, pdata)
const PTR pentry;
const PTR pdata;
{
tree entry = (tree) pentry;
tree data = (tree) pdata;
return TREE_PURPOSE (entry) == data;
}
static hashval_t
t2r_hash (pentry)
const PTR pentry;
{
tree entry = (tree) pentry;
return TYPE_HASH (TREE_PURPOSE (entry));
}
static void
add_type_for_runtime (type)
tree type;
{
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
TYPE_HASH (type), INSERT);
if (*slot == NULL)
{
tree runtime = (*lang_eh_runtime_type) (type);
*slot = tree_cons (type, runtime, NULL_TREE);
}
}
static tree
lookup_type_for_runtime (type)
tree type;
{
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
TYPE_HASH (type), NO_INSERT);
return TREE_VALUE (*slot);
}
struct ttypes_filter GTY(())
{
tree t;
int filter;
};
static int
ttypes_filter_eq (pentry, pdata)
const PTR pentry;
const PTR pdata;
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
tree data = (tree) pdata;
return entry->t == data;
}
static hashval_t
ttypes_filter_hash (pentry)
const PTR pentry;
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
return TYPE_HASH (entry->t);
}
static int
ehspec_filter_eq (pentry, pdata)
const PTR pentry;
const PTR pdata;
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
const struct ttypes_filter *data = (const struct ttypes_filter *) pdata;
return type_list_equal (entry->t, data->t);
}
static hashval_t
ehspec_filter_hash (pentry)
const PTR pentry;
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
hashval_t h = 0;
tree list;
for (list = entry->t; list ; list = TREE_CHAIN (list))
h = (h << 5) + (h >> 27) + TYPE_HASH (TREE_VALUE (list));
return h;
}
static int
add_ttypes_entry (ttypes_hash, type)
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);
if ((n = *slot) == NULL)
{
n = (struct ttypes_filter *) xmalloc (sizeof (*n));
n->t = type;
n->filter = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) + 1;
*slot = n;
VARRAY_PUSH_TREE (cfun->eh->ttype_data, type);
}
return n->filter;
}
static int
add_ehspec_entry (ehspec_hash, ttypes_hash, list)
htab_t ehspec_hash;
htab_t ttypes_hash;
tree list;
{
struct ttypes_filter **slot, *n;
struct ttypes_filter dummy;
dummy.t = list;
slot = (struct ttypes_filter **)
htab_find_slot (ehspec_hash, &dummy, INSERT);
if ((n = *slot) == NULL)
{
n = (struct ttypes_filter *) xmalloc (sizeof (*n));
n->t = list;
n->filter = -(VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) + 1);
*slot = n;
for (; list ; list = TREE_CHAIN (list))
push_uleb128 (&cfun->eh->ehspec_data,
add_ttypes_entry (ttypes_hash, TREE_VALUE (list)));
VARRAY_PUSH_UCHAR (cfun->eh->ehspec_data, 0);
}
return n->filter;
}
static void
assign_filter_values ()
{
int i;
htab_t ttypes, ehspec;
VARRAY_TREE_INIT (cfun->eh->ttype_data, 16, "ttype_data");
VARRAY_UCHAR_INIT (cfun->eh->ehspec_data, 64, "ehspec_data");
ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free);
ehspec = htab_create (31, ehspec_filter_hash, ehspec_filter_eq, free);
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *r = cfun->eh->region_array[i];
if (!r || r->region_number != i)
continue;
switch (r->type)
{
case ERT_CATCH:
r->u.catch.filter_list = NULL_TREE;
if (r->u.catch.type_list != NULL)
{
tree tp_node = r->u.catch.type_list;
for (;tp_node; tp_node = TREE_CHAIN (tp_node))
{
int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node));
tree flt_node = build_int_2 (flt, 0);
r->u.catch.filter_list
= tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list);
}
}
else
{
int flt = add_ttypes_entry (ttypes, NULL);
tree flt_node = build_int_2 (flt, 0);
r->u.catch.filter_list
= tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list);
}
break;
case ERT_ALLOWED_EXCEPTIONS:
r->u.allowed.filter
= add_ehspec_entry (ehspec, ttypes, r->u.allowed.type_list);
break;
default:
break;
}
}
htab_delete (ttypes);
htab_delete (ehspec);
}
static void
build_post_landing_pads ()
{
int i;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region = cfun->eh->region_array[i];
rtx seq;
if (!region || region->region_number != i)
continue;
switch (region->type)
{
case ERT_TRY:
region->post_landing_pad = gen_label_rtx ();
start_sequence ();
emit_label (region->post_landing_pad);
{
struct eh_region *c;
for (c = region->u.try.catch; c ; c = c->u.catch.next_catch)
{
if (c->u.catch.type_list == NULL)
emit_jump (c->label);
else
{
tree tp_node = c->u.catch.type_list;
tree flt_node = c->u.catch.filter_list;
for (; tp_node; )
{
emit_cmp_and_jump_insns
(cfun->eh->filter,
GEN_INT (tree_low_cst (TREE_VALUE (flt_node), 0)),
EQ, NULL_RTX, word_mode, 0, c->label);
tp_node = TREE_CHAIN (tp_node);
flt_node = TREE_CHAIN (flt_node);
}
}
}
}
region->resume
= emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
emit_barrier ();
seq = get_insns ();
end_sequence ();
emit_insn_before (seq, region->u.try.catch->label);
break;
case ERT_ALLOWED_EXCEPTIONS:
region->post_landing_pad = gen_label_rtx ();
start_sequence ();
emit_label (region->post_landing_pad);
emit_cmp_and_jump_insns (cfun->eh->filter,
GEN_INT (region->u.allowed.filter),
EQ, NULL_RTX, word_mode, 0, region->label);
region->resume
= emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
emit_barrier ();
seq = get_insns ();
end_sequence ();
emit_insn_before (seq, region->label);
break;
case ERT_CLEANUP:
case ERT_MUST_NOT_THROW:
region->post_landing_pad = region->label;
break;
case ERT_CATCH:
case ERT_THROW:
break;
default:
abort ();
}
}
}
static void
connect_post_landing_pads ()
{
int i;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region = cfun->eh->region_array[i];
struct eh_region *outer;
rtx seq;
if (!region || region->region_number != i)
continue;
if (! region->resume || INSN_DELETED_P (region->resume))
continue;
for (outer = region->outer; outer ; outer = outer->outer)
if (outer->post_landing_pad)
break;
start_sequence ();
if (outer)
emit_jump (outer->post_landing_pad);
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);
delete_insn (region->resume);
}
}
static void
dw2_build_landing_pads ()
{
int i;
unsigned int j;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
struct eh_region *region = cfun->eh->region_array[i];
rtx seq;
bool clobbers_hard_regs = false;
if (!region || region->region_number != i)
continue;
if (region->type != ERT_CLEANUP
&& region->type != ERT_TRY
&& region->type != ERT_ALLOWED_EXCEPTIONS)
continue;
start_sequence ();
region->landing_pad = gen_label_rtx ();
emit_label (region->landing_pad);
#ifdef HAVE_exception_receiver
if (HAVE_exception_receiver)
emit_insn (gen_exception_receiver ());
else
#endif
#ifdef HAVE_nonlocal_goto_receiver
if (HAVE_nonlocal_goto_receiver)
emit_insn (gen_nonlocal_goto_receiver ());
else
#endif
{ }
for (j = 0; ; ++j)
{
unsigned r = EH_RETURN_DATA_REGNO (j);
if (r == INVALID_REGNUM)
break;
if (! call_used_regs[r])
{
emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, r)));
clobbers_hard_regs = true;
}
}
if (clobbers_hard_regs)
{
emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
}
emit_move_insn (cfun->eh->exc_ptr,
gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
emit_move_insn (cfun->eh->filter,
gen_rtx_REG (word_mode, EH_RETURN_DATA_REGNO (1)));
seq = get_insns ();
end_sequence ();
emit_insn_before (seq, region->post_landing_pad);
}
}
struct sjlj_lp_info
{
int directly_reachable;
int action_index;
int dispatch_index;
int call_site_index;
};
static bool
sjlj_find_directly_reachable_regions (lp_info)
struct sjlj_lp_info *lp_info;
{
rtx insn;
bool found_one = false;
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
{
struct eh_region *region;
enum reachable_code rc;
tree type_thrown;
rtx note;
if (! INSN_P (insn))
continue;
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note || INTVAL (XEXP (note, 0)) <= 0)
continue;
region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
type_thrown = NULL_TREE;
if (region->type == ERT_THROW)
{
type_thrown = region->u.throw.type;
region = region->outer;
}
rc = RNL_NOT_CAUGHT;
for (; region; region = region->outer)
{
rc = reachable_next_level (region, type_thrown, 0);
if (rc != RNL_NOT_CAUGHT)
break;
}
if (rc == RNL_MAYBE_CAUGHT || rc == RNL_CAUGHT)
{
lp_info[region->region_number].directly_reachable = 1;
found_one = true;
}
}
return found_one;
}
static void
sjlj_assign_call_site_values (dispatch_label, lp_info)
rtx dispatch_label;
struct sjlj_lp_info *lp_info;
{
htab_t ar_hash;
int i, index;
VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data");
ar_hash = htab_create (31, action_record_hash, action_record_eq, free);
for (i = cfun->eh->last_region_number; i > 0; --i)
if (lp_info[i].directly_reachable)
{
struct eh_region *r = cfun->eh->region_array[i];
r->landing_pad = dispatch_label;
lp_info[i].action_index = collect_one_action_chain (ar_hash, r);
if (lp_info[i].action_index != -1)
cfun->uses_eh_lsda = 1;
}
htab_delete (ar_hash);
index = 0;
for (i = cfun->eh->last_region_number; i > 0; --i)
if (lp_info[i].directly_reachable)
lp_info[i].dispatch_index = index++;
call_site_base = 1;
for (i = cfun->eh->last_region_number; i > 0; --i)
if (lp_info[i].directly_reachable)
{
int action = lp_info[i].action_index;
if (action == -2)
index = 0;
else if (action == -1)
index = -1;
else
index = add_call_site (GEN_INT (lp_info[i].dispatch_index), action);
lp_info[i].call_site_index = index;
}
}
static void
sjlj_mark_call_sites (lp_info)
struct sjlj_lp_info *lp_info;
{
int last_call_site = -2;
rtx insn, mem;
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
{
struct eh_region *region;
int this_call_site;
rtx note, before, p;
if (GET_CODE (insn) == CODE_LABEL)
last_call_site = -2;
if (! INSN_P (insn))
continue;
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note)
{
if (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn))))
this_call_site = -1;
else
continue;
}
else
{
if (INTVAL (XEXP (note, 0)) <= 0)
continue;
region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
this_call_site = lp_info[region->region_number].call_site_index;
}
if (this_call_site == last_call_site)
continue;
before = insn;
if (GET_CODE (insn) == CALL_INSN)
before = find_first_parameter_load (insn, NULL_RTX);
start_sequence ();
mem = adjust_address (cfun->eh->sjlj_fc, TYPE_MODE (integer_type_node),
sjlj_fc_call_site_ofs);
emit_move_insn (mem, GEN_INT (this_call_site));
p = get_insns ();
end_sequence ();
emit_insn_before (p, before);
last_call_site = this_call_site;
}
}
static void
sjlj_emit_function_enter (dispatch_label)
rtx dispatch_label;
{
rtx fn_begin, fc, mem, seq;
fc = cfun->eh->sjlj_fc;
start_sequence ();
assemble_external_libcall (eh_personality_libfunc);
mem = adjust_address (fc, Pmode, sjlj_fc_personality_ofs);
emit_move_insn (mem, eh_personality_libfunc);
mem = adjust_address (fc, Pmode, sjlj_fc_lsda_ofs);
if (cfun->uses_eh_lsda)
{
char buf[20];
ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no);
emit_move_insn (mem, gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)));
}
else
emit_move_insn (mem, const0_rtx);
#ifdef DONT_USE_BUILTIN_SETJMP
{
rtx x, note;
x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_RETURNS_TWICE,
TYPE_MODE (integer_type_node), 1,
plus_constant (XEXP (fc, 0),
sjlj_fc_jbuf_ofs), Pmode);
note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, x, const0_rtx);
emit_cmp_and_jump_insns (x, const0_rtx, NE, 0,
TYPE_MODE (integer_type_node), 0, dispatch_label);
}
#else
expand_builtin_setjmp_setup (plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs),
dispatch_label);
#endif
emit_library_call (unwind_sjlj_register_libfunc, LCT_NORMAL, VOIDmode,
1, XEXP (fc, 0), Pmode);
seq = get_insns ();
end_sequence ();
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)
break;
emit_insn_after (seq, fn_begin);
}
void
sjlj_emit_function_exit_after (after)
rtx after;
{
cfun->eh->sjlj_exit_after = after;
}
static void
sjlj_emit_function_exit ()
{
rtx seq;
start_sequence ();
emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode,
1, XEXP (cfun->eh->sjlj_fc, 0), Pmode);
seq = get_insns ();
end_sequence ();
emit_insn_after (seq, cfun->eh->sjlj_exit_after);
}
static void
sjlj_emit_dispatch_table (dispatch_label, lp_info)
rtx dispatch_label;
struct sjlj_lp_info *lp_info;
{
int i, first_reachable;
rtx mem, dispatch, seq, fc;
fc = cfun->eh->sjlj_fc;
start_sequence ();
emit_label (dispatch_label);
#ifndef DONT_USE_BUILTIN_SETJMP
expand_builtin_setjmp_receiver (dispatch_label);
#endif
mem = adjust_address (fc, TYPE_MODE (integer_type_node),
sjlj_fc_call_site_ofs);
dispatch = copy_to_reg (mem);
mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs);
if (word_mode != Pmode)
{
#ifdef POINTERS_EXTEND_UNSIGNED
mem = convert_memory_address (Pmode, mem);
#else
mem = convert_to_mode (Pmode, mem, 0);
#endif
}
emit_move_insn (cfun->eh->exc_ptr, mem);
mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs + UNITS_PER_WORD);
emit_move_insn (cfun->eh->filter, mem);
first_reachable = 0;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
if (! lp_info[i].directly_reachable)
continue;
if (! first_reachable)
{
first_reachable = i;
continue;
}
emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index),
EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0,
cfun->eh->region_array[i]->post_landing_pad);
}
seq = get_insns ();
end_sequence ();
emit_insn_before (seq, (cfun->eh->region_array[first_reachable]
->post_landing_pad));
}
static void
sjlj_build_landing_pads ()
{
struct sjlj_lp_info *lp_info;
lp_info = (struct sjlj_lp_info *) xcalloc (cfun->eh->last_region_number + 1,
sizeof (struct sjlj_lp_info));
if (sjlj_find_directly_reachable_regions (lp_info))
{
rtx dispatch_label = gen_label_rtx ();
cfun->eh->sjlj_fc
= assign_stack_local (TYPE_MODE (sjlj_fc_type_node),
int_size_in_bytes (sjlj_fc_type_node),
TYPE_ALIGN (sjlj_fc_type_node));
sjlj_assign_call_site_values (dispatch_label, lp_info);
sjlj_mark_call_sites (lp_info);
sjlj_emit_function_enter (dispatch_label);
sjlj_emit_dispatch_table (dispatch_label, lp_info);
sjlj_emit_function_exit ();
}
free (lp_info);
}
void
finish_eh_generation ()
{
if (cfun->eh->region_tree == NULL)
return;
cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
get_exception_pointer (cfun);
get_exception_filter (cfun);
assign_filter_values ();
build_post_landing_pads ();
connect_post_landing_pads ();
if (USING_SJLJ_EXCEPTIONS)
sjlj_build_landing_pads ();
else
dw2_build_landing_pads ();
cfun->eh->built_landing_pads = 1;
find_exception_handler_labels ();
rebuild_jump_labels (get_insns ());
find_basic_blocks (get_insns (), max_reg_num (), 0);
cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
}
static hashval_t
ehl_hash (pentry)
const PTR pentry;
{
struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry;
const hashval_t scaled_golden_ratio = 0x9e3779b9;
return CODE_LABEL_NUMBER (entry->label) * scaled_golden_ratio;
}
static int
ehl_eq (pentry, pdata)
const PTR pentry;
const PTR pdata;
{
struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry;
struct ehl_map_entry *data = (struct ehl_map_entry *) pdata;
return entry->label == data->label;
}
static void
remove_exception_handler_label (label)
rtx label;
{
struct ehl_map_entry **slot, tmp;
if (cfun->eh->exception_handler_label_map == NULL)
return;
tmp.label = label;
slot = (struct ehl_map_entry **)
htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
if (! slot)
abort ();
htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
}
static void
remove_eh_handler (region)
struct eh_region *region;
{
struct eh_region **pp, **pp_start, *p, *outer, *inner;
rtx lab;
outer = region->outer;
cfun->eh->region_array[region->region_number] = outer;
if (region->aka)
{
int i;
EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i,
{ cfun->eh->region_array[i] = outer; });
}
if (outer)
{
if (!outer->aka)
outer->aka = BITMAP_GGC_ALLOC ();
if (region->aka)
bitmap_a_or_b (outer->aka, outer->aka, region->aka);
bitmap_set_bit (outer->aka, region->region_number);
}
if (cfun->eh->built_landing_pads)
lab = region->landing_pad;
else
lab = region->label;
if (lab)
remove_exception_handler_label (lab);
if (outer)
pp_start = &outer->inner;
else
pp_start = &cfun->eh->region_tree;
for (pp = pp_start, p = *pp; p != region; pp = &p->next_peer, p = *pp)
continue;
*pp = region->next_peer;
inner = region->inner;
if (inner)
{
for (p = inner; p->next_peer ; p = p->next_peer)
p->outer = outer;
p->outer = outer;
p->next_peer = *pp_start;
*pp_start = inner;
}
if (region->type == ERT_CATCH)
{
struct eh_region *try, *next, *prev;
for (try = region->next_peer;
try->type == ERT_CATCH;
try = try->next_peer)
continue;
if (try->type != ERT_TRY)
abort ();
next = region->u.catch.next_catch;
prev = region->u.catch.prev_catch;
if (next)
next->u.catch.prev_catch = prev;
else
try->u.try.last_catch = prev;
if (prev)
prev->u.catch.next_catch = next;
else
{
try->u.try.catch = next;
if (! next)
remove_eh_handler (try);
}
}
}
void
maybe_remove_eh_handler (label)
rtx label;
{
struct ehl_map_entry **slot, tmp;
struct eh_region *region;
if (cfun->eh->built_landing_pads)
return;
tmp.label = label;
slot = (struct ehl_map_entry **)
htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
if (! slot)
return;
region = (*slot)->region;
if (! region)
return;
if (region->type == ERT_MUST_NOT_THROW)
{
htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
region->label = NULL_RTX;
}
else
remove_eh_handler (region);
}
void
for_each_eh_label (callback)
void (*callback) PARAMS ((rtx));
{
htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
(void *)callback);
}
static int
for_each_eh_label_1 (pentry, data)
PTR *pentry;
PTR data;
{
struct ehl_map_entry *entry = *(struct ehl_map_entry **)pentry;
void (*callback) PARAMS ((rtx)) = (void (*) PARAMS ((rtx))) data;
(*callback) (entry->label);
return 1;
}
struct reachable_info GTY(())
{
tree types_caught;
tree types_allowed;
rtx handlers;
};
static int
check_handled (handled, type)
tree handled, type;
{
tree t;
if (! lang_eh_type_covers)
{
for (t = handled; t ; t = TREE_CHAIN (t))
if (TREE_VALUE (t) == type)
return 1;
}
else
{
for (t = handled; t ; t = TREE_CHAIN (t))
if ((*lang_eh_type_covers) (TREE_VALUE (t), type))
return 1;
}
return 0;
}
static void
add_reachable_handler (info, lp_region, region)
struct reachable_info *info;
struct eh_region *lp_region;
struct eh_region *region;
{
if (! info)
return;
if (cfun->eh->built_landing_pads)
{
if (! info->handlers)
info->handlers = alloc_INSN_LIST (lp_region->landing_pad, NULL_RTX);
}
else
info->handlers = alloc_INSN_LIST (region->label, info->handlers);
}
static enum reachable_code
reachable_next_level (region, type_thrown, info)
struct eh_region *region;
tree type_thrown;
struct reachable_info *info;
{
switch (region->type)
{
case ERT_CLEANUP:
add_reachable_handler (info, region, region);
return RNL_MAYBE_CAUGHT;
case ERT_TRY:
{
struct eh_region *c;
enum reachable_code ret = RNL_NOT_CAUGHT;
for (c = region->u.try.catch; c ; c = c->u.catch.next_catch)
{
if (c->u.catch.type_list == NULL)
{
add_reachable_handler (info, region, c);
return RNL_CAUGHT;
}
if (type_thrown)
{
tree tp_node = c->u.catch.type_list;
for (; tp_node; tp_node = TREE_CHAIN (tp_node))
{
tree type = TREE_VALUE (tp_node);
if (type == type_thrown
|| (lang_eh_type_covers
&& (*lang_eh_type_covers) (type, type_thrown)))
{
add_reachable_handler (info, region, c);
return RNL_CAUGHT;
}
}
if (lang_eh_type_covers)
return RNL_NOT_CAUGHT;
}
if (! info)
ret = RNL_MAYBE_CAUGHT;
else
{
tree tp_node = c->u.catch.type_list;
bool maybe_reachable = false;
for (; tp_node; tp_node = TREE_CHAIN (tp_node))
{
tree type = TREE_VALUE (tp_node);
if (! check_handled (info->types_caught, type))
{
info->types_caught
= tree_cons (NULL, type, info->types_caught);
maybe_reachable = true;
}
}
if (maybe_reachable)
{
add_reachable_handler (info, region, c);
ret = RNL_MAYBE_CAUGHT;
}
}
}
return ret;
}
case ERT_ALLOWED_EXCEPTIONS:
if (region->u.allowed.type_list == NULL_TREE)
{
add_reachable_handler (info, region, region);
return RNL_CAUGHT;
}
if (info)
info->types_allowed = tree_cons (NULL_TREE,
region->u.allowed.type_list,
info->types_allowed);
if (type_thrown && lang_eh_type_covers)
{
if (check_handled (region->u.allowed.type_list, type_thrown))
return RNL_NOT_CAUGHT;
else
{
add_reachable_handler (info, region, region);
return RNL_CAUGHT;
}
}
add_reachable_handler (info, region, region);
return RNL_MAYBE_CAUGHT;
case ERT_CATCH:
return RNL_NOT_CAUGHT;
case ERT_MUST_NOT_THROW:
if (info && info->handlers)
{
add_reachable_handler (info, region, region);
return RNL_CAUGHT;
}
else
return RNL_BLOCKED;
case ERT_THROW:
case ERT_FIXUP:
case ERT_UNKNOWN:
break;
}
abort ();
}
rtx
reachable_handlers (insn)
rtx insn;
{
struct reachable_info info;
struct eh_region *region;
tree type_thrown;
int region_number;
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RESX)
region_number = XINT (PATTERN (insn), 0);
else
{
rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note || INTVAL (XEXP (note, 0)) <= 0)
return NULL;
region_number = INTVAL (XEXP (note, 0));
}
memset (&info, 0, sizeof (info));
region = cfun->eh->region_array[region_number];
type_thrown = NULL_TREE;
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RESX)
{
if (region == NULL)
return NULL;
region = region->outer;
}
else if (region->type == ERT_THROW)
{
type_thrown = region->u.throw.type;
region = region->outer;
}
for (; region; region = region->outer)
if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT)
break;
return info.handlers;
}
bool
can_throw_internal (insn)
rtx insn;
{
struct eh_region *region;
tree type_thrown;
rtx note;
if (! INSN_P (insn))
return false;
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
{
int i;
for (i = 0; i < 3; ++i)
{
rtx sub = XEXP (PATTERN (insn), i);
for (; sub ; sub = NEXT_INSN (sub))
if (can_throw_internal (sub))
return true;
}
return false;
}
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note || INTVAL (XEXP (note, 0)) <= 0)
return false;
region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
type_thrown = NULL_TREE;
if (region->type == ERT_THROW)
{
type_thrown = region->u.throw.type;
region = region->outer;
}
for (; region; region = region->outer)
{
enum reachable_code how = reachable_next_level (region, type_thrown, 0);
if (how == RNL_BLOCKED)
return false;
if (how != RNL_NOT_CAUGHT)
return true;
}
return false;
}
bool
can_throw_external (insn)
rtx insn;
{
struct eh_region *region;
tree type_thrown;
rtx note;
if (! INSN_P (insn))
return false;
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
{
int i;
for (i = 0; i < 3; ++i)
{
rtx sub = XEXP (PATTERN (insn), i);
for (; sub ; sub = NEXT_INSN (sub))
if (can_throw_external (sub))
return true;
}
return false;
}
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note)
{
return (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn))));
}
if (INTVAL (XEXP (note, 0)) <= 0)
return false;
region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
type_thrown = NULL_TREE;
if (region->type == ERT_THROW)
{
type_thrown = region->u.throw.type;
region = region->outer;
}
for (; region ; region = region->outer)
if (reachable_next_level (region, type_thrown, NULL) >= RNL_CAUGHT)
return false;
return true;
}
void
set_nothrow_function_flags ()
{
rtx insn;
current_function_nothrow = 1;
cfun->all_throwers_are_sibcalls = 1;
if (! flag_exceptions)
return;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (can_throw_external (insn))
{
current_function_nothrow = 0;
if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn))
{
cfun->all_throwers_are_sibcalls = 0;
return;
}
}
for (insn = current_function_epilogue_delay_list; insn;
insn = XEXP (insn, 1))
if (can_throw_external (insn))
{
current_function_nothrow = 0;
if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn))
{
cfun->all_throwers_are_sibcalls = 0;
return;
}
}
}
void
expand_builtin_unwind_init ()
{
current_function_has_nonlocal_label = 1;
#ifdef SETUP_FRAME_ADDRESSES
SETUP_FRAME_ADDRESSES ();
#endif
}
rtx
expand_builtin_eh_return_data_regno (arglist)
tree arglist;
{
tree which = TREE_VALUE (arglist);
unsigned HOST_WIDE_INT iwhich;
if (TREE_CODE (which) != INTEGER_CST)
{
error ("argument of `__builtin_eh_return_regno' must be constant");
return constm1_rtx;
}
iwhich = tree_low_cst (which, 1);
iwhich = EH_RETURN_DATA_REGNO (iwhich);
if (iwhich == INVALID_REGNUM)
return constm1_rtx;
#ifdef DWARF_FRAME_REGNUM
iwhich = DWARF_FRAME_REGNUM (iwhich);
#else
iwhich = DBX_REGISTER_NUMBER (iwhich);
#endif
return GEN_INT (iwhich);
}
rtx
expand_builtin_extract_return_addr (addr_tree)
tree addr_tree;
{
rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0);
if (GET_MODE (addr) != Pmode
&& GET_MODE (addr) != VOIDmode)
{
#ifdef POINTERS_EXTEND_UNSIGNED
addr = convert_memory_address (Pmode, addr);
#else
addr = convert_to_mode (Pmode, addr, 0);
#endif
}
#ifdef MASK_RETURN_ADDR
expand_and (Pmode, addr, MASK_RETURN_ADDR, addr);
#endif
#if defined (RETURN_ADDR_OFFSET)
addr = plus_constant (addr, RETURN_ADDR_OFFSET);
#endif
return addr;
}
rtx
expand_builtin_frob_return_addr (addr_tree)
tree addr_tree;
{
rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (addr) != Pmode)
addr = convert_memory_address (Pmode, addr);
#endif
#ifdef RETURN_ADDR_OFFSET
addr = force_reg (Pmode, addr);
addr = plus_constant (addr, -RETURN_ADDR_OFFSET);
#endif
return addr;
}
void
expand_builtin_eh_return (stackadj_tree, handler_tree)
tree stackadj_tree, handler_tree;
{
rtx stackadj, handler;
stackadj = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
handler = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (stackadj) != Pmode)
stackadj = convert_memory_address (Pmode, stackadj);
if (GET_MODE (handler) != Pmode)
handler = convert_memory_address (Pmode, handler);
#endif
if (! cfun->eh->ehr_label)
{
cfun->eh->ehr_stackadj = copy_to_reg (stackadj);
cfun->eh->ehr_handler = copy_to_reg (handler);
cfun->eh->ehr_label = gen_label_rtx ();
}
else
{
if (stackadj != cfun->eh->ehr_stackadj)
emit_move_insn (cfun->eh->ehr_stackadj, stackadj);
if (handler != cfun->eh->ehr_handler)
emit_move_insn (cfun->eh->ehr_handler, handler);
}
emit_jump (cfun->eh->ehr_label);
}
void
expand_eh_return ()
{
rtx sa, ra, around_label;
if (! cfun->eh->ehr_label)
return;
sa = EH_RETURN_STACKADJ_RTX;
if (! sa)
{
error ("__builtin_eh_return not supported on this target");
return;
}
current_function_calls_eh_return = 1;
around_label = gen_label_rtx ();
emit_move_insn (sa, const0_rtx);
emit_jump (around_label);
emit_label (cfun->eh->ehr_label);
clobber_return_register ();
#ifdef HAVE_eh_return
if (HAVE_eh_return)
emit_insn (gen_eh_return (cfun->eh->ehr_stackadj, cfun->eh->ehr_handler));
else
#endif
{
ra = EH_RETURN_HANDLER_RTX;
if (! ra)
{
error ("__builtin_eh_return not supported on this target");
ra = gen_reg_rtx (Pmode);
}
emit_move_insn (sa, cfun->eh->ehr_stackadj);
emit_move_insn (ra, cfun->eh->ehr_handler);
}
emit_label (around_label);
}
struct action_record
{
int offset;
int filter;
int next;
};
static int
action_record_eq (pentry, pdata)
const PTR pentry;
const PTR pdata;
{
const struct action_record *entry = (const struct action_record *) pentry;
const struct action_record *data = (const struct action_record *) pdata;
return entry->filter == data->filter && entry->next == data->next;
}
static hashval_t
action_record_hash (pentry)
const PTR pentry;
{
const struct action_record *entry = (const struct action_record *) pentry;
return entry->next * 1009 + entry->filter;
}
static int
add_action_record (ar_hash, filter, next)
htab_t ar_hash;
int filter, next;
{
struct action_record **slot, *new, tmp;
tmp.filter = filter;
tmp.next = next;
slot = (struct action_record **) htab_find_slot (ar_hash, &tmp, INSERT);
if ((new = *slot) == NULL)
{
new = (struct action_record *) xmalloc (sizeof (*new));
new->offset = VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + 1;
new->filter = filter;
new->next = next;
*slot = new;
push_sleb128 (&cfun->eh->action_record_data, filter);
if (next)
next -= VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + 1;
push_sleb128 (&cfun->eh->action_record_data, next);
}
return new->offset;
}
static int
collect_one_action_chain (ar_hash, region)
htab_t ar_hash;
struct eh_region *region;
{
struct eh_region *c;
int next;
if (region == NULL)
return -1;
switch (region->type)
{
case ERT_CLEANUP:
next = collect_one_action_chain (ar_hash, region->outer);
if (next <= 0)
return 0;
for (c = region->outer; c ; c = c->outer)
if (c->type == ERT_CLEANUP)
return next;
return add_action_record (ar_hash, 0, next);
case ERT_TRY:
next = -3;
for (c = region->u.try.last_catch; c ; c = c->u.catch.prev_catch)
{
if (c->u.catch.type_list == NULL)
{
int filter
= TREE_INT_CST_LOW (TREE_VALUE (c->u.catch.filter_list));
next = add_action_record (ar_hash, filter, 0);
}
else
{
tree flt_node;
if (next == -3)
{
next = collect_one_action_chain (ar_hash, region->outer);
if (next == -1)
next = 0;
else if (next <= 0)
next = add_action_record (ar_hash, 0, 0);
}
flt_node = c->u.catch.filter_list;
for (; flt_node; flt_node = TREE_CHAIN (flt_node))
{
int filter = TREE_INT_CST_LOW (TREE_VALUE (flt_node));
next = add_action_record (ar_hash, filter, next);
}
}
}
return next;
case ERT_ALLOWED_EXCEPTIONS:
next = collect_one_action_chain (ar_hash, region->outer);
return add_action_record (ar_hash, region->u.allowed.filter,
next < 0 ? 0 : next);
case ERT_MUST_NOT_THROW:
return -2;
case ERT_CATCH:
case ERT_THROW:
return collect_one_action_chain (ar_hash, region->outer);
default:
abort ();
}
}
static int
add_call_site (landing_pad, action)
rtx landing_pad;
int action;
{
struct call_site_record *data = cfun->eh->call_site_data;
int used = cfun->eh->call_site_data_used;
int size = cfun->eh->call_site_data_size;
if (used >= size)
{
size = (size ? size * 2 : 64);
data = (struct call_site_record *)
ggc_realloc (data, sizeof (*data) * size);
cfun->eh->call_site_data = data;
cfun->eh->call_site_data_size = size;
}
data[used].landing_pad = landing_pad;
data[used].action = action;
cfun->eh->call_site_data_used = used + 1;
return used + call_site_base;
}
void
convert_to_eh_region_ranges ()
{
rtx insn, iter, note;
htab_t ar_hash;
int last_action = -3;
rtx last_action_insn = NULL_RTX;
rtx last_landing_pad = NULL_RTX;
rtx first_no_action_insn = NULL_RTX;
int call_site = 0;
if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL)
return;
VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data");
ar_hash = htab_create (31, action_record_hash, action_record_eq, free);
for (iter = get_insns (); iter ; iter = NEXT_INSN (iter))
if (INSN_P (iter))
{
struct eh_region *region;
int this_action;
rtx this_landing_pad;
insn = iter;
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note)
{
if (! (GET_CODE (insn) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn)))))
continue;
this_action = -1;
region = NULL;
}
else
{
if (INTVAL (XEXP (note, 0)) <= 0)
continue;
region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
this_action = collect_one_action_chain (ar_hash, region);
}
if (this_action != -1)
cfun->uses_eh_lsda = 1;
else if (last_action == -3)
{
first_no_action_insn = iter;
last_action = -1;
}
if (this_action >= 0)
{
struct eh_region *o;
for (o = region; ! o->landing_pad ; o = o->outer)
continue;
this_landing_pad = o->landing_pad;
}
else
this_landing_pad = NULL_RTX;
if (last_action != this_action
|| last_landing_pad != this_landing_pad)
{
if (last_action >= -1)
{
if (first_no_action_insn)
{
call_site = add_call_site (NULL_RTX, 0);
note = emit_note_before (NOTE_INSN_EH_REGION_BEG,
first_no_action_insn);
NOTE_EH_HANDLER (note) = call_site;
first_no_action_insn = NULL_RTX;
}
note = emit_note_after (NOTE_INSN_EH_REGION_END,
last_action_insn);
NOTE_EH_HANDLER (note) = call_site;
}
if (this_action >= -1)
{
call_site = add_call_site (this_landing_pad,
this_action < 0 ? 0 : this_action);
note = emit_note_before (NOTE_INSN_EH_REGION_BEG, iter);
NOTE_EH_HANDLER (note) = call_site;
}
last_action = this_action;
last_landing_pad = this_landing_pad;
}
last_action_insn = iter;
}
if (last_action >= -1 && ! first_no_action_insn)
{
note = emit_note_after (NOTE_INSN_EH_REGION_END, last_action_insn);
NOTE_EH_HANDLER (note) = call_site;
}
htab_delete (ar_hash);
}
static void
push_uleb128 (data_area, value)
varray_type *data_area;
unsigned int value;
{
do
{
unsigned char byte = value & 0x7f;
value >>= 7;
if (value)
byte |= 0x80;
VARRAY_PUSH_UCHAR (*data_area, byte);
}
while (value);
}
static void
push_sleb128 (data_area, value)
varray_type *data_area;
int value;
{
unsigned char byte;
int more;
do
{
byte = value & 0x7f;
value >>= 7;
more = ! ((value == 0 && (byte & 0x40) == 0)
|| (value == -1 && (byte & 0x40) != 0));
if (more)
byte |= 0x80;
VARRAY_PUSH_UCHAR (*data_area, byte);
}
while (more);
}
#ifndef HAVE_AS_LEB128
static int
dw2_size_of_call_site_table ()
{
int n = cfun->eh->call_site_data_used;
int size = n * (4 + 4 + 4);
int i;
for (i = 0; i < n; ++i)
{
struct call_site_record *cs = &cfun->eh->call_site_data[i];
size += size_of_uleb128 (cs->action);
}
return size;
}
static int
sjlj_size_of_call_site_table ()
{
int n = cfun->eh->call_site_data_used;
int size = 0;
int i;
for (i = 0; i < n; ++i)
{
struct call_site_record *cs = &cfun->eh->call_site_data[i];
size += size_of_uleb128 (INTVAL (cs->landing_pad));
size += size_of_uleb128 (cs->action);
}
return size;
}
#endif
static void
dw2_output_call_site_table ()
{
const char *const function_start_lab
= IDENTIFIER_POINTER (current_function_func_begin_label);
int n = cfun->eh->call_site_data_used;
int i;
for (i = 0; i < n; ++i)
{
struct call_site_record *cs = &cfun->eh->call_site_data[i];
char reg_start_lab[32];
char reg_end_lab[32];
char landing_pad_lab[32];
ASM_GENERATE_INTERNAL_LABEL (reg_start_lab, "LEHB", call_site_base + i);
ASM_GENERATE_INTERNAL_LABEL (reg_end_lab, "LEHE", call_site_base + i);
if (cs->landing_pad)
ASM_GENERATE_INTERNAL_LABEL (landing_pad_lab, "L",
CODE_LABEL_NUMBER (cs->landing_pad));
#ifdef HAVE_AS_LEB128
dw2_asm_output_delta_uleb128 (reg_start_lab, function_start_lab,
"region %d start", i);
dw2_asm_output_delta_uleb128 (reg_end_lab, reg_start_lab,
"length");
if (cs->landing_pad)
dw2_asm_output_delta_uleb128 (landing_pad_lab, function_start_lab,
"landing pad");
else
dw2_asm_output_data_uleb128 (0, "landing pad");
#else
dw2_asm_output_delta (4, reg_start_lab, function_start_lab,
"region %d start", i);
dw2_asm_output_delta (4, reg_end_lab, reg_start_lab, "length");
if (cs->landing_pad)
dw2_asm_output_delta (4, landing_pad_lab, function_start_lab,
"landing pad");
else
dw2_asm_output_data (4, 0, "landing pad");
#endif
dw2_asm_output_data_uleb128 (cs->action, "action");
}
call_site_base += n;
}
static void
sjlj_output_call_site_table ()
{
int n = cfun->eh->call_site_data_used;
int i;
for (i = 0; i < n; ++i)
{
struct call_site_record *cs = &cfun->eh->call_site_data[i];
dw2_asm_output_data_uleb128 (INTVAL (cs->landing_pad),
"region %d landing pad", i);
dw2_asm_output_data_uleb128 (cs->action, "action");
}
call_site_base += n;
}
void
default_exception_section ()
{
if (targetm.have_named_sections)
{
int flags;
#ifdef HAVE_LD_RO_RW_SECTION_MIXING
int tt_format = ASM_PREFERRED_EH_DATA_FORMAT (0, 1);
flags = (! flag_pic
|| ((tt_format & 0x70) != DW_EH_PE_absptr
&& (tt_format & 0x70) != DW_EH_PE_aligned))
? 0 : SECTION_WRITE;
#else
flags = SECTION_WRITE;
#endif
named_section_flags (".gcc_except_table", flags);
}
else if (flag_pic)
data_section ();
else
readonly_data_section ();
}
void
output_function_exception_table ()
{
int tt_format, cs_format, lp_format, i, n;
#ifdef HAVE_AS_LEB128
char ttype_label[32];
char cs_after_size_label[32];
char cs_end_label[32];
#else
int call_site_len;
#endif
int have_tt_data;
int tt_format_size = 0;
if (! cfun->uses_eh_lsda)
return;
#ifdef IA64_UNWIND_INFO
fputs ("\t.personality\t", asm_out_file);
output_addr_const (asm_out_file, eh_personality_libfunc);
fputs ("\n\t.handlerdata\n", asm_out_file);
#else
(*targetm.asm_out.exception_section) ();
#endif
have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0
|| VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0);
if (! have_tt_data)
tt_format = DW_EH_PE_omit;
else
{
tt_format = ASM_PREFERRED_EH_DATA_FORMAT (0, 1);
#ifdef HAVE_AS_LEB128
ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT",
current_function_funcdef_no);
#endif
tt_format_size = size_of_encoded_value (tt_format);
assemble_align (tt_format_size * BITS_PER_UNIT);
}
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LLSDA",
current_function_funcdef_no);
lp_format = DW_EH_PE_omit;
dw2_asm_output_data (1, lp_format, "@LPStart format (%s)",
eh_data_format_name (lp_format));
dw2_asm_output_data (1, tt_format, "@TType format (%s)",
eh_data_format_name (tt_format));
#ifndef HAVE_AS_LEB128
if (USING_SJLJ_EXCEPTIONS)
call_site_len = sjlj_size_of_call_site_table ();
else
call_site_len = dw2_size_of_call_site_table ();
#endif
if (have_tt_data)
{
#ifdef HAVE_AS_LEB128
char ttype_after_disp_label[32];
ASM_GENERATE_INTERNAL_LABEL (ttype_after_disp_label, "LLSDATTD",
current_function_funcdef_no);
dw2_asm_output_delta_uleb128 (ttype_label, ttype_after_disp_label,
"@TType base offset");
ASM_OUTPUT_LABEL (asm_out_file, ttype_after_disp_label);
#else
unsigned int before_disp, after_disp, last_disp, disp;
before_disp = 1 + 1;
after_disp = (1 + size_of_uleb128 (call_site_len)
+ call_site_len
+ VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data)
+ (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data)
* tt_format_size));
disp = after_disp;
do
{
unsigned int disp_size, pad;
last_disp = disp;
disp_size = size_of_uleb128 (disp);
pad = before_disp + disp_size + after_disp;
if (pad % tt_format_size)
pad = tt_format_size - (pad % tt_format_size);
else
pad = 0;
disp = after_disp + pad;
}
while (disp != last_disp);
dw2_asm_output_data_uleb128 (disp, "@TType base offset");
#endif
}
#ifdef HAVE_AS_LEB128
cs_format = DW_EH_PE_uleb128;
#else
cs_format = DW_EH_PE_udata4;
#endif
dw2_asm_output_data (1, cs_format, "call-site format (%s)",
eh_data_format_name (cs_format));
#ifdef HAVE_AS_LEB128
ASM_GENERATE_INTERNAL_LABEL (cs_after_size_label, "LLSDACSB",
current_function_funcdef_no);
ASM_GENERATE_INTERNAL_LABEL (cs_end_label, "LLSDACSE",
current_function_funcdef_no);
dw2_asm_output_delta_uleb128 (cs_end_label, cs_after_size_label,
"Call-site table length");
ASM_OUTPUT_LABEL (asm_out_file, cs_after_size_label);
if (USING_SJLJ_EXCEPTIONS)
sjlj_output_call_site_table ();
else
dw2_output_call_site_table ();
ASM_OUTPUT_LABEL (asm_out_file, cs_end_label);
#else
dw2_asm_output_data_uleb128 (call_site_len,"Call-site table length");
if (USING_SJLJ_EXCEPTIONS)
sjlj_output_call_site_table ();
else
dw2_output_call_site_table ();
#endif
n = VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data);
for (i = 0; i < n; ++i)
dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->action_record_data, i),
(i ? NULL : "Action record table"));
if (have_tt_data)
assemble_align (tt_format_size * BITS_PER_UNIT);
i = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data);
while (i-- > 0)
{
tree type = VARRAY_TREE (cfun->eh->ttype_data, i);
rtx value;
if (type == NULL_TREE)
type = integer_zero_node;
else
type = lookup_type_for_runtime (type);
value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
if (tt_format == DW_EH_PE_absptr || tt_format == DW_EH_PE_aligned)
assemble_integer (value, tt_format_size,
tt_format_size * BITS_PER_UNIT, 1);
else
dw2_asm_output_encoded_addr_rtx (tt_format, value, NULL);
}
#ifdef HAVE_AS_LEB128
if (have_tt_data)
ASM_OUTPUT_LABEL (asm_out_file, ttype_label);
#endif
n = VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data);
for (i = 0; i < n; ++i)
dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->ehspec_data, i),
(i ? NULL : "Exception specification table"));
function_section (current_function_decl);
}
#include "gt-except.h"