- We merge all MUST_NOT_THROW regions handled by the run-time into one.
- We alsobring all local MUST_NOT_THROW regions to the roots of EH tree
- (since unwinding never continues to the outer region anyway).
- If MUST_NOT_THROW with local handler is present in the tree, we use
- that region to merge into, since it will remain in tree anyway;
- otherwise we use first MUST_NOT_THROW.
-
- Merging of locally handled regions needs changes to the CFG. Crossjumping
- should take care of this, by looking at the actual code and
- ensuring that the cleanup actions are really the same. */
-
- if (local_must_not_throw)
- first_must_not_throw = local_must_not_throw;
-
- for (i = 0; VEC_iterate (eh_region, must_not_throws, i, r); i++)
- {
- if (!r->label && !r->tree_label && r != first_must_not_throw)
- {
- if (dump_file)
- fprintf (dump_file, "Replacing MUST_NOT_THROW region %i by %i\n",
- r->region_number,
- first_must_not_throw->region_number);
- remove_eh_handler_and_replace (r, first_must_not_throw, false);
- first_must_not_throw->may_contain_throw |= r->may_contain_throw;
- }
- else
- bring_to_root (r);
- }
- merge_peers (cfun->eh->region_tree);
-#ifdef ENABLE_CHECKING
- verify_eh_tree (cfun);
-#endif
- VEC_free (eh_region, heap, must_not_throws);
-}
-
-/* Return array mapping LABEL_DECL_UID to region such that region's tree_label
- is identical to label. */
-
-VEC (int, heap) *
-label_to_region_map (void)
-{
- VEC (int, heap) * label_to_region = NULL;
- int i;
- int idx;
-
- VEC_safe_grow_cleared (int, heap, label_to_region,
- cfun->cfg->last_label_uid + 1);
- for (i = cfun->eh->last_region_number; i > 0; --i)
- {
- struct eh_region_d *r = VEC_index (eh_region, cfun->eh->region_array, i);
- if (r && r->region_number == i
- && r->tree_label && LABEL_DECL_UID (r->tree_label) >= 0)
- {
- if ((idx = VEC_index (int, label_to_region,
- LABEL_DECL_UID (r->tree_label))) != 0)
- r->next_region_sharing_label =
- VEC_index (eh_region, cfun->eh->region_array, idx);
- else
- r->next_region_sharing_label = NULL;
- VEC_replace (int, label_to_region, LABEL_DECL_UID (r->tree_label),
- i);
- }
- }
- return label_to_region;
-}
-
-/* Return number of EH regions. */
-int
-num_eh_regions (void)
-{
- return cfun->eh->last_region_number + 1;
-}
-
-/* Return next region sharing same label as REGION. */
-
-int
-get_next_region_sharing_label (int region)
-{
- struct eh_region_d *r;
- if (!region)
- return 0;
- r = VEC_index (eh_region, cfun->eh->region_array, region);
- if (!r || !r->next_region_sharing_label)
- return 0;
- return r->next_region_sharing_label->region_number;
-}
-
-/* Return bitmap of all labels that are handlers of must not throw regions. */
-
-bitmap
-must_not_throw_labels (void)
-{
- struct eh_region_d *i;
- bitmap labels = BITMAP_ALLOC (NULL);
-
- i = cfun->eh->region_tree;
- if (! i)
- return labels;
-
- while (1)
- {
- if (i->type == ERT_MUST_NOT_THROW && i->tree_label
- && LABEL_DECL_UID (i->tree_label) >= 0)
- bitmap_set_bit (labels, LABEL_DECL_UID (i->tree_label));
-
- /* If there are sub-regions, process them. */
- if (i->inner)
- i = i->inner;
- /* If there are peers, process them. */
- else if (i->next_peer)
- i = i->next_peer;
- /* Otherwise, step back up the tree to the next peer. */
- else
- {
- do {
- i = i->outer;
- if (i == NULL)
- return labels;
- } while (i->next_peer == NULL);
- i = i->next_peer;
- }
- }
-}
-
-/* Set up EH labels for RTL. */
-
-void
-convert_from_eh_region_ranges (void)
-{
- int i, n = cfun->eh->last_region_number;
-
- /* Most of the work is already done at the tree level. All we need to
- do is collect the rtl labels that correspond to the tree labels that
- collect the rtl labels that correspond to the tree labels
- we allocated earlier. */
- for (i = 1; i <= n; ++i)
- {
- struct eh_region_d *region;
-
- region = VEC_index (eh_region, cfun->eh->region_array, i);
- if (region && region->tree_label)
- region->label = DECL_RTL_IF_SET (region->tree_label);
- }
-}
-
-void
-find_exception_handler_labels (void)
-{
- int i;
-
- if (cfun->eh->region_tree == NULL)
- return;
-
- for (i = cfun->eh->last_region_number; i > 0; --i)
- {
- struct eh_region_d *region;
- rtx lab;
-
- region = VEC_index (eh_region, cfun->eh->region_array, i);
- if (! region || region->region_number != i)
- continue;
- if (crtl->eh.built_landing_pads)
- lab = region->landing_pad;
- else
- lab = region->label;
- }
-}
-
-/* Returns true if the current function has exception handling regions. */
-
-bool
-current_function_has_exception_handlers (void)
-{
- int i;
-
- for (i = cfun->eh->last_region_number; i > 0; --i)
- {
- struct eh_region_d *region;
-
- region = VEC_index (eh_region, cfun->eh->region_array, i);
- if (region
- && region->region_number == i
- && region->type != ERT_THROW)
- return true;
- }
-
- return false;
-}
-\f
-/* A subroutine of duplicate_eh_regions. Search the region tree under O
- for the minimum and maximum region numbers. Update *MIN and *MAX. */
-
-static void
-duplicate_eh_regions_0 (eh_region o, int *min, int *max)
-{
- int i;
-
- if (o->aka)
- {
- i = bitmap_first_set_bit (o->aka);
- if (i < *min)
- *min = i;
- i = bitmap_last_set_bit (o->aka);
- if (i > *max)
- *max = i;
- }
- if (o->region_number < *min)
- *min = o->region_number;
- if (o->region_number > *max)
- *max = o->region_number;
-
- if (o->inner)
- {
- o = o->inner;
- duplicate_eh_regions_0 (o, min, max);
- while (o->next_peer)
- {
- o = o->next_peer;
- duplicate_eh_regions_0 (o, min, max);
- }
- }
-}
-
-/* A subroutine of duplicate_eh_regions. Copy the region tree under OLD.
- Root it at OUTER, and apply EH_OFFSET to the region number. Don't worry
- about the other internal pointers just yet, just the tree-like pointers. */
-
-static eh_region
-duplicate_eh_regions_1 (eh_region old, eh_region outer, int eh_offset)
-{
- eh_region ret, n;
-
- ret = n = GGC_NEW (struct eh_region_d);
-
- *n = *old;
- n->outer = outer;
- n->next_peer = NULL;
- if (old->aka)
- {
- unsigned i;
- bitmap_iterator bi;
- n->aka = BITMAP_GGC_ALLOC ();
-
- EXECUTE_IF_SET_IN_BITMAP (old->aka, 0, i, bi)
- {
- bitmap_set_bit (n->aka, i + eh_offset);
- VEC_replace (eh_region, cfun->eh->region_array, i + eh_offset, n);
- }
- }
-
- n->region_number += eh_offset;
- VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n);
-
- if (old->inner)
- {
- old = old->inner;
- n = n->inner = duplicate_eh_regions_1 (old, ret, eh_offset);
- while (old->next_peer)
- {
- old = old->next_peer;
- n = n->next_peer = duplicate_eh_regions_1 (old, ret, eh_offset);
- }
- }
-
- return ret;
-}
-
-/* Look for first outer region of R (or R itself) that is
- TRY region. Return NULL if none. */
-
-static struct eh_region_d *
-find_prev_try (struct eh_region_d * r)
-{
- for (; r && r->type != ERT_TRY; r = r->outer)
- if (r->type == ERT_MUST_NOT_THROW
- || (r->type == ERT_ALLOWED_EXCEPTIONS
- && !r->u.allowed.type_list))
- {
- r = NULL;
- break;
- }
- return r;
-}
-
-/* Duplicate the EH regions of IFUN, rooted at COPY_REGION, into current
- function and root the tree below OUTER_REGION. Remap labels using MAP
- callback. The special case of COPY_REGION of 0 means all regions. */
-
-int
-duplicate_eh_regions (struct function *ifun, duplicate_eh_regions_map map,
- void *data, int copy_region, int outer_region)
-{
- eh_region cur, outer, *splice;
- int i, min_region, max_region, eh_offset, cfun_last_region_number;
- int num_regions;
-
- if (!ifun->eh)
- return 0;
-#ifdef ENABLE_CHECKING
- verify_eh_tree (ifun);
-#endif
-
- /* Find the range of region numbers to be copied. The interface we
- provide here mandates a single offset to find new number from old,
- which means we must look at the numbers present, instead of the
- count or something else. */
- if (copy_region > 0)
- {
- min_region = INT_MAX;
- max_region = 0;
-
- cur = VEC_index (eh_region, ifun->eh->region_array, copy_region);
- duplicate_eh_regions_0 (cur, &min_region, &max_region);
- }
- else
- {
- min_region = 1;
- max_region = ifun->eh->last_region_number;
- }
- num_regions = max_region - min_region + 1;
- cfun_last_region_number = cfun->eh->last_region_number;
- eh_offset = cfun_last_region_number + 1 - min_region;
-
- /* If we've not yet created a region array, do so now. */
- cfun->eh->last_region_number = cfun_last_region_number + num_regions;
- VEC_safe_grow_cleared (eh_region, gc, cfun->eh->region_array,
- cfun->eh->last_region_number + 1);
-
- /* Locate the spot at which to insert the new tree. */
- if (outer_region > 0)
- {
- outer = VEC_index (eh_region, cfun->eh->region_array, outer_region);
- if (outer)
- splice = &outer->inner;
- else
- splice = &cfun->eh->region_tree;
- }
- else
- {
- outer = NULL;
- splice = &cfun->eh->region_tree;
- }
- while (*splice)
- splice = &(*splice)->next_peer;
-
- if (!ifun->eh->region_tree)
- {
- if (outer)
- for (i = cfun_last_region_number + 1;
- i <= cfun->eh->last_region_number; i++)
- {
- VEC_replace (eh_region, cfun->eh->region_array, i, outer);
- if (outer->aka == NULL)
- outer->aka = BITMAP_GGC_ALLOC ();
- bitmap_set_bit (outer->aka, i);
- }
- return eh_offset;
- }
-
- /* Copy all the regions in the subtree. */
- if (copy_region > 0)
- {
- cur = VEC_index (eh_region, ifun->eh->region_array, copy_region);
- *splice = duplicate_eh_regions_1 (cur, outer, eh_offset);
- }
- else
- {
- eh_region n;
-
- cur = ifun->eh->region_tree;
- *splice = n = duplicate_eh_regions_1 (cur, outer, eh_offset);
- while (cur->next_peer)
- {
- cur = cur->next_peer;
- n = n->next_peer = duplicate_eh_regions_1 (cur, outer, eh_offset);
- }
- }
-
- /* Remap all the labels in the new regions. */
- for (i = cfun_last_region_number + 1;
- VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i)
- if (cur && cur->tree_label)
- cur->tree_label = map (cur->tree_label, data);
-
- /* Remap all of the internal catch and cleanup linkages. Since we
- duplicate entire subtrees, all of the referenced regions will have
- been copied too. And since we renumbered them as a block, a simple
- bit of arithmetic finds us the index for the replacement region. */
- for (i = cfun_last_region_number + 1;
- VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i)
- {
- /* All removed EH that is toplevel in input function is now
- in outer EH of output function. */
- if (cur == NULL)
- {
- gcc_assert (VEC_index
- (eh_region, ifun->eh->region_array,
- i - eh_offset) == NULL);
- if (outer)
- {
- VEC_replace (eh_region, cfun->eh->region_array, i, outer);
- if (outer->aka == NULL)
- outer->aka = BITMAP_GGC_ALLOC ();
- bitmap_set_bit (outer->aka, i);
- }
- continue;
- }
- if (i != cur->region_number)
- continue;
-
-#define REMAP(REG) \
- (REG) = VEC_index (eh_region, cfun->eh->region_array, \
- (REG)->region_number + eh_offset)
-
- switch (cur->type)
- {
- case ERT_TRY:
- if (cur->u.eh_try.eh_catch)
- REMAP (cur->u.eh_try.eh_catch);
- if (cur->u.eh_try.last_catch)
- REMAP (cur->u.eh_try.last_catch);
- break;
-
- case ERT_CATCH:
- if (cur->u.eh_catch.next_catch)
- REMAP (cur->u.eh_catch.next_catch);
- if (cur->u.eh_catch.prev_catch)
- REMAP (cur->u.eh_catch.prev_catch);
- break;
-
- default:
- break;
- }
-
-#undef REMAP
- }
-#ifdef ENABLE_CHECKING
- verify_eh_tree (cfun);
-#endif
-
- return eh_offset;
-}
-
-/* Return new copy of eh region OLD inside region NEW_OUTER.
- Do not care about updating the tree otherwise. */
-
-static struct eh_region_d *
-copy_eh_region_1 (struct eh_region_d *old, struct eh_region_d *new_outer)
-{
- struct eh_region_d *new_eh = gen_eh_region (old->type, new_outer);
- new_eh->u = old->u;
- new_eh->tree_label = old->tree_label;
- new_eh->may_contain_throw = old->may_contain_throw;
- VEC_safe_grow (eh_region, gc, cfun->eh->region_array,
- cfun->eh->last_region_number + 1);
- VEC_replace (eh_region, cfun->eh->region_array, new_eh->region_number, new_eh);
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Copying region %i to %i\n", old->region_number, new_eh->region_number);
- return new_eh;
-}
-
-/* Return new copy of eh region OLD inside region NEW_OUTER.
-
- Copy whole catch-try chain if neccesary. */
-
-static struct eh_region_d *
-copy_eh_region (struct eh_region_d *old, struct eh_region_d *new_outer)
-{
- struct eh_region_d *r, *n, *old_try, *new_try, *ret = NULL;
- VEC(eh_region,heap) *catch_list = NULL;
-
- if (old->type != ERT_CATCH)
- {
- gcc_assert (old->type != ERT_TRY);
- r = copy_eh_region_1 (old, new_outer);
- return r;
- }
-
- /* Locate and copy corresponding TRY. */
- for (old_try = old->next_peer; old_try->type == ERT_CATCH; old_try = old_try->next_peer)
- continue;
- gcc_assert (old_try->type == ERT_TRY);
- new_try = gen_eh_region_try (new_outer);
- new_try->tree_label = old_try->tree_label;
- new_try->may_contain_throw = old_try->may_contain_throw;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Copying try-catch regions. Try: %i to %i\n",
- old_try->region_number, new_try->region_number);
- VEC_safe_grow (eh_region, gc, cfun->eh->region_array,
- cfun->eh->last_region_number + 1);
- VEC_replace (eh_region, cfun->eh->region_array, new_try->region_number, new_try);
-
- /* In order to keep CATCH list in order, we need to copy in reverse order. */
- for (r = old_try->u.eh_try.last_catch; r->type == ERT_CATCH; r = r->next_peer)
- VEC_safe_push (eh_region, heap, catch_list, r);
-
- while (VEC_length (eh_region, catch_list))
- {
- r = VEC_pop (eh_region, catch_list);
-
- /* Duplicate CATCH. */
- n = gen_eh_region_catch (new_try, r->u.eh_catch.type_list);
- n->tree_label = r->tree_label;
- n->may_contain_throw = r->may_contain_throw;
- VEC_safe_grow (eh_region, gc, cfun->eh->region_array,
- cfun->eh->last_region_number + 1);
- VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n);
- n->tree_label = r->tree_label;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Copying try-catch regions. Catch: %i to %i\n",
- r->region_number, n->region_number);
- if (r == old)
- ret = n;
- }
- VEC_free (eh_region, heap, catch_list);
- gcc_assert (ret);
- return ret;
-}
-
-/* Callback for forach_reachable_handler that push REGION into single VECtor DATA. */
-
-static void
-push_reachable_handler (struct eh_region_d *region, void *data)
-{
- VEC(eh_region,heap) **trace = (VEC(eh_region,heap) **) data;
- VEC_safe_push (eh_region, heap, *trace, region);
-}
-
-/* Redirect EH edge E that to NEW_DEST_LABEL.
- IS_RESX, INLINABLE_CALL and REGION_NMUBER match the parameter of
- foreach_reachable_handler. */
-
-struct eh_region_d *
-redirect_eh_edge_to_label (edge e, tree new_dest_label, bool is_resx,
- bool inlinable_call, int region_number)
-{
- struct eh_region_d *outer;
- struct eh_region_d *region;
- VEC (eh_region, heap) * trace = NULL;
- int i;
- int start_here = -1;
- basic_block old_bb = e->dest;
- struct eh_region_d *old, *r = NULL;
- bool update_inplace = true;
- edge_iterator ei;
- edge e2;
-
- /* If there is only one EH edge, we don't need to duplicate;
- just update labels in the tree. */
- FOR_EACH_EDGE (e2, ei, old_bb->preds)
- if ((e2->flags & EDGE_EH) && e2 != e)
- {
- update_inplace = false;
- break;
- }
-
- region = VEC_index (eh_region, cfun->eh->region_array, region_number);
- gcc_assert (region);
-
- foreach_reachable_handler (region_number, is_resx, inlinable_call,
- push_reachable_handler, &trace);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- dump_eh_tree (dump_file, cfun);
- fprintf (dump_file, "Trace: ");
- for (i = 0; i < (int) VEC_length (eh_region, trace); i++)
- fprintf (dump_file, " %i", VEC_index (eh_region, trace, i)->region_number);
- fprintf (dump_file, " inplace: %i\n", update_inplace);
- }
-
- if (update_inplace)
- {
- /* In easy route just walk trace and update all occurences of the label. */
- for (i = 0; i < (int) VEC_length (eh_region, trace); i++)
- {
- r = VEC_index (eh_region, trace, i);
- if (r->tree_label && label_to_block (r->tree_label) == old_bb)
- {
- r->tree_label = new_dest_label;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Updating label for region %i\n",
- r->region_number);
- }
- }
- r = region;
- }
- else
- {
- /* Now look for outermost handler that reffers to the basic block in question.
- We start our duplication there. */
- for (i = 0; i < (int) VEC_length (eh_region, trace); i++)
- {
- r = VEC_index (eh_region, trace, i);
- if (r->tree_label && label_to_block (r->tree_label) == old_bb)
- start_here = i;
- }
- outer = VEC_index (eh_region, trace, start_here)->outer;
- gcc_assert (start_here >= 0);
-
- /* And now do the dirty job! */
- for (i = start_here; i >= 0; i--)
- {
- old = VEC_index (eh_region, trace, i);
- gcc_assert (!outer || old->outer != outer->outer);
-
- /* Copy region and update label. */
- r = copy_eh_region (old, outer);
- VEC_replace (eh_region, trace, i, r);
- if (r->tree_label && label_to_block (r->tree_label) == old_bb)
- {
- r->tree_label = new_dest_label;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Updating label for region %i\n",
- r->region_number);
- }
-
- /* We got into copying CATCH. copy_eh_region already did job
- of copying all catch blocks corresponding to the try. Now
- we need to update labels in all of them and see trace.
-
- We continue nesting into TRY region corresponding to CATCH:
- When duplicating EH tree contaiing subregions of CATCH,
- the CATCH region itself is never inserted to trace so we
- never get here anyway. */
- if (r->type == ERT_CATCH)
- {
- /* Walk other catch regions we copied and update labels as needed. */
- for (r = r->next_peer; r->type == ERT_CATCH; r = r->next_peer)
- if (r->tree_label && label_to_block (r->tree_label) == old_bb)
- {
- r->tree_label = new_dest_label;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Updating label for region %i\n",
- r->region_number);
- }
- gcc_assert (r->type == ERT_TRY);
-
- /* Skip sibling catch regions from the trace.
- They are already updated. */
- while (i > 0 && VEC_index (eh_region, trace, i - 1)->outer == old->outer)
- {
- gcc_assert (VEC_index (eh_region, trace, i - 1)->type == ERT_CATCH);
- i--;
- }
- }
-
- outer = r;
- }
-
- if (is_resx || region->type == ERT_THROW)
- r = copy_eh_region (region, outer);
- }
-
- VEC_free (eh_region, heap, trace);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- dump_eh_tree (dump_file, cfun);
- fprintf (dump_file, "New region: %i\n", r->region_number);
- }
- return r;
-}
-
-/* Return region number of region that is outer to both if REGION_A and
- REGION_B in IFUN. */
-
-int
-eh_region_outermost (struct function *ifun, int region_a, int region_b)
-{
- struct eh_region_d *rp_a, *rp_b;
- sbitmap b_outer;
-
- gcc_assert (ifun->eh->last_region_number > 0);
- gcc_assert (ifun->eh->region_tree);
-
- rp_a = VEC_index (eh_region, ifun->eh->region_array, region_a);
- rp_b = VEC_index (eh_region, ifun->eh->region_array, region_b);
- gcc_assert (rp_a != NULL);
- gcc_assert (rp_b != NULL);
-
- b_outer = sbitmap_alloc (ifun->eh->last_region_number + 1);
- sbitmap_zero (b_outer);
-
- do
- {
- SET_BIT (b_outer, rp_b->region_number);
- rp_b = rp_b->outer;
- }
- while (rp_b);
-
- do
- {
- if (TEST_BIT (b_outer, rp_a->region_number))
- {
- sbitmap_free (b_outer);
- return rp_a->region_number;
- }
- rp_a = rp_a->outer;
- }
- while (rp_a);
-
- sbitmap_free (b_outer);
- return -1;
-}
-\f
-static int
-t2r_eq (const void *pentry, const void *pdata)
-{
- const_tree const entry = (const_tree) pentry;
- const_tree const data = (const_tree) pdata;
-
- return TREE_PURPOSE (entry) == data;
-}
-
-static hashval_t
-t2r_hash (const void *pentry)
-{
- const_tree const entry = (const_tree) pentry;
- return TREE_HASH (TREE_PURPOSE (entry));
-}
-
-void
-add_type_for_runtime (tree type)
-{
- tree *slot;
-
- /* If TYPE is NOP_EXPR, it means that it already is a runtime type. */
- if (TREE_CODE (type) == NOP_EXPR)
- return;
-
- slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
- TREE_HASH (type), INSERT);
- if (*slot == NULL)
- {
- tree runtime = lang_hooks.eh_runtime_type (type);
- *slot = tree_cons (type, runtime, NULL_TREE);
- }
-}
-
-tree
-lookup_type_for_runtime (tree type)
-{
- tree *slot;
-
- /* If TYPE is NOP_EXPR, it means that it already is a runtime type. */
- if (TREE_CODE (type) == NOP_EXPR)
- return type;
-
- slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
- TREE_HASH (type), NO_INSERT);
-
- /* We should have always inserted the data earlier. */
- return TREE_VALUE (*slot);
-}
-
-\f
-/* Represent an entry in @TTypes for either catch actions
- or exception filter actions. */
-struct GTY(()) ttypes_filter {
- tree t;
- int filter;
-};
-
-/* Compare ENTRY (a ttypes_filter entry in the hash table) with DATA
- (a tree) for a @TTypes type node we are thinking about adding. */
-
-static int
-ttypes_filter_eq (const void *pentry, const void *pdata)
-{
- const struct ttypes_filter *const entry
- = (const struct ttypes_filter *) pentry;
- const_tree const data = (const_tree) pdata;