static basic_block find_block_to_duplicate_for_splitting_paths (basic_block latch) { /* We should have simple latches at this point. So the latch should have a single successor. This implies the predecessor of the latch likely has the loop exit. And it's that predecessor we're most interested in. To keep things simple, we're going to require that the latch have a single predecessor too. */ if (single_succ_p (latch) && single_pred_p (latch)) { basic_block bb = get_immediate_dominator (CDI_DOMINATORS, latch); gcc_assert (single_pred_edge (latch)->src == bb); /* If BB has been marked as not to be duplicated, then honor that request. */ if (ignore_bb_p (bb)) return NULL; gimple *last = gsi_stmt (gsi_last_nondebug_bb (bb)); /* The immediate dominator of the latch must end in a conditional. */ if (!last || gimple_code (last) != GIMPLE_COND) return NULL; /* We're hoping that BB is a join point for an IF-THEN-ELSE diamond region. Verify that it is. First, verify that BB has two predecessors (each arm of the IF-THEN-ELSE) and two successors (the latch and exit). */ if (EDGE_COUNT (bb->preds) == 2 && EDGE_COUNT (bb->succs) == 2) { /* Now verify that BB's immediate dominator ends in a conditional as well. */ basic_block bb_idom = get_immediate_dominator (CDI_DOMINATORS, bb); gimple *last = gsi_stmt (gsi_last_nondebug_bb (bb_idom)); if (!last || gimple_code (last) != GIMPLE_COND) return NULL; /* And that BB's immediate dominator's successors are the predecessors of BB. */ if (!find_edge (bb_idom, EDGE_PRED (bb, 0)->src) || !find_edge (bb_idom, EDGE_PRED (bb, 1)->src)) return NULL; /* And that the predecessors of BB each have a single successor. */ if (!single_succ_p (EDGE_PRED (bb, 0)->src) || !single_succ_p (EDGE_PRED (bb, 1)->src)) return NULL; /* So at this point we have a simple diamond for an IF-THEN-ELSE construct starting at BB_IDOM, with a join point at BB. BB pass control outside the loop or to the loop latch. We're going to want to create two duplicates of BB, one for each successor of BB_IDOM. */ return bb; } } return NULL; }
static void cfg_blocks_add (basic_block bb) { /* APPLE LOCAL begin mainline */ bool head = false; gcc_assert (bb != ENTRY_BLOCK_PTR && bb != EXIT_BLOCK_PTR); gcc_assert (!TEST_BIT (bb_in_list, bb->index)); if (cfg_blocks_empty_p ()) { cfg_blocks_tail = cfg_blocks_head = 0; cfg_blocks_num = 1; } else { cfg_blocks_num++; if (cfg_blocks_num > VEC_length (basic_block, cfg_blocks)) { /* We have to grow the array now. Adjust to queue to occupy the full space of the original array. We do not need to initialize the newly allocated portion of the array because we keep track of CFG_BLOCKS_HEAD and CFG_BLOCKS_HEAD. */ cfg_blocks_tail = VEC_length (basic_block, cfg_blocks); cfg_blocks_head = 0; VEC_safe_grow (basic_block, heap, cfg_blocks, 2 * cfg_blocks_tail); } /* Minor optimization: we prefer to see blocks with more predecessors later, because there is more of a chance that the incoming edges will be executable. */ else if (EDGE_COUNT (bb->preds) >= EDGE_COUNT (VEC_index (basic_block, cfg_blocks, cfg_blocks_head)->preds)) cfg_blocks_tail = ((cfg_blocks_tail + 1) % VEC_length (basic_block, cfg_blocks)); else { if (cfg_blocks_head == 0) cfg_blocks_head = VEC_length (basic_block, cfg_blocks); --cfg_blocks_head; head = true; } } VEC_replace (basic_block, cfg_blocks, head ? cfg_blocks_head : cfg_blocks_tail, bb); /* APPLE LOCAL end mainline */ SET_BIT (bb_in_list, bb->index); }
/* Check the consistency of profile information. We can't do that in verify_flow_info, as the counts may get invalid for incompletely solved graphs, later eliminating of conditionals or roundoff errors. It is still practical to have them reported for debugging of simple testcases. */ static void check_bb_profile (basic_block bb, FILE * file, int indent, int flags) { edge e; int sum = 0; gcov_type lsum; edge_iterator ei; struct function *fun = DECL_STRUCT_FUNCTION (current_function_decl); char *s_indent = (char *) alloca ((size_t) indent + 1); memset ((void *) s_indent, ' ', (size_t) indent); s_indent[indent] = '\0'; if (profile_status_for_function (fun) == PROFILE_ABSENT) return; if (bb != EXIT_BLOCK_PTR_FOR_FUNCTION (fun)) { FOR_EACH_EDGE (e, ei, bb->succs) sum += e->probability; if (EDGE_COUNT (bb->succs) && abs (sum - REG_BR_PROB_BASE) > 100) fprintf (file, "%s%sInvalid sum of outgoing probabilities %.1f%%\n", (flags & TDF_COMMENT) ? ";; " : "", s_indent, sum * 100.0 / REG_BR_PROB_BASE); lsum = 0; FOR_EACH_EDGE (e, ei, bb->succs) lsum += e->count; if (EDGE_COUNT (bb->succs) && (lsum - bb->count > 100 || lsum - bb->count < -100)) fprintf (file, "%s%sInvalid sum of outgoing counts %i, should be %i\n", (flags & TDF_COMMENT) ? ";; " : "", s_indent, (int) lsum, (int) bb->count); } if (bb != ENTRY_BLOCK_PTR_FOR_FUNCTION (fun)) { sum = 0; FOR_EACH_EDGE (e, ei, bb->preds) sum += EDGE_FREQUENCY (e); if (abs (sum - bb->frequency) > 100) fprintf (file, "%s%sInvalid sum of incoming frequencies %i, should be %i\n", (flags & TDF_COMMENT) ? ";; " : "", s_indent, sum, bb->frequency); lsum = 0; FOR_EACH_EDGE (e, ei, bb->preds) lsum += e->count; if (lsum - bb->count > 100 || lsum - bb->count < -100) fprintf (file, "%s%sInvalid sum of incoming counts %i, should be %i\n", (flags & TDF_COMMENT) ? ";; " : "", s_indent, (int) lsum, (int) bb->count); } }
static edge loop_edge_to_cancel (struct loop *loop) { vec<edge> exits; unsigned i; edge edge_to_cancel; gimple_stmt_iterator gsi; /* We want only one predecestor of the loop. */ if (EDGE_COUNT (loop->latch->preds) > 1) return NULL; exits = get_loop_exit_edges (loop); FOR_EACH_VEC_ELT (exits, i, edge_to_cancel) { /* Find the other edge than the loop exit leaving the conditoinal. */ if (EDGE_COUNT (edge_to_cancel->src->succs) != 2) continue; if (EDGE_SUCC (edge_to_cancel->src, 0) == edge_to_cancel) edge_to_cancel = EDGE_SUCC (edge_to_cancel->src, 1); else edge_to_cancel = EDGE_SUCC (edge_to_cancel->src, 0); /* We only can handle conditionals. */ if (!(edge_to_cancel->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))) continue; /* We should never have conditionals in the loop latch. */ gcc_assert (edge_to_cancel->dest != loop->header); /* Check that it leads to loop latch. */ if (edge_to_cancel->dest != loop->latch) continue; exits.release (); /* Verify that the code in loop latch does nothing that may end program execution without really reaching the exit. This may include non-pure/const function calls, EH statements, volatile ASMs etc. */ for (gsi = gsi_start_bb (loop->latch); !gsi_end_p (gsi); gsi_next (&gsi)) if (gimple_has_side_effects (gsi_stmt (gsi))) return NULL; return edge_to_cancel; } exits.release (); return NULL; }
static void naive_outof_ssa (void) { basic_block bb; hsa_cfun->m_in_ssa = false; FOR_ALL_BB_FN (bb, cfun) { hsa_bb *hbb = hsa_bb_for_bb (bb); hsa_insn_phi *phi; /* naive_process_phi can call split_edge on an incoming edge which order if the incoming edges to the basic block and thus make it inconsistent with the ordering of PHI arguments, so we collect them in advance. */ auto_vec<edge, 8> predecessors; unsigned pred_count = EDGE_COUNT (bb->preds); for (unsigned i = 0; i < pred_count; i++) predecessors.safe_push (EDGE_PRED (bb, i)); for (phi = hbb->m_first_phi; phi; phi = phi->m_next ? as_a <hsa_insn_phi *> (phi->m_next) : NULL) naive_process_phi (phi, predecessors); /* Zap PHI nodes, they will be deallocated when everything else will. */ hbb->m_first_phi = NULL; hbb->m_last_phi = NULL; }
void reserve_phi_args_for_new_edge (basic_block bb) { tree *loc; int len = EDGE_COUNT (bb->preds); int cap = ideal_phi_node_len (len + 4); for (loc = &(bb->phi_nodes); *loc; loc = &PHI_CHAIN (*loc)) { if (len > PHI_ARG_CAPACITY (*loc)) { tree old_phi = *loc; resize_phi_node (loc, cap); /* The result of the phi is defined by this phi node. */ SSA_NAME_DEF_STMT (PHI_RESULT (*loc)) = *loc; release_phi_node (old_phi); } /* We represent a "missing PHI argument" by placing NULL_TREE in the corresponding slot. If PHI arguments were added immediately after an edge is created, this zeroing would not be necessary, but unfortunately this is not the case. For example, the loop optimizer duplicates several basic blocks, redirects edges, and then fixes up PHI arguments later in batch. */ SET_PHI_ARG_DEF (*loc, len - 1, NULL_TREE); PHI_NUM_ARGS (*loc)++; } }
static bool recognize_if_then_else (basic_block cond_bb, basic_block *then_bb, basic_block *else_bb) { edge t, e; if (EDGE_COUNT (cond_bb->succs) != 2) return false; /* Find the then/else edges. */ t = EDGE_SUCC (cond_bb, 0); e = EDGE_SUCC (cond_bb, 1); if (!(t->flags & EDGE_TRUE_VALUE)) std::swap (t, e); if (!(t->flags & EDGE_TRUE_VALUE) || !(e->flags & EDGE_FALSE_VALUE)) return false; /* Check if the edge destinations point to the required block. */ if (*then_bb && t->dest != *then_bb) return false; if (*else_bb && e->dest != *else_bb) return false; if (!*then_bb) *then_bb = t->dest; if (!*else_bb) *else_bb = e->dest; return true; }
void reserve_phi_args_for_new_edge (basic_block bb) { size_t len = EDGE_COUNT (bb->preds); size_t cap = ideal_phi_node_len (len + 4); gimple_stmt_iterator gsi; for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple *loc = gsi_stmt_ptr (&gsi); if (len > gimple_phi_capacity (*loc)) { gimple old_phi = *loc; resize_phi_node (loc, cap); /* The result of the PHI is defined by this PHI node. */ SSA_NAME_DEF_STMT (gimple_phi_result (*loc)) = *loc; release_phi_node (old_phi); } /* We represent a "missing PHI argument" by placing NULL_TREE in the corresponding slot. If PHI arguments were added immediately after an edge is created, this zeroing would not be necessary, but unfortunately this is not the case. For example, the loop optimizer duplicates several basic blocks, redirects edges, and then fixes up PHI arguments later in batch. */ SET_PHI_ARG_DEF (*loc, len - 1, NULL_TREE); (*loc)->gimple_phi.nargs++; } }
static bool should_duplicate_loop_header_p (basic_block header, struct loop *loop, int *limit) { block_stmt_iterator bsi; tree last; /* Do not copy one block more than once (we do not really want to do loop peeling here). */ if (header->aux) return false; gcc_assert (EDGE_COUNT (header->succs) > 0); if (EDGE_COUNT (header->succs) == 1) return false; if (flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 0)->dest) && flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 1)->dest)) return false; /* If this is not the original loop header, we want it to have just one predecessor in order to match the && pattern. */ if (header != loop->header && EDGE_COUNT (header->preds) >= 2) return false; last = last_stmt (header); if (TREE_CODE (last) != COND_EXPR) return false; /* Approximately copy the conditions that used to be used in jump.c -- at most 20 insns and no calls. */ for (bsi = bsi_start (header); !bsi_end_p (bsi); bsi_next (&bsi)) { last = bsi_stmt (bsi); if (TREE_CODE (last) == LABEL_EXPR) continue; if (get_call_expr_in (last)) return false; *limit -= estimate_num_insns (last); if (*limit < 0) return false; } return true; }
static bool candidate_bb_for_phi_optimization (basic_block bb, basic_block *cond_block_p, basic_block *other_block_p) { tree last0, last1; basic_block cond_block, other_block; /* One of the alternatives must come from a block ending with a COND_EXPR. */ last0 = last_stmt (EDGE_PRED (bb, 0)->src); last1 = last_stmt (EDGE_PRED (bb, 1)->src); if (last0 && TREE_CODE (last0) == COND_EXPR) { cond_block = EDGE_PRED (bb, 0)->src; other_block = EDGE_PRED (bb, 1)->src; } else if (last1 && TREE_CODE (last1) == COND_EXPR) { other_block = EDGE_PRED (bb, 0)->src; cond_block = EDGE_PRED (bb, 1)->src; } else return false; /* COND_BLOCK must have precisely two successors. We indirectly verify that those successors are BB and OTHER_BLOCK. */ if (EDGE_COUNT (cond_block->succs) != 2 || (EDGE_SUCC (cond_block, 0)->flags & EDGE_ABNORMAL) != 0 || (EDGE_SUCC (cond_block, 1)->flags & EDGE_ABNORMAL) != 0) return false; /* OTHER_BLOCK must have a single predecessor which is COND_BLOCK, OTHER_BLOCK must have a single successor which is BB and OTHER_BLOCK must have no PHI nodes. */ if (EDGE_COUNT (other_block->preds) != 1 || EDGE_PRED (other_block, 0)->src != cond_block || EDGE_COUNT (other_block->succs) != 1 || EDGE_SUCC (other_block, 0)->dest != bb || phi_nodes (other_block)) return false; *cond_block_p = cond_block; *other_block_p = other_block; /* Everything looks OK. */ return true; }
static void cfg_blocks_add (basic_block bb) { bool head = false; gcc_assert (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun) && bb != EXIT_BLOCK_PTR_FOR_FN (cfun)); gcc_assert (!bitmap_bit_p (bb_in_list, bb->index)); if (cfg_blocks_empty_p ()) { cfg_blocks_tail = cfg_blocks_head = 0; cfg_blocks_num = 1; } else { cfg_blocks_num++; if (cfg_blocks_num > cfg_blocks.length ()) { /* We have to grow the array now. Adjust to queue to occupy the full space of the original array. We do not need to initialize the newly allocated portion of the array because we keep track of CFG_BLOCKS_HEAD and CFG_BLOCKS_HEAD. */ cfg_blocks_tail = cfg_blocks.length (); cfg_blocks_head = 0; cfg_blocks.safe_grow (2 * cfg_blocks_tail); } /* Minor optimization: we prefer to see blocks with more predecessors later, because there is more of a chance that the incoming edges will be executable. */ else if (EDGE_COUNT (bb->preds) >= EDGE_COUNT (cfg_blocks[cfg_blocks_head]->preds)) cfg_blocks_tail = ((cfg_blocks_tail + 1) % cfg_blocks.length ()); else { if (cfg_blocks_head == 0) cfg_blocks_head = cfg_blocks.length (); --cfg_blocks_head; head = true; } } cfg_blocks[head ? cfg_blocks_head : cfg_blocks_tail] = bb; bitmap_set_bit (bb_in_list, bb->index); }
gimple create_phi_node (tree var, basic_block bb) { gimple phi = make_phi_node (var, EDGE_COUNT (bb->preds)); add_phi_node_to_bb (phi, bb); return phi; }
static inline void connect_dest (edge e) { basic_block dest = e->dest; VEC_safe_push (edge, gc, dest->preds, e); e->dest_idx = EDGE_COUNT (dest->preds) - 1; df_mark_solutions_dirty (); }
static bool should_duplicate_loop_header_p (basic_block header, struct loop *loop, int *limit) { gimple_stmt_iterator bsi; gimple last; /* Do not copy one block more than once (we do not really want to do loop peeling here). */ if (header->aux) return false; /* Loop header copying usually increases size of the code. This used not to be true, since quite often it is possible to verify that the condition is satisfied in the first iteration and therefore to eliminate it. Jump threading handles these cases now. */ if (optimize_loop_for_size_p (loop)) return false; gcc_assert (EDGE_COUNT (header->succs) > 0); if (single_succ_p (header)) return false; if (flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 0)->dest) && flow_bb_inside_loop_p (loop, EDGE_SUCC (header, 1)->dest)) return false; /* If this is not the original loop header, we want it to have just one predecessor in order to match the && pattern. */ if (header != loop->header && !single_pred_p (header)) return false; last = last_stmt (header); if (gimple_code (last) != GIMPLE_COND) return false; /* Approximately copy the conditions that used to be used in jump.c -- at most 20 insns and no calls. */ for (bsi = gsi_start_bb (header); !gsi_end_p (bsi); gsi_next (&bsi)) { last = gsi_stmt (bsi); if (gimple_code (last) == GIMPLE_LABEL) continue; if (is_gimple_debug (last)) continue; if (is_gimple_call (last)) return false; *limit -= estimate_num_insns (last, &eni_size_weights); if (*limit < 0) return false; } return true; }
static void print_graphite_scop_statistics (FILE* file, scop_p scop) { long n_bbs = 0; long n_loops = 0; long n_stmts = 0; long n_conditions = 0; long n_p_bbs = 0; long n_p_loops = 0; long n_p_stmts = 0; long n_p_conditions = 0; basic_block bb; FOR_ALL_BB (bb) { gimple_stmt_iterator psi; loop_p loop = bb->loop_father; if (!bb_in_sese_p (bb, SCOP_REGION (scop))) continue; n_bbs++; n_p_bbs += bb->count; if (EDGE_COUNT (bb->succs) > 1) { n_conditions++; n_p_conditions += bb->count; } for (psi = gsi_start_bb (bb); !gsi_end_p (psi); gsi_next (&psi)) { n_stmts++; n_p_stmts += bb->count; } if (loop->header == bb && loop_in_sese_p (loop, SCOP_REGION (scop))) { n_loops++; n_p_loops += bb->count; } } fprintf (file, "\nSCoP statistics ("); fprintf (file, "BBS:%ld, ", n_bbs); fprintf (file, "LOOPS:%ld, ", n_loops); fprintf (file, "CONDITIONS:%ld, ", n_conditions); fprintf (file, "STMTS:%ld)\n", n_stmts); fprintf (file, "\nSCoP profiling statistics ("); fprintf (file, "BBS:%ld, ", n_p_bbs); fprintf (file, "LOOPS:%ld, ", n_p_loops); fprintf (file, "CONDITIONS:%ld, ", n_p_conditions); fprintf (file, "STMTS:%ld)\n", n_p_stmts); }
static bool gate_latent_entropy(void) { // don't bother with noreturn functions for now if (TREE_THIS_VOLATILE(current_function_decl)) return false; // gcc-4.5 doesn't discover some trivial noreturn functions if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0) return false; return lookup_attribute("latent_entropy", DECL_ATTRIBUTES(current_function_decl)) != NULL_TREE; }
static void print_global_statistics (FILE* file) { long n_bbs = 0; long n_loops = 0; long n_stmts = 0; long n_conditions = 0; long n_p_bbs = 0; long n_p_loops = 0; long n_p_stmts = 0; long n_p_conditions = 0; basic_block bb; FOR_ALL_BB (bb) { gimple_stmt_iterator psi; n_bbs++; n_p_bbs += bb->count; /* Ignore artificial surrounding loop. */ if (bb == bb->loop_father->header && bb->index != 0) { n_loops++; n_p_loops += bb->count; } if (EDGE_COUNT (bb->succs) > 1) { n_conditions++; n_p_conditions += bb->count; } for (psi = gsi_start_bb (bb); !gsi_end_p (psi); gsi_next (&psi)) { n_stmts++; n_p_stmts += bb->count; } } fprintf (file, "\nGlobal statistics ("); fprintf (file, "BBS:%ld, ", n_bbs); fprintf (file, "LOOPS:%ld, ", n_loops); fprintf (file, "CONDITIONS:%ld, ", n_conditions); fprintf (file, "STMTS:%ld)\n", n_stmts); fprintf (file, "\nGlobal profiling statistics ("); fprintf (file, "BBS:%ld, ", n_p_bbs); fprintf (file, "LOOPS:%ld, ", n_p_loops); fprintf (file, "CONDITIONS:%ld, ", n_p_conditions); fprintf (file, "STMTS:%ld)\n", n_p_stmts); }
static inline void disconnect_dest (edge e) { basic_block dest = e->dest; unsigned int dest_idx = e->dest_idx; VEC_unordered_remove (edge, dest->preds, dest_idx); /* If we removed an edge in the middle of the edge vector, we need to update dest_idx of the edge that moved into the "hole". */ if (dest_idx < EDGE_COUNT (dest->preds)) EDGE_PRED (dest, dest_idx)->dest_idx = dest_idx; df_mark_solutions_dirty (); }
static void split_pattern_seq (void) { rtx insn; basic_block bb; rtx retlabel, retjmp, saveinsn; int i; seq_block sb; insn = pattern_seqs->insn; bb = BLOCK_FOR_INSN (insn); /* Get the label after the sequence. This will be the return address. The label will be referenced using a symbol_ref so protect it from deleting. */ retlabel = block_label_after (insn); LABEL_PRESERVE_P (retlabel) = 1; /* Emit an indirect jump via the link register after the sequence acting as the return insn. Also emit a barrier and update the basic block. */ if (!find_reg_note (BB_END (bb), REG_NORETURN, NULL)) retjmp = emit_jump_insn_after (gen_indirect_jump (pattern_seqs->link_reg), BB_END (bb)); emit_barrier_after (BB_END (bb)); /* Replace all outgoing edges with a new one to the block of RETLABEL. */ while (EDGE_COUNT (bb->succs) != 0) remove_edge (EDGE_SUCC (bb, 0)); make_edge (bb, BLOCK_FOR_INSN (retlabel), EDGE_ABNORMAL); /* Split the sequence according to SEQ_BLOCKS and cache the label of the resulting basic blocks. */ i = 0; for (sb = seq_blocks; sb; sb = sb->next_seq_block) { for (; i < sb->length; i++) insn = prev_insn_in_block (insn); sb->label = block_label (split_block_and_df_analyze (bb, insn)); } /* Emit an insn saving the return address to the link register before the sequence. */ saveinsn = emit_insn_after (gen_move_insn (pattern_seqs->link_reg, gen_symbol_ref_rtx_for_label (retlabel)), BB_END (bb)); /* Update liveness info. */ SET_REGNO_REG_SET (df_get_live_out (bb), REGNO (pattern_seqs->link_reg)); }
tree create_phi_node (tree var, basic_block bb) { tree phi; phi = make_phi_node (var, EDGE_COUNT (bb->preds)); /* Add the new PHI node to the list of PHI nodes for block BB. */ PHI_CHAIN (phi) = phi_nodes (bb); bb->phi_nodes = phi; /* Associate BB to the PHI node. */ set_bb_for_stmt (phi, bb); return phi; }
static basic_block construct_init_block (void) { basic_block init_block, first_block; edge e = NULL; int flags; /* Multiple entry points not supported yet. */ gcc_assert (EDGE_COUNT (ENTRY_BLOCK_PTR->succs) == 1); e = EDGE_SUCC (ENTRY_BLOCK_PTR, 0); /* When entry edge points to first basic block, we don't need jump, otherwise we have to jump into proper target. */ if (e && e->dest != ENTRY_BLOCK_PTR->next_bb) { tree label = tree_block_label (e->dest); emit_jump (label_rtx (label)); flags = 0; } else flags = EDGE_FALLTHRU; init_block = create_basic_block (NEXT_INSN (get_insns ()), get_last_insn (), ENTRY_BLOCK_PTR); init_block->frequency = ENTRY_BLOCK_PTR->frequency; init_block->count = ENTRY_BLOCK_PTR->count; if (e) { first_block = e->dest; redirect_edge_succ (e, init_block); e = make_edge (init_block, first_block, flags); } else e = make_edge (init_block, EXIT_BLOCK_PTR, EDGE_FALLTHRU); e->probability = REG_BR_PROB_BASE; e->count = ENTRY_BLOCK_PTR->count; update_bb_for_insn (init_block); return init_block; }
static gphi * input_phi (struct lto_input_block *ib, basic_block bb, struct data_in *data_in, struct function *fn) { unsigned HOST_WIDE_INT ix; tree phi_result; int i, len; gphi *result; ix = streamer_read_uhwi (ib); phi_result = (*SSANAMES (fn))[ix]; len = EDGE_COUNT (bb->preds); result = create_phi_node (phi_result, bb); /* We have to go through a lookup process here because the preds in the reconstructed graph are generally in a different order than they were in the original program. */ for (i = 0; i < len; i++) { tree def = stream_read_tree (ib, data_in); int src_index = streamer_read_uhwi (ib); bitpack_d bp = streamer_read_bitpack (ib); /* Do not cache a location - we do not have API to get pointer to the location in PHI statement and we may trigger reallocation. */ location_t arg_loc = stream_input_location_now (&bp, data_in); basic_block sbb = BASIC_BLOCK_FOR_FN (fn, src_index); edge e = NULL; int j; for (j = 0; j < len; j++) if (EDGE_PRED (bb, j)->src == sbb) { e = EDGE_PRED (bb, j); break; } add_phi_arg (result, def, e, arg_loc); } return result; }
/* Subtract COUNT and FREQUENCY from the basic block and it's outgoing edge. */ static void decrease_profile (basic_block bb, gcov_type count, int frequency) { edge e; bb->count -= count; if (bb->count < 0) bb->count = 0; bb->frequency -= frequency; if (bb->frequency < 0) bb->frequency = 0; if (!single_succ_p (bb)) { gcc_assert (!EDGE_COUNT (bb->succs)); return; } e = single_succ_edge (bb); e->count -= count; if (e->count < 0) e->count = 0; }
static void print_global_statistics (FILE* file) { long n_bbs = 0; long n_loops = 0; long n_stmts = 0; long n_conditions = 0; long n_p_bbs = 0; long n_p_loops = 0; long n_p_stmts = 0; long n_p_conditions = 0; basic_block bb; FOR_ALL_BB_FN (bb, cfun) { gimple_stmt_iterator psi; n_bbs++; n_p_bbs += bb->count; /* Ignore artificial surrounding loop. */ if (bb == bb->loop_father->header && bb->index != 0) { n_loops++; n_p_loops += bb->count; } if (EDGE_COUNT (bb->succs) > 1) { n_conditions++; n_p_conditions += bb->count; } for (psi = gsi_start_bb (bb); !gsi_end_p (psi); gsi_next (&psi)) { n_stmts++; n_p_stmts += bb->count; } }
static gimple input_phi (struct lto_input_block *ib, basic_block bb, struct data_in *data_in, struct function *fn) { unsigned HOST_WIDE_INT ix; tree phi_result; int i, len; gimple result; ix = streamer_read_uhwi (ib); phi_result = VEC_index (tree, SSANAMES (fn), ix); len = EDGE_COUNT (bb->preds); result = create_phi_node (phi_result, bb); SSA_NAME_DEF_STMT (phi_result) = result; /* We have to go through a lookup process here because the preds in the reconstructed graph are generally in a different order than they were in the original program. */ for (i = 0; i < len; i++) { tree def = stream_read_tree (ib, data_in); int src_index = streamer_read_uhwi (ib); location_t arg_loc = lto_input_location (ib, data_in); basic_block sbb = BASIC_BLOCK_FOR_FUNCTION (fn, src_index); edge e = NULL; int j; for (j = 0; j < len; j++) if (EDGE_PRED (bb, j)->src == sbb) { e = EDGE_PRED (bb, j); break; } add_phi_arg (result, def, e, arg_loc); } return result; }
unsigned int execute_fixup_cfg (void) { basic_block bb; gimple_stmt_iterator gsi; int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0; gcov_type count_scale; edge e; edge_iterator ei; count_scale = GCOV_COMPUTE_SCALE (cgraph_get_node (current_function_decl)->count, ENTRY_BLOCK_PTR->count); ENTRY_BLOCK_PTR->count = cgraph_get_node (current_function_decl)->count; EXIT_BLOCK_PTR->count = apply_scale (EXIT_BLOCK_PTR->count, count_scale); FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs) e->count = apply_scale (e->count, count_scale); FOR_EACH_BB (bb) { bb->count = apply_scale (bb->count, count_scale); for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple stmt = gsi_stmt (gsi); tree decl = is_gimple_call (stmt) ? gimple_call_fndecl (stmt) : NULL; if (decl) { int flags = gimple_call_flags (stmt); if (flags & (ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE)) { if (gimple_purge_dead_abnormal_call_edges (bb)) todo |= TODO_cleanup_cfg; if (gimple_in_ssa_p (cfun)) { todo |= TODO_update_ssa | TODO_cleanup_cfg; update_stmt (stmt); } } if (flags & ECF_NORETURN && fixup_noreturn_call (stmt)) todo |= TODO_cleanup_cfg; } if (maybe_clean_eh_stmt (stmt) && gimple_purge_dead_eh_edges (bb)) todo |= TODO_cleanup_cfg; } FOR_EACH_EDGE (e, ei, bb->succs) e->count = apply_scale (e->count, count_scale); /* If we have a basic block with no successors that does not end with a control statement or a noreturn call end it with a call to __builtin_unreachable. This situation can occur when inlining a noreturn call that does in fact return. */ if (EDGE_COUNT (bb->succs) == 0) { gimple stmt = last_stmt (bb); if (!stmt || (!is_ctrl_stmt (stmt) && (!is_gimple_call (stmt) || (gimple_call_flags (stmt) & ECF_NORETURN) == 0))) { stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); gimple_stmt_iterator gsi = gsi_last_bb (bb); gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); } } } if (count_scale != REG_BR_PROB_BASE) compute_function_frequency (); /* We just processed all calls. */ if (cfun->gimple_df) vec_free (MODIFIED_NORETURN_CALLS (cfun)); /* Dump a textual representation of the flowgraph. */ if (dump_file) gimple_dump_cfg (dump_file, dump_flags); if (current_loops && (todo & TODO_cleanup_cfg)) loops_state_set (LOOPS_NEED_FIXUP); return todo; }
static basic_block create_pre_exit (int n_entities, int *entity_map, const int *num_modes) { edge eg; edge_iterator ei; basic_block pre_exit; /* The only non-call predecessor at this stage is a block with a fallthrough edge; there can be at most one, but there could be none at all, e.g. when exit is called. */ pre_exit = 0; FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) if (eg->flags & EDGE_FALLTHRU) { basic_block src_bb = eg->src; rtx_insn *last_insn; rtx ret_reg; gcc_assert (!pre_exit); /* If this function returns a value at the end, we have to insert the final mode switch before the return value copy to its hard register. */ if (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 1 && NONJUMP_INSN_P ((last_insn = BB_END (src_bb))) && GET_CODE (PATTERN (last_insn)) == USE && GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG) { int ret_start = REGNO (ret_reg); int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)]; int ret_end = ret_start + nregs; bool short_block = false; bool multi_reg_return = false; bool forced_late_switch = false; rtx_insn *before_return_copy; do { rtx_insn *return_copy = PREV_INSN (last_insn); rtx return_copy_pat, copy_reg; int copy_start, copy_num; int j; if (NONDEBUG_INSN_P (return_copy)) { /* When using SJLJ exceptions, the call to the unregister function is inserted between the clobber of the return value and the copy. We do not want to split the block before this or any other call; if we have not found the copy yet, the copy must have been deleted. */ if (CALL_P (return_copy)) { short_block = true; break; } return_copy_pat = PATTERN (return_copy); switch (GET_CODE (return_copy_pat)) { case USE: /* Skip USEs of multiple return registers. __builtin_apply pattern is also handled here. */ if (GET_CODE (XEXP (return_copy_pat, 0)) == REG && (targetm.calls.function_value_regno_p (REGNO (XEXP (return_copy_pat, 0))))) { multi_reg_return = true; last_insn = return_copy; continue; } break; case ASM_OPERANDS: /* Skip barrier insns. */ if (!MEM_VOLATILE_P (return_copy_pat)) break; /* Fall through. */ case ASM_INPUT: case UNSPEC_VOLATILE: last_insn = return_copy; continue; default: break; } /* If the return register is not (in its entirety) likely spilled, the return copy might be partially or completely optimized away. */ return_copy_pat = single_set (return_copy); if (!return_copy_pat) { return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; else if (!optimize) { /* This might be (clobber (reg [<result>])) when not optimizing. Then check if the previous insn is the clobber for the return register. */ copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG && !HARD_REGISTER_NUM_P (REGNO (copy_reg))) { if (INSN_P (PREV_INSN (return_copy))) { return_copy = PREV_INSN (return_copy); return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; } } } } copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG) copy_start = REGNO (copy_reg); else if (GET_CODE (copy_reg) == SUBREG && GET_CODE (SUBREG_REG (copy_reg)) == REG) copy_start = REGNO (SUBREG_REG (copy_reg)); else { /* When control reaches end of non-void function, there are no return copy insns at all. This avoids an ice on that invalid function. */ if (ret_start + nregs == ret_end) short_block = true; break; } if (!targetm.calls.function_value_regno_p (copy_start)) copy_num = 0; else copy_num = hard_regno_nregs[copy_start][GET_MODE (copy_reg)]; /* If the return register is not likely spilled, - as is the case for floating point on SH4 - then it might be set by an arithmetic operation that needs a different mode than the exit block. */ for (j = n_entities - 1; j >= 0; j--) { int e = entity_map[j]; int mode = targetm.mode_switching.needed (e, return_copy); if (mode != num_modes[e] && mode != targetm.mode_switching.exit (e)) break; } if (j >= 0) { /* __builtin_return emits a sequence of loads to all return registers. One of them might require another mode than MODE_EXIT, even if it is unrelated to the return value, so we want to put the final mode switch after it. */ if (multi_reg_return && targetm.calls.function_value_regno_p (copy_start)) forced_late_switch = true; /* For the SH4, floating point loads depend on fpscr, thus we might need to put the final mode switch after the return value copy. That is still OK, because a floating point return value does not conflict with address reloads. */ if (copy_start >= ret_start && copy_start + copy_num <= ret_end && OBJECT_P (SET_SRC (return_copy_pat))) forced_late_switch = true; break; } if (copy_num == 0) { last_insn = return_copy; continue; } if (copy_start >= ret_start && copy_start + copy_num <= ret_end) nregs -= copy_num; else if (!multi_reg_return || !targetm.calls.function_value_regno_p (copy_start)) break; last_insn = return_copy; } /* ??? Exception handling can lead to the return value copy being already separated from the return value use, as in unwind-dw2.c . Similarly, conditionally returning without a value, and conditionally using builtin_return can lead to an isolated use. */ if (return_copy == BB_HEAD (src_bb)) { short_block = true; break; } last_insn = return_copy; } while (nregs); /* If we didn't see a full return value copy, verify that there is a plausible reason for this. If some, but not all of the return register is likely spilled, we can expect that there is a copy for the likely spilled part. */ gcc_assert (!nregs || forced_late_switch || short_block || !(targetm.class_likely_spilled_p (REGNO_REG_CLASS (ret_start))) || (nregs != hard_regno_nregs[ret_start][GET_MODE (ret_reg)]) /* For multi-hard-register floating point values, sometimes the likely-spilled part is ordinarily copied first, then the other part is set with an arithmetic operation. This doesn't actually cause reload failures, so let it pass. */ || (GET_MODE_CLASS (GET_MODE (ret_reg)) != MODE_INT && nregs != 1)); if (!NOTE_INSN_BASIC_BLOCK_P (last_insn)) { before_return_copy = emit_note_before (NOTE_INSN_DELETED, last_insn); /* Instructions preceding LAST_INSN in the same block might require a different mode than MODE_EXIT, so if we might have such instructions, keep them in a separate block from pre_exit. */ src_bb = split_block (src_bb, PREV_INSN (before_return_copy))->dest; } else before_return_copy = last_insn; pre_exit = split_block (src_bb, before_return_copy)->src; } else { pre_exit = split_edge (eg); } } return pre_exit; }
static bool verify_phi_args (tree phi, basic_block bb, basic_block *definition_block) { edge e; bool err = false; unsigned i, phi_num_args = PHI_NUM_ARGS (phi); if (EDGE_COUNT (bb->preds) != phi_num_args) { error ("incoming edge count does not match number of PHI arguments"); err = true; goto error; } for (i = 0; i < phi_num_args; i++) { use_operand_p op_p = PHI_ARG_DEF_PTR (phi, i); tree op = USE_FROM_PTR (op_p); e = EDGE_PRED (bb, i); if (op == NULL_TREE) { error ("PHI argument is missing for edge %d->%d", e->src->index, e->dest->index); err = true; goto error; } if (TREE_CODE (op) != SSA_NAME && !is_gimple_min_invariant (op)) { error ("PHI argument is not SSA_NAME, or invariant"); err = true; } if (TREE_CODE (op) == SSA_NAME) err = verify_use (e->src, definition_block[SSA_NAME_VERSION (op)], op_p, phi, e->flags & EDGE_ABNORMAL, !is_gimple_reg (PHI_RESULT (phi)), NULL); if (e->dest != bb) { error ("wrong edge %d->%d for PHI argument", e->src->index, e->dest->index); err = true; } if (err) { fprintf (stderr, "PHI argument\n"); print_generic_stmt (stderr, op, TDF_VOPS); goto error; } } error: if (err) { fprintf (stderr, "for PHI node\n"); print_generic_stmt (stderr, phi, TDF_VOPS); } return err; }
void walk_dominator_tree (struct dom_walk_data *walk_data, basic_block bb) { void *bd = NULL; basic_block dest; block_stmt_iterator bsi; bool is_interesting; basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks * 2); int sp = 0; while (true) { /* Don't worry about unreachable blocks. */ if (EDGE_COUNT (bb->preds) > 0 || bb == ENTRY_BLOCK_PTR || bb == EXIT_BLOCK_PTR) { /* If block BB is not interesting to the caller, then none of the callbacks that walk the statements in BB are going to be executed. */ is_interesting = walk_data->interesting_blocks == NULL || TEST_BIT (walk_data->interesting_blocks, bb->index); /* Callback to initialize the local data structure. */ if (walk_data->initialize_block_local_data) { bool recycled; /* First get some local data, reusing any local data pointer we may have saved. */ if (VEC_length (void_p, walk_data->free_block_data) > 0) { bd = VEC_pop (void_p, walk_data->free_block_data); recycled = 1; } else { bd = xcalloc (1, walk_data->block_local_data_size); recycled = 0; } /* Push the local data into the local data stack. */ VEC_safe_push (void_p, heap, walk_data->block_data_stack, bd); /* Call the initializer. */ walk_data->initialize_block_local_data (walk_data, bb, recycled); } /* Callback for operations to execute before we have walked the dominator children, but before we walk statements. */ if (walk_data->before_dom_children_before_stmts) (*walk_data->before_dom_children_before_stmts) (walk_data, bb); /* Statement walk before walking dominator children. */ if (is_interesting && walk_data->before_dom_children_walk_stmts) { if (walk_data->walk_stmts_backward) for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi)) (*walk_data->before_dom_children_walk_stmts) (walk_data, bb, bsi); else for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) (*walk_data->before_dom_children_walk_stmts) (walk_data, bb, bsi); } /* Callback for operations to execute before we have walked the dominator children, and after we walk statements. */ if (walk_data->before_dom_children_after_stmts) (*walk_data->before_dom_children_after_stmts) (walk_data, bb); /* Mark the current BB to be popped out of the recursion stack once childs are processed. */ worklist[sp++] = bb; worklist[sp++] = NULL; for (dest = first_dom_son (walk_data->dom_direction, bb); dest; dest = next_dom_son (walk_data->dom_direction, dest)) worklist[sp++] = dest; } /* NULL is used to signalize pop operation in recursion stack. */ while (sp > 0 && !worklist[sp - 1]) { --sp; bb = worklist[--sp]; is_interesting = walk_data->interesting_blocks == NULL || TEST_BIT (walk_data->interesting_blocks, bb->index); /* Callback for operations to execute after we have walked the dominator children, but before we walk statements. */ if (walk_data->after_dom_children_before_stmts) (*walk_data->after_dom_children_before_stmts) (walk_data, bb); /* Statement walk after walking dominator children. */ if (is_interesting && walk_data->after_dom_children_walk_stmts) { if (walk_data->walk_stmts_backward) for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi)) (*walk_data->after_dom_children_walk_stmts) (walk_data, bb, bsi); else for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) (*walk_data->after_dom_children_walk_stmts) (walk_data, bb, bsi); } /* Callback for operations to execute after we have walked the dominator children and after we have walked statements. */ if (walk_data->after_dom_children_after_stmts) (*walk_data->after_dom_children_after_stmts) (walk_data, bb); if (walk_data->initialize_block_local_data) { /* And finally pop the record off the block local data stack. */ bd = VEC_pop (void_p, walk_data->block_data_stack); /* And save the block data so that we can re-use it. */ VEC_safe_push (void_p, heap, walk_data->free_block_data, bd); } } if (sp) bb = worklist[--sp]; else break; } free (worklist); }
static bool tail_duplicate (void) { fibnode_t *blocks = XCNEWVEC (fibnode_t, last_basic_block); basic_block *trace = XNEWVEC (basic_block, n_basic_blocks); int *counts = XNEWVEC (int, last_basic_block); int ninsns = 0, nduplicated = 0; gcov_type weighted_insns = 0, traced_insns = 0; fibheap_t heap = fibheap_new (); gcov_type cover_insns; int max_dup_insns; basic_block bb; bool changed = false; /* Create an oversized sbitmap to reduce the chance that we need to resize it. */ bb_seen = sbitmap_alloc (last_basic_block * 2); bitmap_clear (bb_seen); initialize_original_copy_tables (); if (profile_info && flag_branch_probabilities) probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY_FEEDBACK); else probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY); probability_cutoff = REG_BR_PROB_BASE / 100 * probability_cutoff; branch_ratio_cutoff = (REG_BR_PROB_BASE / 100 * PARAM_VALUE (TRACER_MIN_BRANCH_RATIO)); FOR_EACH_BB (bb) { int n = count_insns (bb); if (!ignore_bb_p (bb)) blocks[bb->index] = fibheap_insert (heap, -bb->frequency, bb); counts [bb->index] = n; ninsns += n; weighted_insns += n * bb->frequency; } if (profile_info && flag_branch_probabilities) cover_insns = PARAM_VALUE (TRACER_DYNAMIC_COVERAGE_FEEDBACK); else cover_insns = PARAM_VALUE (TRACER_DYNAMIC_COVERAGE); cover_insns = (weighted_insns * cover_insns + 50) / 100; max_dup_insns = (ninsns * PARAM_VALUE (TRACER_MAX_CODE_GROWTH) + 50) / 100; while (traced_insns < cover_insns && nduplicated < max_dup_insns && !fibheap_empty (heap)) { basic_block bb = (basic_block) fibheap_extract_min (heap); int n, pos; if (!bb) break; blocks[bb->index] = NULL; if (ignore_bb_p (bb)) continue; gcc_assert (!bb_seen_p (bb)); n = find_trace (bb, trace); bb = trace[0]; traced_insns += bb->frequency * counts [bb->index]; if (blocks[bb->index]) { fibheap_delete_node (heap, blocks[bb->index]); blocks[bb->index] = NULL; } for (pos = 1; pos < n; pos++) { basic_block bb2 = trace[pos]; if (blocks[bb2->index]) { fibheap_delete_node (heap, blocks[bb2->index]); blocks[bb2->index] = NULL; } traced_insns += bb2->frequency * counts [bb2->index]; if (EDGE_COUNT (bb2->preds) > 1 && can_duplicate_block_p (bb2) /* We have the tendency to duplicate the loop header of all do { } while loops. Do not do that - it is not profitable and it might create a loop with multiple entries or at least rotate the loop. */ && (!current_loops || bb2->loop_father->header != bb2)) { edge e; basic_block copy; nduplicated += counts [bb2->index]; e = find_edge (bb, bb2); copy = duplicate_block (bb2, e, bb); flush_pending_stmts (e); add_phi_args_after_copy (©, 1, NULL); /* Reconsider the original copy of block we've duplicated. Removing the most common predecessor may make it to be head. */ blocks[bb2->index] = fibheap_insert (heap, -bb2->frequency, bb2); if (dump_file) fprintf (dump_file, "Duplicated %i as %i [%i]\n", bb2->index, copy->index, copy->frequency); bb2 = copy; changed = true; } mark_bb_seen (bb2); bb = bb2; /* In case the trace became infrequent, stop duplicating. */ if (ignore_bb_p (bb)) break; } if (dump_file) fprintf (dump_file, " covered now %.1f\n\n", traced_insns * 100.0 / weighted_insns); } if (dump_file) fprintf (dump_file, "Duplicated %i insns (%i%%)\n", nduplicated, nduplicated * 100 / ninsns); free_original_copy_tables (); sbitmap_free (bb_seen); free (blocks); free (trace); free (counts); fibheap_delete (heap); return changed; }