+ /* Remove BB from the original basic block array. */
+ VEC_replace (basic_block, cfun->cfg->x_basic_block_info, bb->index, NULL);
+ cfun->cfg->x_n_basic_blocks--;
+
+ /* Grow DEST_CFUN's basic block array if needed. */
+ cfg = dest_cfun->cfg;
+ cfg->x_n_basic_blocks++;
+ if (bb->index >= cfg->x_last_basic_block)
+ cfg->x_last_basic_block = bb->index + 1;
+
+ old_len = VEC_length (basic_block, cfg->x_basic_block_info);
+ if ((unsigned) cfg->x_last_basic_block >= old_len)
+ {
+ new_len = cfg->x_last_basic_block + (cfg->x_last_basic_block + 3) / 4;
+ VEC_safe_grow_cleared (basic_block, gc, cfg->x_basic_block_info,
+ new_len);
+ }
+
+ VEC_replace (basic_block, cfg->x_basic_block_info,
+ bb->index, bb);
+
+ /* The statements in BB need to be associated with a new TREE_BLOCK.
+ Labels need to be associated with a new label-to-block map. */
+ memset (&d, 0, sizeof (d));
+ d.vars_to_remove = vars_to_remove;
+
+ for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+ {
+ tree stmt = bsi_stmt (si);
+ int region;
+
+ d.from_context = cfun->decl;
+ d.to_context = dest_cfun->decl;
+ d.remap_decls_p = true;
+ d.new_label_map = new_label_map;
+ if (TREE_BLOCK (stmt))
+ d.block = DECL_INITIAL (dest_cfun->decl);
+
+ walk_tree (&stmt, move_stmt_r, &d, NULL);
+
+ if (TREE_CODE (stmt) == LABEL_EXPR)
+ {
+ tree label = LABEL_EXPR_LABEL (stmt);
+ int uid = LABEL_DECL_UID (label);
+
+ gcc_assert (uid > -1);
+
+ old_len = VEC_length (basic_block, cfg->x_label_to_block_map);
+ if (old_len <= (unsigned) uid)
+ {
+ new_len = 3 * uid / 2;
+ VEC_safe_grow_cleared (basic_block, gc,
+ cfg->x_label_to_block_map, new_len);
+ }
+
+ VEC_replace (basic_block, cfg->x_label_to_block_map, uid, bb);
+ VEC_replace (basic_block, cfun->cfg->x_label_to_block_map, uid, NULL);
+
+ gcc_assert (DECL_CONTEXT (label) == dest_cfun->decl);
+
+ if (uid >= dest_cfun->last_label_uid)
+ dest_cfun->last_label_uid = uid + 1;
+ }
+ else if (TREE_CODE (stmt) == RESX_EXPR && eh_offset != 0)
+ TREE_OPERAND (stmt, 0) =
+ build_int_cst (NULL_TREE,
+ TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0))
+ + eh_offset);
+
+ region = lookup_stmt_eh_region (stmt);
+ if (region >= 0)
+ {
+ add_stmt_to_eh_region_fn (dest_cfun, stmt, region + eh_offset);
+ remove_stmt_from_eh_region (stmt);
+ gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt);
+ gimple_remove_stmt_histograms (cfun, stmt);
+ }
+ }
+}
+
+/* Examine the statements in BB (which is in SRC_CFUN); find and return
+ the outermost EH region. Use REGION as the incoming base EH region. */
+
+static int
+find_outermost_region_in_block (struct function *src_cfun,
+ basic_block bb, int region)
+{
+ block_stmt_iterator si;
+
+ for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+ {
+ tree stmt = bsi_stmt (si);
+ int stmt_region;
+
+ if (TREE_CODE (stmt) == RESX_EXPR)
+ stmt_region = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0));
+ else
+ stmt_region = lookup_stmt_eh_region_fn (src_cfun, stmt);
+ if (stmt_region > 0)
+ {
+ if (region < 0)
+ region = stmt_region;
+ else if (stmt_region != region)
+ {
+ region = eh_region_outermost (src_cfun, stmt_region, region);
+ gcc_assert (region != -1);
+ }
+ }
+ }
+
+ return region;
+}
+
+static tree
+new_label_mapper (tree decl, void *data)
+{
+ htab_t hash = (htab_t) data;
+ struct tree_map *m;
+ void **slot;
+
+ gcc_assert (TREE_CODE (decl) == LABEL_DECL);
+
+ m = xmalloc (sizeof (struct tree_map));
+ m->hash = DECL_UID (decl);
+ m->base.from = decl;
+ m->to = create_artificial_label ();
+ LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl);
+
+ slot = htab_find_slot_with_hash (hash, m, m->hash, INSERT);
+ gcc_assert (*slot == NULL);
+
+ *slot = m;
+
+ return m->to;
+}
+
+/* Move a single-entry, single-exit region delimited by ENTRY_BB and
+ EXIT_BB to function DEST_CFUN. The whole region is replaced by a
+ single basic block in the original CFG and the new basic block is
+ returned. DEST_CFUN must not have a CFG yet.
+
+ Note that the region need not be a pure SESE region. Blocks inside
+ the region may contain calls to abort/exit. The only restriction
+ is that ENTRY_BB should be the only entry point and it must
+ dominate EXIT_BB.
+
+ All local variables referenced in the region are assumed to be in
+ the corresponding BLOCK_VARS and unexpanded variable lists
+ associated with DEST_CFUN. */
+
+basic_block
+move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
+ basic_block exit_bb)
+{
+ VEC(basic_block,heap) *bbs;
+ basic_block after, bb, *entry_pred, *exit_succ;
+ struct function *saved_cfun;
+ int *entry_flag, *exit_flag, eh_offset;
+ unsigned i, num_entry_edges, num_exit_edges;
+ edge e;
+ edge_iterator ei;
+ bitmap vars_to_remove;
+ htab_t new_label_map;
+
+ saved_cfun = cfun;
+
+ /* Collect all the blocks in the region. Manually add ENTRY_BB
+ because it won't be added by dfs_enumerate_from. */
+ calculate_dominance_info (CDI_DOMINATORS);
+
+ /* If ENTRY does not strictly dominate EXIT, this cannot be an SESE
+ region. */
+ gcc_assert (entry_bb != exit_bb
+ && (!exit_bb
+ || dominated_by_p (CDI_DOMINATORS, exit_bb, entry_bb)));
+
+ bbs = NULL;
+ VEC_safe_push (basic_block, heap, bbs, entry_bb);
+ gather_blocks_in_sese_region (entry_bb, exit_bb, &bbs);
+
+ /* Detach ENTRY_BB and EXIT_BB from CFUN->CFG. We need to remember
+ the predecessor edges to ENTRY_BB and the successor edges to
+ EXIT_BB so that we can re-attach them to the new basic block that
+ will replace the region. */
+ num_entry_edges = EDGE_COUNT (entry_bb->preds);
+ entry_pred = (basic_block *) xcalloc (num_entry_edges, sizeof (basic_block));
+ entry_flag = (int *) xcalloc (num_entry_edges, sizeof (int));
+ i = 0;
+ for (ei = ei_start (entry_bb->preds); (e = ei_safe_edge (ei)) != NULL;)
+ {
+ entry_flag[i] = e->flags;
+ entry_pred[i++] = e->src;
+ remove_edge (e);
+ }
+
+ if (exit_bb)
+ {
+ num_exit_edges = EDGE_COUNT (exit_bb->succs);
+ exit_succ = (basic_block *) xcalloc (num_exit_edges,
+ sizeof (basic_block));
+ exit_flag = (int *) xcalloc (num_exit_edges, sizeof (int));
+ i = 0;
+ for (ei = ei_start (exit_bb->succs); (e = ei_safe_edge (ei)) != NULL;)
+ {
+ exit_flag[i] = e->flags;
+ exit_succ[i++] = e->dest;
+ remove_edge (e);
+ }
+ }