static basic_block hoist_edge_and_branch_if_true (gimple_stmt_iterator *gsip, tree cond, edge e_true, bool update_dominators) { tree tmp; gcond *cond_stmt; edge e_false; basic_block new_bb, split_bb = gsi_bb (*gsip); bool dominated_e_true = false; gcc_assert (e_true->src == split_bb); if (update_dominators && get_immediate_dominator (CDI_DOMINATORS, e_true->dest) == split_bb) dominated_e_true = true; tmp = force_gimple_operand_gsi (gsip, cond, /*simple=*/true, NULL, /*before=*/true, GSI_SAME_STMT); cond_stmt = gimple_build_cond_from_tree (tmp, NULL_TREE, NULL_TREE); gsi_insert_before (gsip, cond_stmt, GSI_SAME_STMT); e_false = split_block (split_bb, cond_stmt); new_bb = e_false->dest; redirect_edge_pred (e_true, split_bb); e_true->flags &= ~EDGE_FALLTHRU; e_true->flags |= EDGE_TRUE_VALUE; e_false->flags &= ~EDGE_FALLTHRU; e_false->flags |= EDGE_FALSE_VALUE; e_false->probability = REG_BR_PROB_BASE - e_true->probability; e_false->count = split_bb->count - e_true->count; new_bb->count = e_false->count; if (update_dominators) { if (dominated_e_true) set_immediate_dominator (CDI_DOMINATORS, e_true->dest, split_bb); set_immediate_dominator (CDI_DOMINATORS, e_false->dest, split_bb); } return new_bb; }
static void unloop_loops (bitmap loop_closed_ssa_invalidated, bool *irred_invalidated) { while (loops_to_unloop.length ()) { struct loop *loop = loops_to_unloop.pop (); int n_unroll = loops_to_unloop_nunroll.pop (); basic_block latch = loop->latch; edge latch_edge = loop_latch_edge (loop); int flags = latch_edge->flags; location_t locus = latch_edge->goto_locus; gcall *stmt; gimple_stmt_iterator gsi; remove_exits_and_undefined_stmts (loop, n_unroll); /* Unloop destroys the latch edge. */ unloop (loop, irred_invalidated, loop_closed_ssa_invalidated); /* Create new basic block for the latch edge destination and wire it in. */ stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags); latch_edge->probability = 0; latch_edge->count = 0; latch_edge->flags |= flags; latch_edge->goto_locus = locus; latch_edge->dest->loop_father = current_loops->tree_root; latch_edge->dest->count = 0; latch_edge->dest->frequency = 0; set_immediate_dominator (CDI_DOMINATORS, latch_edge->dest, latch_edge->src); gsi = gsi_start_bb (latch_edge->dest); gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); } loops_to_unloop.release (); loops_to_unloop_nunroll.release (); /* Remove edges in peeled copies. */ unsigned i; edge e; FOR_EACH_VEC_ELT (edges_to_remove, i, e) { bool ok = remove_path (e); gcc_assert (ok); }
static void mf_build_check_statement_for (tree base, tree limit, gimple_stmt_iterator *instr_gsi, location_t location, tree dirflag) { gimple_stmt_iterator gsi; basic_block cond_bb, then_bb, join_bb; edge e; tree cond, t, u, v; tree mf_base; tree mf_elem; tree mf_limit; gimple g; gimple_seq seq, stmts; /* We first need to split the current basic block, and start altering the CFG. This allows us to insert the statements we're about to construct into the right basic blocks. */ cond_bb = gimple_bb (gsi_stmt (*instr_gsi)); gsi = *instr_gsi; gsi_prev (&gsi); if (! gsi_end_p (gsi)) e = split_block (cond_bb, gsi_stmt (gsi)); else e = split_block_after_labels (cond_bb); cond_bb = e->src; join_bb = e->dest; /* A recap at this point: join_bb is the basic block at whose head is the gimple statement for which this check expression is being built. cond_bb is the (possibly new, synthetic) basic block the end of which will contain the cache-lookup code, and a conditional that jumps to the cache-miss code or, much more likely, over to join_bb. */ /* Create the bb that contains the cache-miss fallback block (mf_check). */ then_bb = create_empty_bb (cond_bb); make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE); make_single_succ_edge (then_bb, join_bb, EDGE_FALLTHRU); /* Mark the pseudo-fallthrough edge from cond_bb to join_bb. */ e = find_edge (cond_bb, join_bb); e->flags = EDGE_FALSE_VALUE; e->count = cond_bb->count; e->probability = REG_BR_PROB_BASE; /* Update dominance info. Note that bb_join's data was updated by split_block. */ if (dom_info_available_p (CDI_DOMINATORS)) { set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); set_immediate_dominator (CDI_DOMINATORS, join_bb, cond_bb); } /* Update loop info. */ if (current_loops) add_bb_to_loop (then_bb, cond_bb->loop_father); /* Build our local variables. */ mf_elem = create_tmp_reg (mf_cache_structptr_type, "__mf_elem"); mf_base = create_tmp_reg (mf_uintptr_type, "__mf_base"); mf_limit = create_tmp_reg (mf_uintptr_type, "__mf_limit"); /* Build: __mf_base = (uintptr_t) <base address expression>. */ seq = NULL; t = fold_convert_loc (location, mf_uintptr_type, unshare_expr (base)); t = force_gimple_operand (t, &stmts, false, NULL_TREE); gimple_seq_add_seq (&seq, stmts); g = gimple_build_assign (mf_base, t); gimple_set_location (g, location); gimple_seq_add_stmt (&seq, g); /* Build: __mf_limit = (uintptr_t) <limit address expression>. */ t = fold_convert_loc (location, mf_uintptr_type, unshare_expr (limit)); t = force_gimple_operand (t, &stmts, false, NULL_TREE); gimple_seq_add_seq (&seq, stmts); g = gimple_build_assign (mf_limit, t); gimple_set_location (g, location); gimple_seq_add_stmt (&seq, g); /* Build: __mf_elem = &__mf_lookup_cache [(__mf_base >> __mf_shift) & __mf_mask]. */ t = build2 (RSHIFT_EXPR, mf_uintptr_type, mf_base, flag_mudflap_threads ? mf_cache_shift_decl : mf_cache_shift_decl_l); t = build2 (BIT_AND_EXPR, mf_uintptr_type, t, flag_mudflap_threads ? mf_cache_mask_decl : mf_cache_mask_decl_l); t = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (mf_cache_array_decl)), mf_cache_array_decl, t, NULL_TREE, NULL_TREE); t = build1 (ADDR_EXPR, mf_cache_structptr_type, t); t = force_gimple_operand (t, &stmts, false, NULL_TREE); gimple_seq_add_seq (&seq, stmts); g = gimple_build_assign (mf_elem, t); gimple_set_location (g, location); gimple_seq_add_stmt (&seq, g); /* Quick validity check. if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) { __mf_check (); ... and only if single-threaded: __mf_lookup_shift_1 = f...; __mf_lookup_mask_l = ...; } It is expected that this body of code is rarely executed so we mark the edge to the THEN clause of the conditional jump as unlikely. */ /* Construct t <-- '__mf_elem->low > __mf_base'. */ t = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TYPE_FIELDS (mf_cache_struct_type), NULL_TREE); t = build2 (GT_EXPR, boolean_type_node, t, mf_base); /* Construct '__mf_elem->high < __mf_limit'. First build: 1) u <-- '__mf_elem->high' 2) v <-- '__mf_limit'. Then build 'u <-- (u < v). */ u = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), DECL_CHAIN (TYPE_FIELDS (mf_cache_struct_type)), NULL_TREE); v = mf_limit; u = build2 (LT_EXPR, boolean_type_node, u, v); /* Build the composed conditional: t <-- 't || u'. Then store the result of the evaluation of 't' in a temporary variable which we can use as the condition for the conditional jump. */ t = build2 (TRUTH_OR_EXPR, boolean_type_node, t, u); t = force_gimple_operand (t, &stmts, false, NULL_TREE); gimple_seq_add_seq (&seq, stmts); cond = create_tmp_reg (boolean_type_node, "__mf_unlikely_cond"); g = gimple_build_assign (cond, t); gimple_set_location (g, location); gimple_seq_add_stmt (&seq, g); /* Build the conditional jump. 'cond' is just a temporary so we can simply build a void COND_EXPR. We do need labels in both arms though. */ g = gimple_build_cond (NE_EXPR, cond, boolean_false_node, NULL_TREE, NULL_TREE); gimple_set_location (g, location); gimple_seq_add_stmt (&seq, g); /* At this point, after so much hard work, we have only constructed the conditional jump, if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) The lowered GIMPLE tree representing this code is in the statement list starting at 'head'. We can insert this now in the current basic block, i.e. the one that the statement we're instrumenting was originally in. */ gsi = gsi_last_bb (cond_bb); gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING); /* Now build up the body of the cache-miss handling: __mf_check(); refresh *_l vars. This is the body of the conditional. */ seq = NULL; /* u is a string, so it is already a gimple value. */ u = mf_file_function_line_tree (location); /* NB: we pass the overall [base..limit] range to mf_check. */ v = fold_build2_loc (location, PLUS_EXPR, mf_uintptr_type, fold_build2_loc (location, MINUS_EXPR, mf_uintptr_type, mf_limit, mf_base), build_int_cst (mf_uintptr_type, 1)); v = force_gimple_operand (v, &stmts, true, NULL_TREE); gimple_seq_add_seq (&seq, stmts); g = gimple_build_call (mf_check_fndecl, 4, mf_base, v, dirflag, u); gimple_seq_add_stmt (&seq, g); if (! flag_mudflap_threads) { if (stmt_ends_bb_p (g)) { gsi = gsi_start_bb (then_bb); gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING); e = split_block (then_bb, g); then_bb = e->dest; seq = NULL; } g = gimple_build_assign (mf_cache_shift_decl_l, mf_cache_shift_decl); gimple_seq_add_stmt (&seq, g); g = gimple_build_assign (mf_cache_mask_decl_l, mf_cache_mask_decl); gimple_seq_add_stmt (&seq, g); } /* Insert the check code in the THEN block. */ gsi = gsi_start_bb (then_bb); gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING); *instr_gsi = gsi_start_bb (join_bb); }
static void emit_case_bit_tests (gswitch *swtch, tree index_expr, tree minval, tree range, tree maxval) { struct case_bit_test test[MAX_CASE_BIT_TESTS]; unsigned int i, j, k; unsigned int count; basic_block switch_bb = gimple_bb (swtch); basic_block default_bb, new_default_bb, new_bb; edge default_edge; bool update_dom = dom_info_available_p (CDI_DOMINATORS); vec<basic_block> bbs_to_fix_dom = vNULL; tree index_type = TREE_TYPE (index_expr); tree unsigned_index_type = unsigned_type_for (index_type); unsigned int branch_num = gimple_switch_num_labels (swtch); gimple_stmt_iterator gsi; gassign *shift_stmt; tree idx, tmp, csui; tree word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); tree word_mode_zero = fold_convert (word_type_node, integer_zero_node); tree word_mode_one = fold_convert (word_type_node, integer_one_node); int prec = TYPE_PRECISION (word_type_node); wide_int wone = wi::one (prec); memset (&test, 0, sizeof (test)); /* Get the edge for the default case. */ tmp = gimple_switch_default_label (swtch); default_bb = label_to_block (CASE_LABEL (tmp)); default_edge = find_edge (switch_bb, default_bb); /* Go through all case labels, and collect the case labels, profile counts, and other information we need to build the branch tests. */ count = 0; for (i = 1; i < branch_num; i++) { unsigned int lo, hi; tree cs = gimple_switch_label (swtch, i); tree label = CASE_LABEL (cs); edge e = find_edge (switch_bb, label_to_block (label)); for (k = 0; k < count; k++) if (e == test[k].target_edge) break; if (k == count) { gcc_checking_assert (count < MAX_CASE_BIT_TESTS); test[k].mask = wi::zero (prec); test[k].target_edge = e; test[k].label = label; test[k].bits = 1; count++; } else test[k].bits++; lo = tree_to_uhwi (int_const_binop (MINUS_EXPR, CASE_LOW (cs), minval)); if (CASE_HIGH (cs) == NULL_TREE) hi = lo; else hi = tree_to_uhwi (int_const_binop (MINUS_EXPR, CASE_HIGH (cs), minval)); for (j = lo; j <= hi; j++) test[k].mask |= wi::lshift (wone, j); } qsort (test, count, sizeof (*test), case_bit_test_cmp); /* If all values are in the 0 .. BITS_PER_WORD-1 range, we can get rid of the minval subtractions, but it might make the mask constants more expensive. So, compare the costs. */ if (compare_tree_int (minval, 0) > 0 && compare_tree_int (maxval, GET_MODE_BITSIZE (word_mode)) < 0) { int cost_diff; HOST_WIDE_INT m = tree_to_uhwi (minval); rtx reg = gen_raw_REG (word_mode, 10000); bool speed_p = optimize_bb_for_speed_p (gimple_bb (swtch)); cost_diff = set_rtx_cost (gen_rtx_PLUS (word_mode, reg, GEN_INT (-m)), speed_p); for (i = 0; i < count; i++) { rtx r = immed_wide_int_const (test[i].mask, word_mode); cost_diff += set_src_cost (gen_rtx_AND (word_mode, reg, r), word_mode, speed_p); r = immed_wide_int_const (wi::lshift (test[i].mask, m), word_mode); cost_diff -= set_src_cost (gen_rtx_AND (word_mode, reg, r), word_mode, speed_p); } if (cost_diff > 0) { for (i = 0; i < count; i++) test[i].mask = wi::lshift (test[i].mask, m); minval = build_zero_cst (TREE_TYPE (minval)); range = maxval; } } /* We generate two jumps to the default case label. Split the default edge, so that we don't have to do any PHI node updating. */ new_default_bb = split_edge (default_edge); if (update_dom) { bbs_to_fix_dom.create (10); bbs_to_fix_dom.quick_push (switch_bb); bbs_to_fix_dom.quick_push (default_bb); bbs_to_fix_dom.quick_push (new_default_bb); } /* Now build the test-and-branch code. */ gsi = gsi_last_bb (switch_bb); /* idx = (unsigned)x - minval. */ idx = fold_convert (unsigned_index_type, index_expr); idx = fold_build2 (MINUS_EXPR, unsigned_index_type, idx, fold_convert (unsigned_index_type, minval)); idx = force_gimple_operand_gsi (&gsi, idx, /*simple=*/true, NULL_TREE, /*before=*/true, GSI_SAME_STMT); /* if (idx > range) goto default */ range = force_gimple_operand_gsi (&gsi, fold_convert (unsigned_index_type, range), /*simple=*/true, NULL_TREE, /*before=*/true, GSI_SAME_STMT); tmp = fold_build2 (GT_EXPR, boolean_type_node, idx, range); new_bb = hoist_edge_and_branch_if_true (&gsi, tmp, default_edge, update_dom); if (update_dom) bbs_to_fix_dom.quick_push (new_bb); gcc_assert (gimple_bb (swtch) == new_bb); gsi = gsi_last_bb (new_bb); /* Any blocks dominated by the GIMPLE_SWITCH, but that are not successors of NEW_BB, are still immediately dominated by SWITCH_BB. Make it so. */ if (update_dom) { vec<basic_block> dom_bbs; basic_block dom_son; dom_bbs = get_dominated_by (CDI_DOMINATORS, new_bb); FOR_EACH_VEC_ELT (dom_bbs, i, dom_son) { edge e = find_edge (new_bb, dom_son); if (e && single_pred_p (e->dest)) continue; set_immediate_dominator (CDI_DOMINATORS, dom_son, switch_bb); bbs_to_fix_dom.safe_push (dom_son); } dom_bbs.release (); }
static void emit_case_bit_tests (gimple swtch, tree index_expr, tree minval, tree range) { struct case_bit_test test[MAX_CASE_BIT_TESTS]; unsigned int i, j, k; unsigned int count; basic_block switch_bb = gimple_bb (swtch); basic_block default_bb, new_default_bb, new_bb; edge default_edge; bool update_dom = dom_info_available_p (CDI_DOMINATORS); vec<basic_block> bbs_to_fix_dom = vNULL; tree index_type = TREE_TYPE (index_expr); tree unsigned_index_type = unsigned_type_for (index_type); unsigned int branch_num = gimple_switch_num_labels (swtch); gimple_stmt_iterator gsi; gimple shift_stmt; tree idx, tmp, csui; tree word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); tree word_mode_zero = fold_convert (word_type_node, integer_zero_node); tree word_mode_one = fold_convert (word_type_node, integer_one_node); memset (&test, 0, sizeof (test)); /* Get the edge for the default case. */ tmp = gimple_switch_default_label (swtch); default_bb = label_to_block (CASE_LABEL (tmp)); default_edge = find_edge (switch_bb, default_bb); /* Go through all case labels, and collect the case labels, profile counts, and other information we need to build the branch tests. */ count = 0; for (i = 1; i < branch_num; i++) { unsigned int lo, hi; tree cs = gimple_switch_label (swtch, i); tree label = CASE_LABEL (cs); edge e = find_edge (switch_bb, label_to_block (label)); for (k = 0; k < count; k++) if (e == test[k].target_edge) break; if (k == count) { gcc_checking_assert (count < MAX_CASE_BIT_TESTS); test[k].hi = 0; test[k].lo = 0; test[k].target_edge = e; test[k].label = label; test[k].bits = 1; count++; } else test[k].bits++; lo = tree_to_uhwi (int_const_binop (MINUS_EXPR, CASE_LOW (cs), minval)); if (CASE_HIGH (cs) == NULL_TREE) hi = lo; else hi = tree_to_uhwi (int_const_binop (MINUS_EXPR, CASE_HIGH (cs), minval)); for (j = lo; j <= hi; j++) if (j >= HOST_BITS_PER_WIDE_INT) test[k].hi |= (HOST_WIDE_INT) 1 << (j - HOST_BITS_PER_INT); else test[k].lo |= (HOST_WIDE_INT) 1 << j; } qsort (test, count, sizeof (*test), case_bit_test_cmp); /* We generate two jumps to the default case label. Split the default edge, so that we don't have to do any PHI node updating. */ new_default_bb = split_edge (default_edge); if (update_dom) { bbs_to_fix_dom.create (10); bbs_to_fix_dom.quick_push (switch_bb); bbs_to_fix_dom.quick_push (default_bb); bbs_to_fix_dom.quick_push (new_default_bb); } /* Now build the test-and-branch code. */ gsi = gsi_last_bb (switch_bb); /* idx = (unsigned)x - minval. */ idx = fold_convert (unsigned_index_type, index_expr); idx = fold_build2 (MINUS_EXPR, unsigned_index_type, idx, fold_convert (unsigned_index_type, minval)); idx = force_gimple_operand_gsi (&gsi, idx, /*simple=*/true, NULL_TREE, /*before=*/true, GSI_SAME_STMT); /* if (idx > range) goto default */ range = force_gimple_operand_gsi (&gsi, fold_convert (unsigned_index_type, range), /*simple=*/true, NULL_TREE, /*before=*/true, GSI_SAME_STMT); tmp = fold_build2 (GT_EXPR, boolean_type_node, idx, range); new_bb = hoist_edge_and_branch_if_true (&gsi, tmp, default_edge, update_dom); if (update_dom) bbs_to_fix_dom.quick_push (new_bb); gcc_assert (gimple_bb (swtch) == new_bb); gsi = gsi_last_bb (new_bb); /* Any blocks dominated by the GIMPLE_SWITCH, but that are not successors of NEW_BB, are still immediately dominated by SWITCH_BB. Make it so. */ if (update_dom) { vec<basic_block> dom_bbs; basic_block dom_son; dom_bbs = get_dominated_by (CDI_DOMINATORS, new_bb); FOR_EACH_VEC_ELT (dom_bbs, i, dom_son) { edge e = find_edge (new_bb, dom_son); if (e && single_pred_p (e->dest)) continue; set_immediate_dominator (CDI_DOMINATORS, dom_son, switch_bb); bbs_to_fix_dom.safe_push (dom_son); } dom_bbs.release (); }
static bool generate_builtin (struct loop *loop, bitmap partition, bool copy_p) { bool res = false; unsigned i, x = 0; basic_block *bbs; gimple write = NULL; tree op0, op1; gimple_stmt_iterator bsi; tree nb_iter = number_of_exit_cond_executions (loop); if (!nb_iter || nb_iter == chrec_dont_know) return false; bbs = get_loop_body_in_dom_order (loop); for (i = 0; i < loop->num_nodes; i++) { basic_block bb = bbs[i]; for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi)) x++; for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi)) { gimple stmt = gsi_stmt (bsi); if (bitmap_bit_p (partition, x++) && is_gimple_assign (stmt) && !is_gimple_reg (gimple_assign_lhs (stmt))) { /* Don't generate the builtins when there are more than one memory write. */ if (write != NULL) goto end; write = stmt; } } } if (!write) goto end; op0 = gimple_assign_lhs (write); op1 = gimple_assign_rhs1 (write); if (!(TREE_CODE (op0) == ARRAY_REF || TREE_CODE (op0) == INDIRECT_REF)) goto end; /* The new statements will be placed before LOOP. */ bsi = gsi_last_bb (loop_preheader_edge (loop)->src); if (gimple_assign_rhs_code (write) == INTEGER_CST && (integer_zerop (op1) || real_zerop (op1))) res = generate_memset_zero (write, op0, nb_iter, bsi); /* If this is the last partition for which we generate code, we have to destroy the loop. */ if (res && !copy_p) { unsigned nbbs = loop->num_nodes; basic_block src = loop_preheader_edge (loop)->src; basic_block dest = single_exit (loop)->dest; prop_phis (dest); make_edge (src, dest, EDGE_FALLTHRU); cancel_loop_tree (loop); for (i = 0; i < nbbs; i++) delete_basic_block (bbs[i]); set_immediate_dominator (CDI_DOMINATORS, dest, recompute_dominator (CDI_DOMINATORS, dest)); } end: free (bbs); return res; }
void ubsan_expand_null_ifn (gimple_stmt_iterator gsi) { gimple stmt = gsi_stmt (gsi); location_t loc = gimple_location (stmt); gcc_assert (gimple_call_num_args (stmt) == 2); tree ptr = gimple_call_arg (stmt, 0); tree ckind = gimple_call_arg (stmt, 1); basic_block cur_bb = gsi_bb (gsi); /* Split the original block holding the pointer dereference. */ edge e = split_block (cur_bb, stmt); /* Get a hold on the 'condition block', the 'then block' and the 'else block'. */ basic_block cond_bb = e->src; basic_block fallthru_bb = e->dest; basic_block then_bb = create_empty_bb (cond_bb); add_bb_to_loop (then_bb, cond_bb->loop_father); loops_state_set (LOOPS_NEED_FIXUP); /* Make an edge coming from the 'cond block' into the 'then block'; this edge is unlikely taken, so set up the probability accordingly. */ e = make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE); e->probability = PROB_VERY_UNLIKELY; /* Connect 'then block' with the 'else block'. This is needed as the ubsan routines we call in the 'then block' are not noreturn. The 'then block' only has one outcoming edge. */ make_single_succ_edge (then_bb, fallthru_bb, EDGE_FALLTHRU); /* Set up the fallthrough basic block. */ e = find_edge (cond_bb, fallthru_bb); e->flags = EDGE_FALSE_VALUE; e->count = cond_bb->count; e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY; /* Update dominance info for the newly created then_bb; note that fallthru_bb's dominance info has already been updated by split_bock. */ if (dom_info_available_p (CDI_DOMINATORS)) set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); /* Put the ubsan builtin call into the newly created BB. */ gimple g; if (flag_sanitize_undefined_trap_on_error) g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0); else { enum built_in_function bcode = flag_sanitize_recover ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; tree fn = builtin_decl_implicit (bcode); const struct ubsan_mismatch_data m = { build_zero_cst (pointer_sized_int_node), ckind }; tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m, ubsan_type_descriptor (TREE_TYPE (ptr), UBSAN_PRINT_POINTER), NULL_TREE); data = build_fold_addr_expr_loc (loc, data); g = gimple_build_call (fn, 2, data, build_zero_cst (pointer_sized_int_node)); } gimple_set_location (g, loc); gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb); gsi_insert_after (&gsi2, g, GSI_NEW_STMT); /* Unlink the UBSAN_NULLs vops before replacing it. */ unlink_stmt_vdef (stmt); g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0), NULL_TREE, NULL_TREE); gimple_set_location (g, loc); /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */ gsi_replace (&gsi, g, false); }
static void shrink_wrap_one_built_in_call_with_conds (gcall *bi_call, vec <gimple *> conds, unsigned int nconds) { gimple_stmt_iterator bi_call_bsi; basic_block bi_call_bb, join_tgt_bb, guard_bb; edge join_tgt_in_edge_from_call, join_tgt_in_edge_fall_thru; edge bi_call_in_edge0, guard_bb_in_edge; unsigned tn_cond_stmts; unsigned ci; gimple *cond_expr = NULL; gimple *cond_expr_start; /* The cfg we want to create looks like this: [guard n-1] <- guard_bb (old block) | \ | [guard n-2] } | / \ } | / ... } new blocks | / [guard 0] } | / / | } [ call ] | <- bi_call_bb } | \ | | \ | | [ join ] <- join_tgt_bb (old iff call must end bb) | possible EH edges (only if [join] is old) When [join] is new, the immediate dominators for these blocks are: 1. [guard n-1]: unchanged 2. [call]: [guard n-1] 3. [guard m]: [guard m+1] for 0 <= m <= n-2 4. [join]: [guard n-1] We punt for the more complex case case of [join] being old and simply free the dominance info. We also punt on postdominators, which aren't expected to be available at this point anyway. */ bi_call_bb = gimple_bb (bi_call); /* Now find the join target bb -- split bi_call_bb if needed. */ if (stmt_ends_bb_p (bi_call)) { /* We checked that there was a fallthrough edge in can_guard_call_p. */ join_tgt_in_edge_from_call = find_fallthru_edge (bi_call_bb->succs); gcc_assert (join_tgt_in_edge_from_call); /* We don't want to handle PHIs. */ if (EDGE_COUNT (join_tgt_in_edge_from_call->dest->preds) > 1) join_tgt_bb = split_edge (join_tgt_in_edge_from_call); else { join_tgt_bb = join_tgt_in_edge_from_call->dest; /* We may have degenerate PHIs in the destination. Propagate those out. */ for (gphi_iterator i = gsi_start_phis (join_tgt_bb); !gsi_end_p (i);) { gphi *phi = i.phi (); replace_uses_by (gimple_phi_result (phi), gimple_phi_arg_def (phi, 0)); remove_phi_node (&i, true); } } } else { join_tgt_in_edge_from_call = split_block (bi_call_bb, bi_call); join_tgt_bb = join_tgt_in_edge_from_call->dest; } bi_call_bsi = gsi_for_stmt (bi_call); /* Now it is time to insert the first conditional expression into bi_call_bb and split this bb so that bi_call is shrink-wrapped. */ tn_cond_stmts = conds.length (); cond_expr = NULL; cond_expr_start = conds[0]; for (ci = 0; ci < tn_cond_stmts; ci++) { gimple *c = conds[ci]; gcc_assert (c || ci != 0); if (!c) break; gsi_insert_before (&bi_call_bsi, c, GSI_SAME_STMT); cond_expr = c; } ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); typedef std::pair<edge, edge> edge_pair; auto_vec<edge_pair, 8> edges; bi_call_in_edge0 = split_block (bi_call_bb, cond_expr); bi_call_in_edge0->flags &= ~EDGE_FALLTHRU; bi_call_in_edge0->flags |= EDGE_FALSE_VALUE; guard_bb = bi_call_bb; bi_call_bb = bi_call_in_edge0->dest; join_tgt_in_edge_fall_thru = make_edge (guard_bb, join_tgt_bb, EDGE_TRUE_VALUE); edges.reserve (nconds); edges.quick_push (edge_pair (bi_call_in_edge0, join_tgt_in_edge_fall_thru)); /* Code generation for the rest of the conditions */ for (unsigned int i = 1; i < nconds; ++i) { unsigned ci0; edge bi_call_in_edge; gimple_stmt_iterator guard_bsi = gsi_for_stmt (cond_expr_start); ci0 = ci; cond_expr_start = conds[ci0]; for (; ci < tn_cond_stmts; ci++) { gimple *c = conds[ci]; gcc_assert (c || ci != ci0); if (!c) break; gsi_insert_before (&guard_bsi, c, GSI_SAME_STMT); cond_expr = c; } ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); guard_bb_in_edge = split_block (guard_bb, cond_expr); guard_bb_in_edge->flags &= ~EDGE_FALLTHRU; guard_bb_in_edge->flags |= EDGE_TRUE_VALUE; bi_call_in_edge = make_edge (guard_bb, bi_call_bb, EDGE_FALSE_VALUE); edges.quick_push (edge_pair (bi_call_in_edge, guard_bb_in_edge)); } /* Now update the probability and profile information, processing the guards in order of execution. There are two approaches we could take here. On the one hand we could assign a probability of X to the call block and distribute that probability among its incoming edges. On the other hand we could assign a probability of X to each individual call edge. The choice only affects calls that have more than one condition. In those cases, the second approach would give the call block a greater probability than the first. However, the difference is only small, and our chosen X is a pure guess anyway. Here we take the second approach because it's slightly simpler and because it's easy to see that it doesn't lose profile counts. */ bi_call_bb->count = profile_count::zero (); while (!edges.is_empty ()) { edge_pair e = edges.pop (); edge call_edge = e.first; edge nocall_edge = e.second; basic_block src_bb = call_edge->src; gcc_assert (src_bb == nocall_edge->src); call_edge->probability = profile_probability::very_unlikely (); nocall_edge->probability = profile_probability::always () - call_edge->probability; bi_call_bb->count += call_edge->count (); if (nocall_edge->dest != join_tgt_bb) nocall_edge->dest->count = src_bb->count - bi_call_bb->count; } if (dom_info_available_p (CDI_DOMINATORS)) { /* The split_blocks leave [guard 0] as the immediate dominator of [call] and [call] as the immediate dominator of [join]. Fix them up. */ set_immediate_dominator (CDI_DOMINATORS, bi_call_bb, guard_bb); set_immediate_dominator (CDI_DOMINATORS, join_tgt_bb, guard_bb); } if (dump_file && (dump_flags & TDF_DETAILS)) { location_t loc; loc = gimple_location (bi_call); fprintf (dump_file, "%s:%d: note: function call is shrink-wrapped" " into error conditions.\n", LOCATION_FILE (loc), LOCATION_LINE (loc)); } }
static void mf_build_check_statement_for (tree base, tree limit, block_stmt_iterator *instr_bsi, location_t *locus, tree dirflag) { tree_stmt_iterator head, tsi; block_stmt_iterator bsi; basic_block cond_bb, then_bb, join_bb; edge e; tree cond, t, u, v; tree mf_base; tree mf_elem; tree mf_limit; /* We first need to split the current basic block, and start altering the CFG. This allows us to insert the statements we're about to construct into the right basic blocks. */ cond_bb = bb_for_stmt (bsi_stmt (*instr_bsi)); bsi = *instr_bsi; bsi_prev (&bsi); if (! bsi_end_p (bsi)) e = split_block (cond_bb, bsi_stmt (bsi)); else e = split_block_after_labels (cond_bb); cond_bb = e->src; join_bb = e->dest; /* A recap at this point: join_bb is the basic block at whose head is the gimple statement for which this check expression is being built. cond_bb is the (possibly new, synthetic) basic block the end of which will contain the cache-lookup code, and a conditional that jumps to the cache-miss code or, much more likely, over to join_bb. */ /* Create the bb that contains the cache-miss fallback block (mf_check). */ then_bb = create_empty_bb (cond_bb); make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE); make_single_succ_edge (then_bb, join_bb, EDGE_FALLTHRU); /* Mark the pseudo-fallthrough edge from cond_bb to join_bb. */ e = find_edge (cond_bb, join_bb); e->flags = EDGE_FALSE_VALUE; e->count = cond_bb->count; e->probability = REG_BR_PROB_BASE; /* Update dominance info. Note that bb_join's data was updated by split_block. */ if (dom_info_available_p (CDI_DOMINATORS)) { set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); set_immediate_dominator (CDI_DOMINATORS, join_bb, cond_bb); } /* Build our local variables. */ mf_elem = create_tmp_var (mf_cache_structptr_type, "__mf_elem"); mf_base = create_tmp_var (mf_uintptr_type, "__mf_base"); mf_limit = create_tmp_var (mf_uintptr_type, "__mf_limit"); /* Build: __mf_base = (uintptr_t) <base address expression>. */ t = build2 (MODIFY_EXPR, void_type_node, mf_base, convert (mf_uintptr_type, unshare_expr (base))); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); head = tsi_start (t); tsi = tsi_last (t); /* Build: __mf_limit = (uintptr_t) <limit address expression>. */ t = build2 (MODIFY_EXPR, void_type_node, mf_limit, convert (mf_uintptr_type, unshare_expr (limit))); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Build: __mf_elem = &__mf_lookup_cache [(__mf_base >> __mf_shift) & __mf_mask]. */ t = build2 (RSHIFT_EXPR, mf_uintptr_type, mf_base, (flag_mudflap_threads ? mf_cache_shift_decl : mf_cache_shift_decl_l)); t = build2 (BIT_AND_EXPR, mf_uintptr_type, t, (flag_mudflap_threads ? mf_cache_mask_decl : mf_cache_mask_decl_l)); t = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (mf_cache_array_decl)), mf_cache_array_decl, t, NULL_TREE, NULL_TREE); t = build1 (ADDR_EXPR, mf_cache_structptr_type, t); t = build2 (MODIFY_EXPR, void_type_node, mf_elem, t); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Quick validity check. if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) { __mf_check (); ... and only if single-threaded: __mf_lookup_shift_1 = f...; __mf_lookup_mask_l = ...; } It is expected that this body of code is rarely executed so we mark the edge to the THEN clause of the conditional jump as unlikely. */ /* Construct t <-- '__mf_elem->low > __mf_base'. */ t = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TYPE_FIELDS (mf_cache_struct_type), NULL_TREE); t = build2 (GT_EXPR, boolean_type_node, t, mf_base); /* Construct '__mf_elem->high < __mf_limit'. First build: 1) u <-- '__mf_elem->high' 2) v <-- '__mf_limit'. Then build 'u <-- (u < v). */ u = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TREE_CHAIN (TYPE_FIELDS (mf_cache_struct_type)), NULL_TREE); v = mf_limit; u = build2 (LT_EXPR, boolean_type_node, u, v); /* Build the composed conditional: t <-- 't || u'. Then store the result of the evaluation of 't' in a temporary variable which we can use as the condition for the conditional jump. */ t = build2 (TRUTH_OR_EXPR, boolean_type_node, t, u); cond = create_tmp_var (boolean_type_node, "__mf_unlikely_cond"); t = build2 (MODIFY_EXPR, boolean_type_node, cond, t); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Build the conditional jump. 'cond' is just a temporary so we can simply build a void COND_EXPR. We do need labels in both arms though. */ t = build3 (COND_EXPR, void_type_node, cond, build1 (GOTO_EXPR, void_type_node, tree_block_label (then_bb)), build1 (GOTO_EXPR, void_type_node, tree_block_label (join_bb))); SET_EXPR_LOCUS (t, locus); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* At this point, after so much hard work, we have only constructed the conditional jump, if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) The lowered GIMPLE tree representing this code is in the statement list starting at 'head'. We can insert this now in the current basic block, i.e. the one that the statement we're instrumenting was originally in. */ bsi = bsi_last (cond_bb); for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); /* Now build up the body of the cache-miss handling: __mf_check(); refresh *_l vars. This is the body of the conditional. */ u = tree_cons (NULL_TREE, mf_file_function_line_tree (locus == NULL ? UNKNOWN_LOCATION : *locus), NULL_TREE); u = tree_cons (NULL_TREE, dirflag, u); /* NB: we pass the overall [base..limit] range to mf_check. */ u = tree_cons (NULL_TREE, fold_build2 (PLUS_EXPR, integer_type_node, fold_build2 (MINUS_EXPR, mf_uintptr_type, mf_limit, mf_base), integer_one_node), u); u = tree_cons (NULL_TREE, mf_base, u); t = build_function_call_expr (mf_check_fndecl, u); gimplify_to_stmt_list (&t); head = tsi_start (t); tsi = tsi_last (t); if (! flag_mudflap_threads) { t = build2 (MODIFY_EXPR, void_type_node, mf_cache_shift_decl_l, mf_cache_shift_decl); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); t = build2 (MODIFY_EXPR, void_type_node, mf_cache_mask_decl_l, mf_cache_mask_decl); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); } /* Insert the check code in the THEN block. */ bsi = bsi_start (then_bb); for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); *instr_bsi = bsi_start (join_bb); bsi_next (instr_bsi); }
void copy_bbs (basic_block *bbs, unsigned n, basic_block *new_bbs, edge *edges, unsigned n_edges, edge *new_edges, struct loop *base) { unsigned i, j; basic_block bb, new_bb, dom_bb; edge e; /* Duplicate bbs, update dominators, assign bbs to loops. */ for (i = 0; i < n; i++) { /* Duplicate. */ bb = bbs[i]; new_bb = new_bbs[i] = cfg_layout_duplicate_bb (bb, NULL); bb->rbi->duplicated = 1; /* Add to loop. */ add_bb_to_loop (new_bb, bb->loop_father->copy); add_to_dominance_info (CDI_DOMINATORS, new_bb); /* Possibly set header. */ if (bb->loop_father->header == bb && bb->loop_father != base) new_bb->loop_father->header = new_bb; /* Or latch. */ if (bb->loop_father->latch == bb && bb->loop_father != base) new_bb->loop_father->latch = new_bb; } /* Set dominators. */ for (i = 0; i < n; i++) { bb = bbs[i]; new_bb = new_bbs[i]; dom_bb = get_immediate_dominator (CDI_DOMINATORS, bb); if (dom_bb->rbi->duplicated) { dom_bb = dom_bb->rbi->copy; set_immediate_dominator (CDI_DOMINATORS, new_bb, dom_bb); } } /* Redirect edges. */ for (j = 0; j < n_edges; j++) new_edges[j] = NULL; for (i = 0; i < n; i++) { new_bb = new_bbs[i]; bb = bbs[i]; for (e = new_bb->succ; e; e = e->succ_next) { for (j = 0; j < n_edges; j++) if (edges[j] && edges[j]->src == bb && edges[j]->dest == e->dest) new_edges[j] = e; if (!e->dest->rbi->duplicated) continue; redirect_edge_and_branch_force (e, e->dest->rbi->copy); } } /* Clear information about duplicates. */ for (i = 0; i < n; i++) bbs[i]->rbi->duplicated = 0; }
static void doloop_modify (struct loop *loop, struct niter_desc *desc, rtx doloop_seq, rtx condition, rtx count) { rtx counter_reg; rtx tmp, noloop = NULL_RTX; rtx sequence; rtx jump_insn; rtx jump_label; int nonneg = 0, irr; bool increment_count; basic_block loop_end = desc->out_edge->src; enum machine_mode mode; jump_insn = BB_END (loop_end); if (dump_file) { fprintf (dump_file, "Doloop: Inserting doloop pattern ("); if (desc->const_iter) fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, desc->niter); else fputs ("runtime", dump_file); fputs (" iterations).\n", dump_file); } /* Discard original jump to continue loop. The original compare result may still be live, so it cannot be discarded explicitly. */ delete_insn (jump_insn); counter_reg = XEXP (condition, 0); if (GET_CODE (counter_reg) == PLUS) counter_reg = XEXP (counter_reg, 0); mode = GET_MODE (counter_reg); increment_count = false; switch (GET_CODE (condition)) { case NE: /* Currently only NE tests against zero and one are supported. */ if (XEXP (condition, 1) == const1_rtx) { increment_count = true; noloop = const1_rtx; } else if (XEXP (condition, 1) == const0_rtx) noloop = const0_rtx; else abort (); break; case GE: /* Currently only GE tests against zero are supported. */ if (XEXP (condition, 1) != const0_rtx) abort (); noloop = constm1_rtx; /* The iteration count does not need incrementing for a GE test. */ increment_count = false; /* Determine if the iteration counter will be non-negative. Note that the maximum value loaded is iterations_max - 1. */ if (desc->niter_max <= ((unsigned HOST_WIDEST_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) nonneg = 1; break; /* Abort if an invalid doloop pattern has been generated. */ default: abort (); } if (increment_count) count = simplify_gen_binary (PLUS, mode, count, const1_rtx); /* Insert initialization of the count register into the loop header. */ start_sequence (); tmp = force_operand (count, counter_reg); convert_move (counter_reg, tmp, 1); sequence = get_insns (); end_sequence (); emit_insn_after (sequence, BB_END (loop_preheader_edge (loop)->src)); if (desc->noloop_assumptions) { rtx ass = copy_rtx (desc->noloop_assumptions); basic_block preheader = loop_preheader_edge (loop)->src; basic_block set_zero = loop_split_edge_with (loop_preheader_edge (loop), NULL_RTX); basic_block new_preheader = loop_split_edge_with (loop_preheader_edge (loop), NULL_RTX); basic_block bb; edge te; gcov_type cnt; /* Expand the condition testing the assumptions and if it does not pass, reset the count register to 0. */ add_test (XEXP (ass, 0), preheader, set_zero); EDGE_SUCC (preheader, 0)->flags &= ~EDGE_FALLTHRU; cnt = EDGE_SUCC (preheader, 0)->count; EDGE_SUCC (preheader, 0)->probability = 0; EDGE_SUCC (preheader, 0)->count = 0; irr = EDGE_SUCC (preheader, 0)->flags & EDGE_IRREDUCIBLE_LOOP; te = make_edge (preheader, new_preheader, EDGE_FALLTHRU | irr); te->probability = REG_BR_PROB_BASE; te->count = cnt; set_immediate_dominator (CDI_DOMINATORS, new_preheader, preheader); set_zero->count = 0; set_zero->frequency = 0; for (ass = XEXP (ass, 1); ass; ass = XEXP (ass, 1)) { bb = loop_split_edge_with (te, NULL_RTX); te = EDGE_SUCC (bb, 0); add_test (XEXP (ass, 0), bb, set_zero); make_edge (bb, set_zero, irr); } start_sequence (); convert_move (counter_reg, noloop, 0); sequence = get_insns (); end_sequence (); emit_insn_after (sequence, BB_END (set_zero)); } /* Some targets (eg, C4x) need to initialize special looping registers. */ #ifdef HAVE_doloop_begin { rtx init; unsigned level = get_loop_level (loop) + 1; init = gen_doloop_begin (counter_reg, desc->const_iter ? desc->niter_expr : const0_rtx, desc->niter_max, GEN_INT (level)); if (init) { start_sequence (); emit_insn (init); sequence = get_insns (); end_sequence (); emit_insn_after (sequence, BB_END (loop_preheader_edge (loop)->src)); } } #endif /* Insert the new low-overhead looping insn. */ emit_jump_insn_after (doloop_seq, BB_END (loop_end)); jump_insn = BB_END (loop_end); jump_label = block_label (desc->in_edge->dest); JUMP_LABEL (jump_insn) = jump_label; LABEL_NUSES (jump_label)++; /* Ensure the right fallthru edge is marked, for case we have reversed the condition. */ desc->in_edge->flags &= ~EDGE_FALLTHRU; desc->out_edge->flags |= EDGE_FALLTHRU; /* Add a REG_NONNEG note if the actual or estimated maximum number of iterations is non-negative. */ if (nonneg) { REG_NOTES (jump_insn) = gen_rtx_EXPR_LIST (REG_NONNEG, NULL_RTX, REG_NOTES (jump_insn)); } }
static bool generate_builtin (struct loop *loop, bitmap partition, bool copy_p) { bool res = false; unsigned i, x = 0; basic_block *bbs; gimple write = NULL; gimple_stmt_iterator bsi; tree nb_iter = number_of_exit_cond_executions (loop); if (!nb_iter || nb_iter == chrec_dont_know) return false; bbs = get_loop_body_in_dom_order (loop); for (i = 0; i < loop->num_nodes; i++) { basic_block bb = bbs[i]; for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi)) x++; for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi)) { gimple stmt = gsi_stmt (bsi); if (gimple_code (stmt) == GIMPLE_LABEL || is_gimple_debug (stmt)) continue; if (!bitmap_bit_p (partition, x++)) continue; /* If the stmt has uses outside of the loop fail. */ if (stmt_has_scalar_dependences_outside_loop (stmt)) goto end; if (is_gimple_assign (stmt) && !is_gimple_reg (gimple_assign_lhs (stmt))) { /* Don't generate the builtins when there are more than one memory write. */ if (write != NULL) goto end; write = stmt; if (bb == loop->latch) nb_iter = number_of_latch_executions (loop); } } } if (!stmt_with_adjacent_zero_store_dr_p (write)) goto end; /* The new statements will be placed before LOOP. */ bsi = gsi_last_bb (loop_preheader_edge (loop)->src); generate_memset_zero (write, gimple_assign_lhs (write), nb_iter, bsi); res = true; /* If this is the last partition for which we generate code, we have to destroy the loop. */ if (!copy_p) { unsigned nbbs = loop->num_nodes; edge exit = single_exit (loop); basic_block src = loop_preheader_edge (loop)->src, dest = exit->dest; redirect_edge_pred (exit, src); exit->flags &= ~(EDGE_TRUE_VALUE|EDGE_FALSE_VALUE); exit->flags |= EDGE_FALLTHRU; cancel_loop_tree (loop); rescan_loop_exit (exit, false, true); for (i = 0; i < nbbs; i++) delete_basic_block (bbs[i]); set_immediate_dominator (CDI_DOMINATORS, dest, recompute_dominator (CDI_DOMINATORS, dest)); } end: free (bbs); return res; }