#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.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"
#include "cgraph.h"
#ifndef EH_RETURN_DATA_REGNO
#define EH_RETURN_DATA_REGNO(N) INVALID_REGNUM
#endif
tree (*lang_protect_cleanup_actions) (void);
int (*lang_eh_type_covers) (tree a, tree b);
tree (*lang_eh_runtime_type) (tree);
struct ehl_map_entry GTY(())
{
rtx label;
struct eh_region *region;
};
static GTY(()) 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;
struct eh_region *prev_try;
} 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;
tree tree_label;
rtx landing_pad;
rtx post_landing_pad;
rtx resume;
unsigned may_contain_throw : 1;
};
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 (const void *, const void *);
static hashval_t t2r_hash (const void *);
static void add_type_for_runtime (tree);
static tree lookup_type_for_runtime (tree);
static void remove_unreachable_regions (rtx);
static int ttypes_filter_eq (const void *, const void *);
static hashval_t ttypes_filter_hash (const void *);
static int ehspec_filter_eq (const void *, const void *);
static hashval_t ehspec_filter_hash (const void *);
static int add_ttypes_entry (htab_t, tree);
static int add_ehspec_entry (htab_t, htab_t, tree);
static void assign_filter_values (void);
static void build_post_landing_pads (void);
static void connect_post_landing_pads (void);
static void dw2_build_landing_pads (void);
struct sjlj_lp_info;
static bool sjlj_find_directly_reachable_regions (struct sjlj_lp_info *);
static void sjlj_assign_call_site_values (rtx, struct sjlj_lp_info *);
static void sjlj_mark_call_sites (struct sjlj_lp_info *);
static rtx sjlj_generate_setjmp_sequence (rtx, bool);
static void sjlj_emit_function_setjmps (rtx);
static void sjlj_emit_function_exit (void);
static void sjlj_emit_dispatch_table (rtx, struct sjlj_lp_info *);
static void sjlj_build_landing_pads (void);
static hashval_t ehl_hash (const void *);
static int ehl_eq (const void *, const void *);
static void add_ehl_entry (rtx, struct eh_region *);
static void remove_exception_handler_label (rtx);
static void remove_eh_handler (struct eh_region *);
static int for_each_eh_label_1 (void **, void *);
enum reachable_code
{
RNL_NOT_CAUGHT,
RNL_MAYBE_CAUGHT,
RNL_CAUGHT,
RNL_BLOCKED
};
struct reachable_info;
static enum reachable_code reachable_next_level (struct eh_region *, tree,
struct reachable_info *);
static int action_record_eq (const void *, const void *);
static hashval_t action_record_hash (const void *);
static int add_action_record (htab_t, int, int);
static int collect_one_action_chain (htab_t, struct eh_region *);
static int add_call_site (rtx, int);
static void push_uleb128 (varray_type *, unsigned int);
static void push_sleb128 (varray_type *, int);
#ifndef HAVE_AS_LEB128
static int dw2_size_of_call_site_table (void);
static int sjlj_size_of_call_site_table (void);
#endif
static void dw2_output_call_site_table (void);
static void sjlj_output_call_site_table (void);
int
doing_eh (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 (void)
{
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_cst (NULL_TREE, 4 - 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;
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_cst (NULL_TREE, JMP_BUF_SIZE - 1);
#else
tmp = build_int_cst (NULL_TREE, FIRST_PSEUDO_REGISTER + 2 - 1);
#endif
#else
tmp = build_int_cst (NULL_TREE, 5 * BITS_PER_WORD / POINTER_SIZE - 1);
#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 (void)
{
cfun->eh = ggc_alloc_cleared (sizeof (struct eh_status));
}
static struct eh_region *
gen_eh_region (enum eh_region_type type, struct eh_region *outer)
{
struct eh_region *new;
#ifdef ENABLE_CHECKING
gcc_assert (doing_eh (0));
#endif
new = ggc_alloc_cleared (sizeof (*new));
new->type = type;
new->outer = outer;
if (outer)
{
new->next_peer = outer->inner;
outer->inner = new;
}
else
{
new->next_peer = cfun->eh->region_tree;
cfun->eh->region_tree = new;
}
new->region_number = ++cfun->eh->last_region_number;
return new;
}
struct eh_region *
gen_eh_region_cleanup (struct eh_region *outer, struct eh_region *prev_try)
{
struct eh_region *cleanup = gen_eh_region (ERT_CLEANUP, outer);
cleanup->u.cleanup.prev_try = prev_try;
return cleanup;
}
struct eh_region *
gen_eh_region_try (struct eh_region *outer)
{
return gen_eh_region (ERT_TRY, outer);
}
struct eh_region *
gen_eh_region_catch (struct eh_region *t, tree type_or_list)
{
struct eh_region *c, *l;
tree type_list, type_node;
type_list = type_or_list;
if (type_or_list)
{
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));
}
c = gen_eh_region (ERT_CATCH, t->outer);
c->u.catch.type_list = type_list;
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;
return c;
}
struct eh_region *
gen_eh_region_allowed (struct eh_region *outer, tree allowed)
{
struct eh_region *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer);
region->u.allowed.type_list = allowed;
for (; allowed ; allowed = TREE_CHAIN (allowed))
add_type_for_runtime (TREE_VALUE (allowed));
return region;
}
struct eh_region *
gen_eh_region_must_not_throw (struct eh_region *outer)
{
return gen_eh_region (ERT_MUST_NOT_THROW, outer);
}
int
get_eh_region_number (struct eh_region *region)
{
return region->region_number;
}
bool
get_eh_region_may_contain_throw (struct eh_region *region)
{
return region->may_contain_throw;
}
tree
get_eh_region_tree_label (struct eh_region *region)
{
return region->tree_label;
}
void
set_eh_region_tree_label (struct eh_region *region, tree lab)
{
region->tree_label = lab;
}
void
expand_resx_expr (tree exp)
{
int region_nr = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0));
struct eh_region *reg = cfun->eh->region_array[region_nr];
reg->resume = emit_jump_insn (gen_rtx_RESX (VOIDmode, region_nr));
emit_barrier ();
}
void
note_eh_region_may_contain_throw (struct eh_region *region)
{
while (region && !region->may_contain_throw)
{
region->may_contain_throw = 1;
region = region->outer;
}
}
void
note_current_region_may_contain_throw (void)
{
note_eh_region_may_contain_throw (cfun->eh->cur_region);
}
rtx
get_exception_pointer (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;
}
rtx
get_exception_filter (struct function *fun)
{
rtx filter = fun->eh->filter;
if (fun == cfun && ! filter)
{
filter = gen_reg_rtx (targetm.eh_return_filter_mode ());
fun->eh->filter = filter;
}
return filter;
}
void
collect_eh_region_array (void)
{
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
remove_unreachable_regions (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)
{
gcc_assert (!uid_region_num[INSN_UID (r->resume)]);
uid_region_num[INSN_UID (r->resume)] = i;
}
if (r->label)
{
gcc_assert (!uid_region_num[INSN_UID (r->label)]);
uid_region_num[INSN_UID (r->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])
{
bool kill_it = true;
switch (r->type)
{
case ERT_THROW:
if (r->outer && reachable[r->outer->region_number])
kill_it = false;
break;
case ERT_MUST_NOT_THROW:
kill_it = false;
break;
case ERT_TRY:
{
struct eh_region *c;
for (c = r->u.try.catch; c ; c = c->u.catch.next_catch)
if (reachable[c->region_number])
{
kill_it = false;
break;
}
break;
}
default:
break;
}
if (kill_it)
remove_eh_handler (r);
}
}
free (reachable);
free (uid_region_num);
}
void
convert_from_eh_region_ranges (void)
{
rtx insns = get_insns ();
int i, n = cfun->eh->last_region_number;
for (i = 1; i <= n; ++i)
{
struct eh_region *region = cfun->eh->region_array[i];
if (region && region->tree_label)
region->label = DECL_RTL_IF_SET (region->tree_label);
}
remove_unreachable_regions (insns);
}
static void
add_ehl_entry (rtx label, struct eh_region *region)
{
struct ehl_map_entry **slot, *entry;
LABEL_PRESERVE_P (label) = 1;
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);
gcc_assert (!*slot || cfun->eh->built_landing_pads);
*slot = entry;
}
void
find_exception_handler_labels (void)
{
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 (void)
{
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 int
t2r_eq (const void *pentry, const void *pdata)
{
tree entry = (tree) pentry;
tree data = (tree) pdata;
return TREE_PURPOSE (entry) == data;
}
static hashval_t
t2r_hash (const void *pentry)
{
tree entry = (tree) pentry;
return TREE_HASH (TREE_PURPOSE (entry));
}
static void
add_type_for_runtime (tree type)
{
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
TREE_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 (tree type)
{
tree *slot;
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
TREE_HASH (type), NO_INSERT);
return TREE_VALUE (*slot);
}
struct ttypes_filter GTY(())
{
tree t;
int filter;
};
static int
ttypes_filter_eq (const void *pentry, const void *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 (const void *pentry)
{
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
return TREE_HASH (entry->t);
}
static int
ehspec_filter_eq (const void *pentry, const void *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 (const void *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) + TREE_HASH (TREE_VALUE (list));
return h;
}
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, TREE_HASH (type), INSERT);
if ((n = *slot) == NULL)
{
n = 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 (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 = 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 (void)
{
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_cst (NULL_TREE, flt);
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_cst (NULL_TREE, flt);
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 basic_block
emit_to_new_bb_before (rtx seq, rtx insn)
{
rtx last;
basic_block bb;
edge e;
edge_iterator ei;
for (ei = ei_start (BLOCK_FOR_INSN (insn)->preds); (e = ei_safe_edge (ei)); )
if (e->flags & EDGE_FALLTHRU)
force_nonfallthru (e);
else
ei_next (&ei);
last = emit_insn_before (seq, insn);
if (BARRIER_P (last))
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;
}
static void
build_post_landing_pads (void)
{
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,
targetm.eh_return_filter_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_to_new_bb_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,
targetm.eh_return_filter_mode (), 0, region->label);
region->resume
= emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
emit_barrier ();
seq = get_insns ();
end_sequence ();
emit_to_new_bb_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:
gcc_unreachable ();
}
}
}
static void
connect_post_landing_pads (void)
{
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;
rtx barrier;
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)
{
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 (EDGE_COUNT (src->succs) > 0)
remove_edge (EDGE_SUCC (src, 0));
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);
delete_insns_since (NEXT_INSN (last_call_insn ()));
}
seq = get_insns ();
end_sequence ();
barrier = emit_insn_before (seq, region->resume);
gcc_assert (BARRIER_P (barrier));
delete_insn (barrier);
delete_insn (region->resume);
if (region->label == NULL && region->type == ERT_CLEANUP)
remove_eh_handler (region);
}
}
static void
dw2_build_landing_pads (void)
{
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;
basic_block bb;
bool clobbers_hard_regs = false;
edge e;
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 (targetm.eh_return_filter_mode (),
EH_RETURN_DATA_REGNO (1)));
seq = get_insns ();
end_sequence ();
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;
}
}
struct sjlj_lp_info
{
int directly_reachable;
int action_index;
int dispatch_index;
int call_site_index;
};
static bool
sjlj_find_directly_reachable_regions (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, NULL);
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 (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 (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 (LABEL_P (insn))
last_call_site = -2;
if (! INSN_P (insn))
continue;
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note)
{
if (CALL_P (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 (CALL_P (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 rtx
sjlj_generate_setjmp_sequence (rtx dispatch_label, bool unregister_first)
{
rtx fc, mem, seq;
fc = cfun->eh->sjlj_fc;
start_sequence ();
if (unregister_first == true)
emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode,
1, XEXP (fc, 0), Pmode);
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];
rtx sym;
ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no);
sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_LOCAL;
emit_move_insn (mem, sym);
}
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 (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 ();
return seq;
}
static void
sjlj_emit_function_setjmps (rtx dispatch_label)
{
rtx seq, fn_begin;
seq = sjlj_generate_setjmp_sequence (dispatch_label, false);
for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin))
if (NOTE_P (fn_begin)
&& (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG
|| NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK))
break;
if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
insert_insn_on_edge (seq, EDGE_SUCC (ENTRY_BLOCK_PTR, 0));
else
{
rtx last = BB_END (EDGE_SUCC (ENTRY_BLOCK_PTR, 0)->dest);
for (; ; fn_begin = NEXT_INSN (fn_begin))
if ((NOTE_P (fn_begin)
&& NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
|| fn_begin == last)
break;
emit_insn_after (seq, fn_begin);
}
#ifdef TARGET_ARM
if (current_function_calls_alloca)
{
for (fn_begin = get_insns ();
fn_begin;
fn_begin = NEXT_INSN (fn_begin))
if (NOTE_P (fn_begin)
&& NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_ALLOCA)
{
seq = sjlj_generate_setjmp_sequence (dispatch_label, true);
emit_insn_after (seq, fn_begin);
}
}
#endif
}
void
sjlj_emit_function_exit_after (rtx after)
{
cfun->eh->sjlj_exit_after = after;
}
static void
sjlj_emit_function_exit (void)
{
rtx seq;
edge e;
edge_iterator ei;
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 ();
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
if (e->flags & EDGE_FALLTHRU)
break;
if (e)
{
rtx insn;
gcc_assert (e->src->next_bb == EXIT_BLOCK_PTR);
for (insn = BB_HEAD (e->src); ; insn = NEXT_INSN (insn))
{
if (insn == cfun->eh->sjlj_exit_after)
{
if (LABEL_P (insn))
insn = NEXT_INSN (insn);
emit_insn_after (seq, insn);
return;
}
if (insn == BB_END (e->src))
break;
}
insert_insn_on_edge (seq, e);
}
}
static void
sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info)
{
int i, first_reachable;
rtx mem, dispatch, seq, fc;
rtx before;
basic_block bb;
edge e;
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 != ptr_mode)
{
#ifdef POINTERS_EXTEND_UNSIGNED
mem = convert_memory_address (ptr_mode, mem);
#else
mem = convert_to_mode (ptr_mode, 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 ();
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
sjlj_build_landing_pads (void)
{
struct sjlj_lp_info *lp_info;
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_setjmps (dispatch_label);
sjlj_emit_dispatch_table (dispatch_label, lp_info);
sjlj_emit_function_exit ();
}
free (lp_info);
}
void
finish_eh_generation (void)
{
basic_block bb;
if (cfun->eh->region_tree == NULL)
return;
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 ();
break_superblocks ();
if (USING_SJLJ_EXCEPTIONS)
commit_edge_insertions ();
FOR_EACH_BB (bb)
{
edge e;
edge_iterator ei;
bool eh = false;
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
if (e->flags & EDGE_EH)
{
remove_edge (e);
eh = true;
}
else
ei_next (&ei);
}
if (eh)
rtl_make_eh_edge (NULL, bb, BB_END (bb));
}
}
static hashval_t
ehl_hash (const void *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 (const void *pentry, const void *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 (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);
gcc_assert (slot);
htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
}
static void
remove_eh_handler (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)
{
unsigned i;
bitmap_iterator bi;
EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i, bi)
{
cfun->eh->region_array[i] = outer;
}
}
if (outer)
{
if (!outer->aka)
outer->aka = BITMAP_GGC_ALLOC ();
if (region->aka)
bitmap_ior_into (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;
gcc_assert (try->type == ERT_TRY);
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 (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 (void (*callback) (rtx))
{
htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
(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;
(*callback) (entry->label);
return 1;
}
void
for_each_eh_region (void (*callback) (struct eh_region *))
{
int i, n = cfun->eh->last_region_number;
for (i = 1; i <= n; ++i)
{
struct eh_region *region = cfun->eh->region_array[i];
if (region)
(*callback) (region);
}
}
struct reachable_info
{
tree types_caught;
tree types_allowed;
void (*callback) (struct eh_region *, void *);
void *callback_data;
bool saw_any_handlers;
};
int
check_handled (tree handled, tree 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 (struct reachable_info *info,
struct eh_region *lp_region, struct eh_region *region)
{
if (! info)
return;
info->saw_any_handlers = true;
if (cfun->eh->built_landing_pads)
info->callback (lp_region, info->callback_data);
else
info->callback (region, info->callback_data);
}
static enum reachable_code
reachable_next_level (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->saw_any_handlers)
{
add_reachable_handler (info, region, region);
return RNL_CAUGHT;
}
else
return RNL_BLOCKED;
case ERT_THROW:
case ERT_FIXUP:
case ERT_UNKNOWN:
gcc_unreachable ();
break;
default:
gcc_unreachable ();
}
}
void
foreach_reachable_handler (int region_number, bool is_resx,
void (*callback) (struct eh_region *, void *),
void *callback_data)
{
struct reachable_info info;
struct eh_region *region;
tree type_thrown;
memset (&info, 0, sizeof (info));
info.callback = callback;
info.callback_data = callback_data;
region = cfun->eh->region_array[region_number];
type_thrown = NULL_TREE;
if (is_resx)
{
if (region == NULL)
return;
region = region->outer;
}
else if (region->type == ERT_THROW)
{
type_thrown = region->u.throw.type;
region = region->outer;
}
while (region)
{
if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT)
break;
if (region->type == ERT_CLEANUP)
region = region->u.cleanup.prev_try;
else
region = region->outer;
}
}
static void
arh_to_landing_pad (struct eh_region *region, void *data)
{
rtx *p_handlers = data;
if (! *p_handlers)
*p_handlers = alloc_INSN_LIST (region->landing_pad, NULL_RTX);
}
static void
arh_to_label (struct eh_region *region, void *data)
{
rtx *p_handlers = data;
*p_handlers = alloc_INSN_LIST (region->label, *p_handlers);
}
rtx
reachable_handlers (rtx insn)
{
bool is_resx = false;
rtx handlers = NULL;
int region_number;
if (JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) == RESX)
{
region_number = XINT (PATTERN (insn), 0);
is_resx = true;
}
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));
}
foreach_reachable_handler (region_number, is_resx,
(cfun->eh->built_landing_pads
? arh_to_landing_pad
: arh_to_label),
&handlers);
return handlers;
}
bool
can_throw_internal_1 (int region_number)
{
struct eh_region *region;
tree type_thrown;
region = cfun->eh->region_array[region_number];
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_internal (rtx insn)
{
rtx note;
if (! INSN_P (insn))
return false;
if (JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) == RESX
&& XINT (PATTERN (insn), 0) > 0)
return can_throw_internal_1 (XINT (PATTERN (insn), 0));
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note || INTVAL (XEXP (note, 0)) <= 0)
return false;
return can_throw_internal_1 (INTVAL (XEXP (note, 0)));
}
bool
can_throw_external_1 (int region_number)
{
struct eh_region *region;
tree type_thrown;
region = cfun->eh->region_array[region_number];
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;
}
bool
can_throw_external (rtx insn)
{
rtx note;
if (! INSN_P (insn))
return false;
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
insn = XVECEXP (PATTERN (insn), 0, 0);
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (!note)
{
return (CALL_P (insn)
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (insn))));
}
if (INTVAL (XEXP (note, 0)) <= 0)
return false;
return can_throw_external_1 (INTVAL (XEXP (note, 0)));
}
void
set_nothrow_function_flags (void)
{
rtx insn;
TREE_NOTHROW (current_function_decl) = 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))
{
TREE_NOTHROW (current_function_decl) = 0;
if (!CALL_P (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))
{
TREE_NOTHROW (current_function_decl) = 0;
if (!CALL_P (insn) || !SIBLING_CALL_P (insn))
{
cfun->all_throwers_are_sibcalls = 0;
return;
}
}
}
void
expand_builtin_unwind_init (void)
{
current_function_has_nonlocal_label = 1;
#ifdef SETUP_FRAME_ADDRESSES
SETUP_FRAME_ADDRESSES ();
#endif
}
rtx
expand_builtin_eh_return_data_regno (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 (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 (tree addr_tree)
{
rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
addr = convert_memory_address (Pmode, addr);
#ifdef RETURN_ADDR_OFFSET
addr = force_reg (Pmode, addr);
addr = plus_constant (addr, -RETURN_ADDR_OFFSET);
#endif
return addr;
}
void
expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED,
tree handler_tree)
{
rtx tmp;
#ifdef EH_RETURN_STACKADJ_RTX
tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
tmp = convert_memory_address (Pmode, tmp);
if (!cfun->eh->ehr_stackadj)
cfun->eh->ehr_stackadj = copy_to_reg (tmp);
else if (tmp != cfun->eh->ehr_stackadj)
emit_move_insn (cfun->eh->ehr_stackadj, tmp);
#endif
tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
tmp = convert_memory_address (Pmode, tmp);
if (!cfun->eh->ehr_handler)
cfun->eh->ehr_handler = copy_to_reg (tmp);
else if (tmp != cfun->eh->ehr_handler)
emit_move_insn (cfun->eh->ehr_handler, tmp);
if (!cfun->eh->ehr_label)
cfun->eh->ehr_label = gen_label_rtx ();
emit_jump (cfun->eh->ehr_label);
}
void
expand_eh_return (void)
{
rtx around_label;
if (! cfun->eh->ehr_label)
return;
current_function_calls_eh_return = 1;
#ifdef EH_RETURN_STACKADJ_RTX
emit_move_insn (EH_RETURN_STACKADJ_RTX, const0_rtx);
#endif
around_label = gen_label_rtx ();
emit_jump (around_label);
emit_label (cfun->eh->ehr_label);
clobber_return_register ();
#ifdef EH_RETURN_STACKADJ_RTX
emit_move_insn (EH_RETURN_STACKADJ_RTX, cfun->eh->ehr_stackadj);
#endif
#ifdef HAVE_eh_return
if (HAVE_eh_return)
emit_insn (gen_eh_return (cfun->eh->ehr_handler));
else
#endif
{
#ifdef EH_RETURN_HANDLER_RTX
emit_move_insn (EH_RETURN_HANDLER_RTX, cfun->eh->ehr_handler);
#else
error ("__builtin_eh_return not supported on this target");
#endif
}
emit_label (around_label);
}
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
extend = 1;
#endif
return convert_modes (word_mode, ptr_mode, addr, extend);
}
struct action_record
{
int offset;
int filter;
int next;
};
static int
action_record_eq (const void *pentry, const void *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 (const void *pentry)
{
const struct action_record *entry = (const struct action_record *) pentry;
return entry->next * 1009 + entry->filter;
}
static int
add_action_record (htab_t ar_hash, int filter, int 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 = 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 (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);
if (next == -1)
next = 0;
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:
return -2;
case ERT_CATCH:
case ERT_THROW:
return collect_one_action_chain (ar_hash, region->outer);
default:
gcc_unreachable ();
}
}
static int
add_call_site (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 = 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 (void)
{
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 (NONJUMP_INSN_P (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 (! (CALL_P (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 (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 (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 (void)
{
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 (void)
{
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 (void)
{
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,
current_function_func_begin_label,
"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,
current_function_func_begin_label,
"landing pad");
else
dw2_asm_output_data_uleb128 (0, "landing pad");
#else
dw2_asm_output_delta (4, reg_start_lab,
current_function_func_begin_label,
"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,
current_function_func_begin_label,
"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 (void)
{
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 (void)
{
if (targetm.have_named_sections)
{
int flags;
if (EH_TABLES_CAN_BE_READ_ONLY)
{
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;
named_section_flags (".gcc_except_table", flags);
}
else if (flag_pic)
data_section ();
else
readonly_data_section ();
}
void
output_function_exception_table (void)
{
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 TARGET_UNWIND_INFO
assemble_external_libcall (eh_personality_libfunc);
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
targetm.asm_out.except_table_label (asm_out_file);
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);
}
targetm.asm_out.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)
value = const0_rtx;
else
{
struct cgraph_varpool_node *node;
type = lookup_type_for_runtime (type);
value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
STRIP_NOPS (type);
if (TREE_CODE (type) == ADDR_EXPR)
{
type = TREE_OPERAND (type, 0);
if (TREE_CODE (type) == VAR_DECL)
{
node = cgraph_varpool_node (type);
if (node)
cgraph_varpool_mark_needed_node (node);
}
}
else
gcc_assert (TREE_CODE (type) == INTEGER_CST);
}
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"