bool cfg_layout_can_duplicate_bb_p (basic_block bb) { /* Do not attempt to duplicate tablejumps, as we need to unshare the dispatch table. This is difficult to do, as the instructions computing jump destination may be hoisted outside the basic block. */ if (tablejump_p (BB_END (bb), NULL, NULL)) return false; /* Do not duplicate blocks containing insns that can't be copied. */ if (targetm.cannot_copy_insn_p) { rtx insn = BB_HEAD (bb); while (1) { if (INSN_P (insn) && targetm.cannot_copy_insn_p (insn)) return false; if (insn == BB_END (bb)) break; insn = NEXT_INSN (insn); } } return true; }
static void compute_init_costs (void) { rtx rtx_jump, rtx_store, rtx_return, reg, label; basic_block bb; FOR_EACH_BB (bb) if (BB_HEAD (bb)) break; label = block_label (bb); reg = gen_rtx_REG (Pmode, 0); /* Pattern for indirect jump. */ rtx_jump = gen_indirect_jump (reg); /* Pattern for storing address. */ rtx_store = gen_rtx_SET (VOIDmode, reg, gen_symbol_ref_rtx_for_label (label)); /* Pattern for return insn. */ rtx_return = gen_jump (label); /* The cost of jump. */ seq_jump_cost = compute_rtx_cost (make_jump_insn_raw (rtx_jump)); /* The cost of calling sequence. */ seq_call_cost = seq_jump_cost + compute_rtx_cost (make_insn_raw (rtx_store)); /* The cost of return. */ seq_return_cost = compute_rtx_cost (make_jump_insn_raw (rtx_return)); /* Simple heuristic for minimal sequence cost. */ seq_call_cost = (int)(seq_call_cost * (double)SEQ_CALL_COST_MULTIPLIER); }
/* INSN is being scheduled after LAST. Update counters. */ static void begin_schedule_ready (rtx insn, rtx last) { sched_rgn_n_insns++; if (BLOCK_FOR_INSN (insn) == last_bb /* INSN is a jump in the last block, ... */ && control_flow_insn_p (insn) /* that is going to be moved over some instructions. */ && last != PREV_INSN (insn)) { edge e; basic_block bb; /* An obscure special case, where we do have partially dead instruction scheduled after last control flow instruction. In this case we can create new basic block. It is always exactly one basic block last in the sequence. */ e = find_fallthru_edge (last_bb->succs); gcc_checking_assert (!e || !(e->flags & EDGE_COMPLEX)); gcc_checking_assert (BLOCK_FOR_INSN (insn) == last_bb && !IS_SPECULATION_CHECK_P (insn) && BB_HEAD (last_bb) != insn && BB_END (last_bb) == insn); { rtx x; x = NEXT_INSN (insn); if (e) gcc_checking_assert (NOTE_P (x) || LABEL_P (x)); else gcc_checking_assert (BARRIER_P (x)); } if (e) { bb = split_edge (e); gcc_assert (NOTE_INSN_BASIC_BLOCK_P (BB_END (bb))); } else /* Create an empty unreachable block after the INSN. */ bb = create_basic_block (NEXT_INSN (insn), NULL_RTX, last_bb); /* split_edge () creates BB before E->DEST. Keep in mind, that this operation extends scheduling region till the end of BB. Hence, we need to shift NEXT_TAIL, so haifa-sched.c won't go out of the scheduling region. */ current_sched_info->next_tail = NEXT_INSN (BB_END (bb)); gcc_assert (current_sched_info->next_tail); /* Append new basic block to the end of the ebb. */ sched_init_only_bb (bb, last_bb); gcc_assert (last_bb == bb); } }
static rtx prev_active_insn_bb (basic_block bb, rtx insn) { for (insn = PREV_INSN (insn); insn != PREV_INSN (BB_HEAD (bb)); insn = PREV_INSN (insn)) if (active_insn_p (insn)) return insn; return NULL_RTX; }
static void record_effective_endpoints (void) { rtx next_insn; basic_block bb; rtx insn; for (insn = get_insns (); insn && NOTE_P (insn) && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK; insn = NEXT_INSN (insn)) continue; /* No basic blocks at all? */ gcc_assert (insn); if (PREV_INSN (insn)) cfg_layout_function_header = unlink_insn_chain (get_insns (), PREV_INSN (insn)); else cfg_layout_function_header = NULL_RTX; next_insn = get_insns (); FOR_EACH_BB (bb) { rtx end; if (PREV_INSN (BB_HEAD (bb)) && next_insn != BB_HEAD (bb)) bb->il.rtl->header = unlink_insn_chain (next_insn, PREV_INSN (BB_HEAD (bb))); end = skip_insns_after_block (bb); if (NEXT_INSN (BB_END (bb)) && BB_END (bb) != end) bb->il.rtl->footer = unlink_insn_chain (NEXT_INSN (BB_END (bb)), end); next_insn = NEXT_INSN (BB_END (bb)); } cfg_layout_function_footer = next_insn; if (cfg_layout_function_footer) cfg_layout_function_footer = unlink_insn_chain (cfg_layout_function_footer, get_last_insn ()); }
static int count_insns (basic_block bb) { rtx insn; int n = 0; for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn)) if (active_insn_p (insn)) n++; return n; }
static rtx label_for_bb (basic_block bb) { rtx label = BB_HEAD (bb); if (GET_CODE (label) != CODE_LABEL) { if (rtl_dump_file) fprintf (rtl_dump_file, "Emitting label for block %d\n", bb->index); label = block_label (bb); } return label; }
static rtx label_for_bb (basic_block bb) { rtx label = BB_HEAD (bb); if (!LABEL_P (label)) { if (dump_file) fprintf (dump_file, "Emitting label for block %d\n", bb->index); label = block_label (bb); } return label; }
static void verify_three_block_rtl_cfg (function *fun) { verify_three_block_cfg (fun); /* The "fake" basic blocks should be flagged as RTL, but with no insns. */ basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (fun); ASSERT_TRUE (entry != NULL); ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); ASSERT_EQ (NULL, BB_HEAD (entry)); basic_block exit = EXIT_BLOCK_PTR_FOR_FN (fun); ASSERT_TRUE (exit != NULL); ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); ASSERT_EQ (NULL, BB_HEAD (exit)); /* The "real" basic block should be flagged as RTL, and have one or more insns. */ basic_block bb2 = get_real_block (fun); ASSERT_TRUE (bb2 != NULL); ASSERT_EQ (BB_RTL, entry->flags & BB_RTL); ASSERT_TRUE (BB_HEAD (bb2) != NULL); }
static rtx prev_insn_in_block (rtx insn) { basic_block bb = BLOCK_FOR_INSN (insn); if (!bb) return NULL_RTX; while (insn != BB_HEAD (bb)) { insn = PREV_INSN (insn); if (INSN_P (insn)) return insn; } return NULL_RTX; }
static bool copy_bb_p (basic_block bb, int code_may_grow) { int size = 0; int max_size = uncond_jump_length; rtx insn; int n_succ; edge e; if (!bb->frequency) return false; if (!bb->pred || !bb->pred->pred_next) return false; if (!cfg_layout_can_duplicate_bb_p (bb)) return false; /* Avoid duplicating blocks which have many successors (PR/13430). */ n_succ = 0; for (e = bb->succ; e; e = e->succ_next) { n_succ++; if (n_succ > 8) return false; } if (code_may_grow && maybe_hot_bb_p (bb)) max_size *= 8; for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn)) { if (INSN_P (insn)) size += get_attr_length (insn); } if (size <= max_size) return true; if (rtl_dump_file) { fprintf (rtl_dump_file, "Block %d can't be copied because its size = %d.\n", bb->index, size); } return false; }
basic_block cfg_layout_duplicate_bb (basic_block bb) { rtx insn; basic_block new_bb; insn = duplicate_insn_chain (BB_HEAD (bb), BB_END (bb)); new_bb = create_basic_block (insn, insn ? get_last_insn () : NULL, EXIT_BLOCK_PTR->prev_bb); BB_COPY_PARTITION (new_bb, bb); if (bb->il.rtl->header) { insn = bb->il.rtl->header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); insn = duplicate_insn_chain (bb->il.rtl->header, insn); if (insn) new_bb->il.rtl->header = unlink_insn_chain (insn, get_last_insn ()); } if (bb->il.rtl->footer) { insn = bb->il.rtl->footer; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); insn = duplicate_insn_chain (bb->il.rtl->footer, insn); if (insn) new_bb->il.rtl->footer = unlink_insn_chain (insn, get_last_insn ()); } if (bb->il.rtl->global_live_at_start) { new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); COPY_REG_SET (new_bb->il.rtl->global_live_at_start, bb->il.rtl->global_live_at_start); COPY_REG_SET (new_bb->il.rtl->global_live_at_end, bb->il.rtl->global_live_at_end); } return new_bb; }
bool cfg_layout_can_duplicate_bb_p (basic_block bb) { edge s; if (bb == EXIT_BLOCK_PTR || bb == ENTRY_BLOCK_PTR) return false; /* Duplicating fallthru block to exit would require adding a jump and splitting the real last BB. */ for (s = bb->succ; s; s = s->succ_next) if (s->dest == EXIT_BLOCK_PTR && s->flags & EDGE_FALLTHRU) return false; /* Do not attempt to duplicate tablejumps, as we need to unshare the dispatch table. This is difficult to do, as the instructions computing jump destination may be hoisted outside the basic block. */ if (tablejump_p (BB_END (bb), NULL, NULL)) return false; /* Do not duplicate blocks containing insns that can't be copied. */ if (targetm.cannot_copy_insn_p) { rtx insn = BB_HEAD (bb); while (1) { if (INSN_P (insn) && (*targetm.cannot_copy_insn_p) (insn)) return false; if (insn == BB_END (bb)) break; insn = NEXT_INSN (insn); } } return true; }
static void collect_pattern_seqs (void) { htab_iterator hti0, hti1, hti2; p_hash_bucket hash_bucket; p_hash_elem e0, e1; #if defined STACK_REGS || defined HAVE_cc0 basic_block bb; bitmap_head dont_collect; /* Extra initialization step to ensure that no stack registers (if present) or cc0 code (if present) are live across abnormal edges. Set a flag in DONT_COLLECT for an insn if a stack register is live after the insn or the insn is cc0 setter or user. */ bitmap_initialize (&dont_collect, NULL); #ifdef STACK_REGS FOR_EACH_BB (bb) { regset_head live; rtx insn; rtx prev; /* Initialize liveness propagation. */ INIT_REG_SET (&live); bitmap_copy (&live, DF_LR_OUT (bb)); df_simulate_initialize_backwards (bb, &live); /* Propagate liveness info and mark insns where a stack reg is live. */ insn = BB_END (bb); for (insn = BB_END (bb); ; insn = prev) { prev = PREV_INSN (insn); if (INSN_P (insn)) { int reg; for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) { if (REGNO_REG_SET_P (&live, reg)) { bitmap_set_bit (&dont_collect, INSN_UID (insn)); break; } } } if (insn == BB_HEAD (bb)) break; df_simulate_one_insn_backwards (bb, insn, &live); insn = prev; } /* Free unused data. */ CLEAR_REG_SET (&live); } #endif #ifdef HAVE_cc0 /* Mark CC0 setters and users as ineligible for collection into sequences. This is an over-conservative fix, since it is OK to include a cc0_setter, but only if we also include the corresponding cc0_user, and vice versa. */ FOR_EACH_BB (bb) { rtx insn; rtx next_tail; next_tail = NEXT_INSN (BB_END (bb)); for (insn = BB_HEAD (bb); insn != next_tail; insn = NEXT_INSN (insn)) { if (INSN_P (insn) && reg_mentioned_p (cc0_rtx, PATTERN (insn))) bitmap_set_bit (&dont_collect, INSN_UID (insn)); } } #endif #endif /* defined STACK_REGS || defined HAVE_cc0 */ /* Initialize PATTERN_SEQS to empty. */ pattern_seqs = 0; /* Try to match every abstractable insn with every other insn in the same HASH_BUCKET. */ FOR_EACH_HTAB_ELEMENT (hash_buckets, hash_bucket, p_hash_bucket, hti0) if (htab_elements (hash_bucket->seq_candidates) > 1) FOR_EACH_HTAB_ELEMENT (hash_bucket->seq_candidates, e0, p_hash_elem, hti1) FOR_EACH_HTAB_ELEMENT (hash_bucket->seq_candidates, e1, p_hash_elem, hti2) if (e0 != e1 #if defined STACK_REGS || defined HAVE_cc0 && !bitmap_bit_p (&dont_collect, INSN_UID (e0->insn)) && !bitmap_bit_p (&dont_collect, INSN_UID (e1->insn)) #endif ) match_seqs (e0, e1); #if defined STACK_REGS || defined HAVE_cc0 /* Free unused data. */ bitmap_clear (&dont_collect); #endif }
static bool doloop_valid_p (struct loop *loop, struct niter_desc *desc) { basic_block *body = get_loop_body (loop), bb; rtx insn; unsigned i; bool result = true; /* Check for loops that may not terminate under special conditions. */ if (!desc->simple_p || desc->assumptions || desc->infinite) { /* There are some cases that would require a special attention. For example if the comparison is LEU and the comparison value is UINT_MAX then the loop will not terminate. Similarly, if the comparison code is GEU and the comparison value is 0, the loop will not terminate. If the absolute increment is not 1, the loop can be infinite even with LTU/GTU, e.g. for (i = 3; i > 0; i -= 2) APPLE LOCAL begin lno Note that with LE and GE, the loop behavior is undefined (C++ standard section 5 clause 5) if an overflow occurs, say between INT_MAX and INT_MAX + 1. We thus don't have to worry about these two cases. APPLE LOCAL end lno ??? We could compute these conditions at run-time and have a additional jump around the loop to ensure an infinite loop. However, it is very unlikely that this is the intended behavior of the loop and checking for these rare boundary conditions would pessimize all other code. If the loop is executed only a few times an extra check to restart the loop could use up most of the benefits of using a count register loop. Note however, that normally, this restart branch would never execute, so it could be predicted well by the CPU. We should generate the pessimistic code by default, and have an option, e.g. -funsafe-loops that would enable count-register loops in this case. */ if (dump_file) fprintf (dump_file, "Doloop: Possible infinite iteration case.\n"); result = false; goto cleanup; } for (i = 0; i < loop->num_nodes; i++) { bb = body[i]; for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn)) { /* A called function may clobber any special registers required for low-overhead looping. */ if (CALL_P (insn)) { if (dump_file) fprintf (dump_file, "Doloop: Function call in loop.\n"); result = false; goto cleanup; } /* Some targets (eg, PPC) use the count register for branch on table instructions. ??? This should be a target specific check. */ if (JUMP_P (insn) && (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC || GET_CODE (PATTERN (insn)) == ADDR_VEC)) { if (dump_file) fprintf (dump_file, "Doloop: Computed branch in the loop.\n"); result = false; goto cleanup; } } } result = true; cleanup: free (body); return result; }
static void fixup_reorder_chain (void) { basic_block bb, prev_bb; int index; rtx insn = NULL; if (cfg_layout_function_header) { set_first_insn (cfg_layout_function_header); insn = cfg_layout_function_header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } /* First do the bulk reordering -- rechain the blocks without regard to the needed changes to jumps and labels. */ for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS; bb != 0; bb = bb->aux, index++) { if (bb->il.rtl->header) { if (insn) NEXT_INSN (insn) = bb->il.rtl->header; else set_first_insn (bb->il.rtl->header); PREV_INSN (bb->il.rtl->header) = insn; insn = bb->il.rtl->header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } if (insn) NEXT_INSN (insn) = BB_HEAD (bb); else set_first_insn (BB_HEAD (bb)); PREV_INSN (BB_HEAD (bb)) = insn; insn = BB_END (bb); if (bb->il.rtl->footer) { NEXT_INSN (insn) = bb->il.rtl->footer; PREV_INSN (bb->il.rtl->footer) = insn; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } } gcc_assert (index == n_basic_blocks); NEXT_INSN (insn) = cfg_layout_function_footer; if (cfg_layout_function_footer) PREV_INSN (cfg_layout_function_footer) = insn; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); set_last_insn (insn); #ifdef ENABLE_CHECKING verify_insn_chain (); #endif delete_dead_jumptables (); /* Now add jumps and labels as needed to match the blocks new outgoing edges. */ for (bb = ENTRY_BLOCK_PTR->next_bb; bb ; bb = bb->aux) { edge e_fall, e_taken, e; rtx bb_end_insn; basic_block nb; edge_iterator ei; if (EDGE_COUNT (bb->succs) == 0) continue; /* Find the old fallthru edge, and another non-EH edge for a taken jump. */ e_taken = e_fall = NULL; FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALLTHRU) e_fall = e; else if (! (e->flags & EDGE_EH)) e_taken = e; bb_end_insn = BB_END (bb); if (JUMP_P (bb_end_insn)) { if (any_condjump_p (bb_end_insn)) { /* If the old fallthru is still next, nothing to do. */ if (bb->aux == e_fall->dest || e_fall->dest == EXIT_BLOCK_PTR) continue; /* The degenerated case of conditional jump jumping to the next instruction can happen for jumps with side effects. We need to construct a forwarder block and this will be done just fine by force_nonfallthru below. */ if (!e_taken) ; /* There is another special case: if *neither* block is next, such as happens at the very end of a function, then we'll need to add a new unconditional jump. Choose the taken edge based on known or assumed probability. */ else if (bb->aux != e_taken->dest) { rtx note = find_reg_note (bb_end_insn, REG_BR_PROB, 0); if (note && INTVAL (XEXP (note, 0)) < REG_BR_PROB_BASE / 2 && invert_jump (bb_end_insn, (e_fall->dest == EXIT_BLOCK_PTR ? NULL_RTX : label_for_bb (e_fall->dest)), 0)) { e_fall->flags &= ~EDGE_FALLTHRU; #ifdef ENABLE_CHECKING gcc_assert (could_fall_through (e_taken->src, e_taken->dest)); #endif e_taken->flags |= EDGE_FALLTHRU; update_br_prob_note (bb); e = e_fall, e_fall = e_taken, e_taken = e; } } /* If the "jumping" edge is a crossing edge, and the fall through edge is non-crossing, leave things as they are. */ else if ((e_taken->flags & EDGE_CROSSING) && !(e_fall->flags & EDGE_CROSSING)) continue; /* Otherwise we can try to invert the jump. This will basically never fail, however, keep up the pretense. */ else if (invert_jump (bb_end_insn, (e_fall->dest == EXIT_BLOCK_PTR ? NULL_RTX : label_for_bb (e_fall->dest)), 0)) { e_fall->flags &= ~EDGE_FALLTHRU; #ifdef ENABLE_CHECKING gcc_assert (could_fall_through (e_taken->src, e_taken->dest)); #endif e_taken->flags |= EDGE_FALLTHRU; update_br_prob_note (bb); continue; } } else { /* Otherwise we have some return, switch or computed jump. In the 99% case, there should not have been a fallthru edge. */ gcc_assert (returnjump_p (bb_end_insn) || !e_fall); continue; } } else { /* No fallthru implies a noreturn function with EH edges, or something similarly bizarre. In any case, we don't need to do anything. */ if (! e_fall) continue; /* If the fallthru block is still next, nothing to do. */ if (bb->aux == e_fall->dest) continue; /* A fallthru to exit block. */ if (e_fall->dest == EXIT_BLOCK_PTR) continue; } /* We got here if we need to add a new jump insn. */ nb = force_nonfallthru (e_fall); if (nb) { nb->il.rtl->visited = 1; nb->aux = bb->aux; bb->aux = nb; /* Don't process this new block. */ bb = nb; /* Make sure new bb is tagged for correct section (same as fall-thru source, since you cannot fall-throu across section boundaries). */ BB_COPY_PARTITION (e_fall->src, single_pred (bb)); if (flag_reorder_blocks_and_partition && targetm.have_named_sections && JUMP_P (BB_END (bb)) && !any_condjump_p (BB_END (bb)) && (EDGE_SUCC (bb, 0)->flags & EDGE_CROSSING)) REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb))); } } /* Put basic_block_info in the new order. */ if (dump_file) { fprintf (dump_file, "Reordered sequence:\n"); for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS; bb; bb = bb->aux, index++) { fprintf (dump_file, " %i ", index); if (get_bb_original (bb)) fprintf (dump_file, "duplicate of %i ", get_bb_original (bb)->index); else if (forwarder_block_p (bb) && !LABEL_P (BB_HEAD (bb))) fprintf (dump_file, "compensation "); else fprintf (dump_file, "bb %i ", bb->index); fprintf (dump_file, " [%i]\n", bb->frequency); } } prev_bb = ENTRY_BLOCK_PTR; bb = ENTRY_BLOCK_PTR->next_bb; index = NUM_FIXED_BLOCKS; for (; bb; prev_bb = bb, bb = bb->aux, index ++) { bb->index = index; SET_BASIC_BLOCK (index, bb); bb->prev_bb = prev_bb; prev_bb->next_bb = bb; } prev_bb->next_bb = EXIT_BLOCK_PTR; EXIT_BLOCK_PTR->prev_bb = prev_bb; /* Annoying special case - jump around dead jumptables left in the code. */ FOR_EACH_BB (bb) { edge e; edge_iterator ei; FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALLTHRU) break; if (e && !can_fallthru (e->src, e->dest)) force_nonfallthru (e); } }
void find_comparison_dom_walker::before_dom_children (basic_block bb) { struct comparison *last_cmp; rtx_insn *insn, *next, *last_clobber; bool last_cmp_valid; bool need_purge = false; bitmap killed; killed = BITMAP_ALLOC (NULL); /* The last comparison that was made. Will be reset to NULL once the flags are clobbered. */ last_cmp = NULL; /* True iff the last comparison has not been clobbered, nor have its inputs. Used to eliminate duplicate compares. */ last_cmp_valid = false; /* The last insn that clobbered the flags, if that insn is of a form that may be valid for eliminating a following compare. To be reset to NULL once the flags are set otherwise. */ last_clobber = NULL; /* Propagate the last live comparison throughout the extended basic block. */ if (single_pred_p (bb)) { last_cmp = (struct comparison *) single_pred (bb)->aux; if (last_cmp) last_cmp_valid = last_cmp->inputs_valid; } for (insn = BB_HEAD (bb); insn; insn = next) { rtx src; next = (insn == BB_END (bb) ? NULL : NEXT_INSN (insn)); if (!NONDEBUG_INSN_P (insn)) continue; /* Compute the set of registers modified by this instruction. */ bitmap_clear (killed); df_simulate_find_defs (insn, killed); src = conforming_compare (insn); if (src) { rtx eh_note = NULL; if (cfun->can_throw_non_call_exceptions) eh_note = find_reg_note (insn, REG_EH_REGION, NULL); if (last_cmp_valid && can_eliminate_compare (src, eh_note, last_cmp)) { if (eh_note) need_purge = true; delete_insn (insn); continue; } last_cmp = XCNEW (struct comparison); last_cmp->insn = insn; last_cmp->prev_clobber = last_clobber; last_cmp->in_a = XEXP (src, 0); last_cmp->in_b = XEXP (src, 1); last_cmp->eh_note = eh_note; last_cmp->orig_mode = GET_MODE (src); all_compares.safe_push (last_cmp); /* It's unusual, but be prepared for comparison patterns that also clobber an input, or perhaps a scratch. */ last_clobber = NULL; last_cmp_valid = true; } /* Notice if this instruction kills the flags register. */ else if (bitmap_bit_p (killed, targetm.flags_regnum)) { /* See if this insn could be the "clobber" that eliminates a future comparison. */ last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL); /* In either case, the previous compare is no longer valid. */ last_cmp = NULL; last_cmp_valid = false; } /* Notice if this instruction uses the flags register. */ else if (last_cmp) find_flags_uses_in_insn (last_cmp, insn); /* Notice if any of the inputs to the comparison have changed. */ if (last_cmp_valid && (bitmap_bit_p (killed, REGNO (last_cmp->in_a)) || (REG_P (last_cmp->in_b) && bitmap_bit_p (killed, REGNO (last_cmp->in_b))))) last_cmp_valid = false; }
static bool copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd) { bool anything_changed = false; rtx insn; for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn)) { int n_ops, i, alt, predicated; bool is_asm, any_replacements; rtx set; bool replaced[MAX_RECOG_OPERANDS]; bool changed = false; struct kill_set_value_data ksvd; if (!NONDEBUG_INSN_P (insn)) { if (DEBUG_INSN_P (insn)) { rtx loc = INSN_VAR_LOCATION_LOC (insn); if (!VAR_LOC_UNKNOWN_P (loc)) replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn), ALL_REGS, GET_MODE (loc), ADDR_SPACE_GENERIC, insn, vd); } if (insn == BB_END (bb)) break; else continue; } set = single_set (insn); extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); alt = which_alternative; n_ops = recog_data.n_operands; is_asm = asm_noperands (PATTERN (insn)) >= 0; /* Simplify the code below by rewriting things to reflect matching constraints. Also promote OP_OUT to OP_INOUT in predicated instructions. */ predicated = GET_CODE (PATTERN (insn)) == COND_EXEC; for (i = 0; i < n_ops; ++i) { int matches = recog_op_alt[i][alt].matches; if (matches >= 0) recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl; if (matches >= 0 || recog_op_alt[i][alt].matched >= 0 || (predicated && recog_data.operand_type[i] == OP_OUT)) recog_data.operand_type[i] = OP_INOUT; } /* Apply changes to earlier DEBUG_INSNs if possible. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); /* For each earlyclobber operand, zap the value data. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Within asms, a clobber cannot overlap inputs or outputs. I wouldn't think this were true for regular insns, but scan_rtx treats them like that... */ note_stores (PATTERN (insn), kill_clobbered_value, vd); /* Kill all auto-incremented values. */ /* ??? REG_INC is useless, since stack pushes aren't done that way. */ for_each_rtx (&PATTERN (insn), kill_autoinc_value, vd); /* Kill all early-clobbered operands. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Special-case plain move instructions, since we may well be able to do the move from a different register class. */ if (set && REG_P (SET_SRC (set))) { rtx src = SET_SRC (set); unsigned int regno = REGNO (src); enum machine_mode mode = GET_MODE (src); unsigned int i; rtx new_rtx; /* If we are accessing SRC in some mode other that what we set it in, make sure that the replacement is valid. */ if (mode != vd->e[regno].mode) { if (hard_regno_nregs[regno][mode] > hard_regno_nregs[regno][vd->e[regno].mode]) goto no_move_special_case; /* And likewise, if we are narrowing on big endian the transformation is also invalid. */ if (hard_regno_nregs[regno][mode] < hard_regno_nregs[regno][vd->e[regno].mode] && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)) goto no_move_special_case; } /* If the destination is also a register, try to find a source register in the same class. */ if (REG_P (SET_DEST (set))) { new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd); if (new_rtx && validate_change (insn, &SET_SRC (set), new_rtx, 0)) { if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } /* Otherwise, try all valid registers and see if its valid. */ for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno) { new_rtx = maybe_mode_change (vd->e[i].mode, vd->e[regno].mode, mode, i, regno); if (new_rtx != NULL_RTX) { if (validate_change (insn, &SET_SRC (set), new_rtx, 0)) { ORIGINAL_REGNO (new_rtx) = ORIGINAL_REGNO (src); REG_ATTRS (new_rtx) = REG_ATTRS (src); REG_POINTER (new_rtx) = REG_POINTER (src); if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } } } no_move_special_case: any_replacements = false; /* For each input operand, replace a hard register with the eldest live copy that's in an appropriate register class. */ for (i = 0; i < n_ops; i++) { replaced[i] = false; /* Don't scan match_operand here, since we've no reg class information to pass down. Any operands that we could substitute in will be represented elsewhere. */ if (recog_data.constraints[i][0] == '\0') continue; /* Don't replace in asms intentionally referencing hard regs. */ if (is_asm && REG_P (recog_data.operand[i]) && (REGNO (recog_data.operand[i]) == ORIGINAL_REGNO (recog_data.operand[i]))) continue; if (recog_data.operand_type[i] == OP_IN) { if (recog_op_alt[i][alt].is_address) replaced[i] = replace_oldest_value_addr (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, VOIDmode, ADDR_SPACE_GENERIC, insn, vd); else if (REG_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_reg (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, insn, vd); else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); } else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); /* If we performed any replacement, update match_dups. */ if (replaced[i]) { int j; rtx new_rtx; new_rtx = *recog_data.operand_loc[i]; recog_data.operand[i] = new_rtx; for (j = 0; j < recog_data.n_dups; j++) if (recog_data.dup_num[j] == i) validate_unshare_change (insn, recog_data.dup_loc[j], new_rtx, 1); any_replacements = true; } } if (any_replacements) { if (! apply_change_group ()) { for (i = 0; i < n_ops; i++) if (replaced[i]) { rtx old = *recog_data.operand_loc[i]; recog_data.operand[i] = old; } if (dump_file) fprintf (dump_file, "insn %u: reg replacements not verified\n", INSN_UID (insn)); } else changed = true; } did_replacement: if (changed) { anything_changed = true; /* If something changed, perhaps further changes to earlier DEBUG_INSNs can be applied. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); } ksvd.vd = vd; ksvd.ignore_set_reg = NULL_RTX; /* Clobber call-clobbered registers. */ if (CALL_P (insn)) { unsigned int set_regno = INVALID_REGNUM; unsigned int set_nregs = 0; unsigned int regno; rtx exp; hard_reg_set_iterator hrsi; for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1)) { rtx x = XEXP (exp, 0); if (GET_CODE (x) == SET) { rtx dest = SET_DEST (x); kill_value (dest, vd); set_value_regno (REGNO (dest), GET_MODE (dest), vd); copy_value (dest, SET_SRC (x), vd); ksvd.ignore_set_reg = dest; set_regno = REGNO (dest); set_nregs = hard_regno_nregs[set_regno][GET_MODE (dest)]; break; } } EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi) if (regno < set_regno || regno >= set_regno + set_nregs) kill_value_regno (regno, 1, vd); /* If SET was seen in CALL_INSN_FUNCTION_USAGE, and SET_SRC of the SET isn't in regs_invalidated_by_call hard reg set, but instead among CLOBBERs on the CALL_INSN, we could wrongly assume the value in it is still live. */ if (ksvd.ignore_set_reg) note_stores (PATTERN (insn), kill_clobbered_value, vd); } /* Notice stores. */ note_stores (PATTERN (insn), kill_set_value, &ksvd); /* Notice copies. */ if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set))) copy_value (SET_DEST (set), SET_SRC (set), vd); if (insn == BB_END (bb)) break; } return anything_changed; }
basic_block cfg_layout_duplicate_bb (basic_block bb, edge e) { rtx insn; edge s, n; basic_block new_bb; gcov_type new_count = e ? e->count : 0; if (bb->count < new_count) new_count = bb->count; if (!bb->pred) abort (); #ifdef ENABLE_CHECKING if (!cfg_layout_can_duplicate_bb_p (bb)) abort (); #endif insn = duplicate_insn_chain (BB_HEAD (bb), BB_END (bb)); new_bb = create_basic_block (insn, insn ? get_last_insn () : NULL, EXIT_BLOCK_PTR->prev_bb); if (bb->rbi->header) { insn = bb->rbi->header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); insn = duplicate_insn_chain (bb->rbi->header, insn); if (insn) new_bb->rbi->header = unlink_insn_chain (insn, get_last_insn ()); } if (bb->rbi->footer) { insn = bb->rbi->footer; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); insn = duplicate_insn_chain (bb->rbi->footer, insn); if (insn) new_bb->rbi->footer = unlink_insn_chain (insn, get_last_insn ()); } if (bb->global_live_at_start) { new_bb->global_live_at_start = OBSTACK_ALLOC_REG_SET (&flow_obstack); new_bb->global_live_at_end = OBSTACK_ALLOC_REG_SET (&flow_obstack); COPY_REG_SET (new_bb->global_live_at_start, bb->global_live_at_start); COPY_REG_SET (new_bb->global_live_at_end, bb->global_live_at_end); } new_bb->loop_depth = bb->loop_depth; new_bb->flags = bb->flags; for (s = bb->succ; s; s = s->succ_next) { /* Since we are creating edges from a new block to successors of another block (which therefore are known to be disjoint), there is no need to actually check for duplicated edges. */ n = unchecked_make_edge (new_bb, s->dest, s->flags); n->probability = s->probability; if (e && bb->count) { /* Take care for overflows! */ n->count = s->count * (new_count * 10000 / bb->count) / 10000; s->count -= n->count; } else n->count = s->count; n->aux = s->aux; } if (e) { new_bb->count = new_count; bb->count -= new_count; new_bb->frequency = EDGE_FREQUENCY (e); bb->frequency -= EDGE_FREQUENCY (e); redirect_edge_and_branch_force (e, new_bb); if (bb->count < 0) bb->count = 0; if (bb->frequency < 0) bb->frequency = 0; } else { new_bb->count = bb->count; new_bb->frequency = bb->frequency; } new_bb->rbi->original = bb; bb->rbi->copy = new_bb; return new_bb; }
static void combine_stack_adjustments_for_block (basic_block bb) { HOST_WIDE_INT last_sp_adjust = 0; rtx last_sp_set = NULL_RTX; rtx last2_sp_set = NULL_RTX; struct csa_reflist *reflist = NULL; rtx insn, next, set; struct record_stack_refs_data data; bool end_of_block = false; for (insn = BB_HEAD (bb); !end_of_block ; insn = next) { end_of_block = insn == BB_END (bb); next = NEXT_INSN (insn); if (! INSN_P (insn)) continue; set = single_set_for_csa (insn); if (set) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); /* Find constant additions to the stack pointer. */ if (dest == stack_pointer_rtx && GET_CODE (src) == PLUS && XEXP (src, 0) == stack_pointer_rtx && CONST_INT_P (XEXP (src, 1))) { HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1)); /* If we've not seen an adjustment previously, record it now and continue. */ if (! last_sp_set) { last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* If not all recorded refs can be adjusted, or the adjustment is now too large for a constant addition, we cannot merge the two stack adjustments. Also we need to be careful to not move stack pointer such that we create stack accesses outside the allocated area. We can combine an allocation into the first insn, or a deallocation into the second insn. We can not combine an allocation followed by a deallocation. The only somewhat frequent occurrence of the later is when a function allocates a stack frame but does not use it. For this case, we would need to analyze rtl stream to be sure that allocated area is really unused. This means not only checking the memory references, but also all registers or global memory references possibly containing a stack frame address. Perhaps the best way to address this problem is to teach gcc not to allocate stack for objects never used. */ /* Combine an allocation into the first instruction. */ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) { if (try_apply_stack_adjustment (last_sp_set, reflist, last_sp_adjust + this_adjust, this_adjust)) { /* It worked! */ maybe_move_args_size_note (last_sp_set, insn, false); delete_insn (insn); last_sp_adjust += this_adjust; continue; } } /* Otherwise we have a deallocation. Do not combine with a previous allocation. Combine into the second insn. */ else if (STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) { if (try_apply_stack_adjustment (insn, reflist, last_sp_adjust + this_adjust, -last_sp_adjust)) { /* It worked! */ maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); last_sp_set = insn; last_sp_adjust += this_adjust; free_csa_reflist (reflist); reflist = NULL; continue; } } /* Combination failed. Restart processing from here. If deallocation+allocation conspired to cancel, we can delete the old deallocation insn. */ if (last_sp_set) { if (last_sp_adjust == 0) { maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); } else last2_sp_set = last_sp_set; } free_csa_reflist (reflist); reflist = NULL; last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* Find a store with pre-(dec|inc)rement or pre-modify of exactly the previous adjustment and turn it into a simple store. This is equivalent to anticipating the stack adjustment so this must be an allocation. */ if (MEM_P (dest) && ((STACK_GROWS_DOWNWARD ? (GET_CODE (XEXP (dest, 0)) == PRE_DEC && last_sp_adjust == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))) : (GET_CODE (XEXP (dest, 0)) == PRE_INC && last_sp_adjust == -(HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) || ((STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) && GET_CODE (XEXP (dest, 0)) == PRE_MODIFY && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx && GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == CONST_INT && INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == -last_sp_adjust)) && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx && !reg_mentioned_p (stack_pointer_rtx, src) && memory_address_p (GET_MODE (dest), stack_pointer_rtx) && try_apply_stack_adjustment (insn, reflist, 0, -last_sp_adjust)) { if (last2_sp_set) maybe_move_args_size_note (last2_sp_set, last_sp_set, false); else maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); free_csa_reflist (reflist); reflist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; continue; } } data.insn = insn; data.reflist = reflist; if (!CALL_P (insn) && last_sp_set && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data)) { reflist = data.reflist; continue; } reflist = data.reflist; /* Otherwise, we were not able to process the instruction. Do not continue collecting data across such a one. */ if (last_sp_set && (CALL_P (insn) || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) { if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } free_csa_reflist (reflist); reflist = NULL; last2_sp_set = NULL_RTX; last_sp_set = NULL_RTX; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } if (reflist) free_csa_reflist (reflist); }
static void fixup_reorder_chain (void) { basic_block bb, prev_bb; int index; rtx insn = NULL; if (cfg_layout_function_header) { set_first_insn (cfg_layout_function_header); insn = cfg_layout_function_header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } /* First do the bulk reordering -- rechain the blocks without regard to the needed changes to jumps and labels. */ for (bb = ENTRY_BLOCK_PTR->next_bb, index = 0; bb != 0; bb = bb->rbi->next, index++) { if (bb->rbi->header) { if (insn) NEXT_INSN (insn) = bb->rbi->header; else set_first_insn (bb->rbi->header); PREV_INSN (bb->rbi->header) = insn; insn = bb->rbi->header; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } if (insn) NEXT_INSN (insn) = BB_HEAD (bb); else set_first_insn (BB_HEAD (bb)); PREV_INSN (BB_HEAD (bb)) = insn; insn = BB_END (bb); if (bb->rbi->footer) { NEXT_INSN (insn) = bb->rbi->footer; PREV_INSN (bb->rbi->footer) = insn; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); } } if (index != n_basic_blocks) abort (); NEXT_INSN (insn) = cfg_layout_function_footer; if (cfg_layout_function_footer) PREV_INSN (cfg_layout_function_footer) = insn; while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); set_last_insn (insn); #ifdef ENABLE_CHECKING verify_insn_chain (); #endif delete_dead_jumptables (); /* Now add jumps and labels as needed to match the blocks new outgoing edges. */ for (bb = ENTRY_BLOCK_PTR->next_bb; bb ; bb = bb->rbi->next) { edge e_fall, e_taken, e; rtx bb_end_insn; basic_block nb; if (bb->succ == NULL) continue; /* Find the old fallthru edge, and another non-EH edge for a taken jump. */ e_taken = e_fall = NULL; for (e = bb->succ; e ; e = e->succ_next) if (e->flags & EDGE_FALLTHRU) e_fall = e; else if (! (e->flags & EDGE_EH)) e_taken = e; bb_end_insn = BB_END (bb); if (GET_CODE (bb_end_insn) == JUMP_INSN) { if (any_condjump_p (bb_end_insn)) { /* If the old fallthru is still next, nothing to do. */ if (bb->rbi->next == e_fall->dest || (!bb->rbi->next && e_fall->dest == EXIT_BLOCK_PTR)) continue; /* The degenerated case of conditional jump jumping to the next instruction can happen on target having jumps with side effects. Create temporarily the duplicated edge representing branch. It will get unidentified by force_nonfallthru_and_redirect that would otherwise get confused by fallthru edge not pointing to the next basic block. */ if (!e_taken) { rtx note; edge e_fake; e_fake = unchecked_make_edge (bb, e_fall->dest, 0); if (!redirect_jump (BB_END (bb), block_label (bb), 0)) abort (); note = find_reg_note (BB_END (bb), REG_BR_PROB, NULL_RTX); if (note) { int prob = INTVAL (XEXP (note, 0)); e_fake->probability = prob; e_fake->count = e_fall->count * prob / REG_BR_PROB_BASE; e_fall->probability -= e_fall->probability; e_fall->count -= e_fake->count; if (e_fall->probability < 0) e_fall->probability = 0; if (e_fall->count < 0) e_fall->count = 0; } } /* There is one special case: if *neither* block is next, such as happens at the very end of a function, then we'll need to add a new unconditional jump. Choose the taken edge based on known or assumed probability. */ else if (bb->rbi->next != e_taken->dest) { rtx note = find_reg_note (bb_end_insn, REG_BR_PROB, 0); if (note && INTVAL (XEXP (note, 0)) < REG_BR_PROB_BASE / 2 && invert_jump (bb_end_insn, label_for_bb (e_fall->dest), 0)) { e_fall->flags &= ~EDGE_FALLTHRU; e_taken->flags |= EDGE_FALLTHRU; update_br_prob_note (bb); e = e_fall, e_fall = e_taken, e_taken = e; } } /* Otherwise we can try to invert the jump. This will basically never fail, however, keep up the pretense. */ else if (invert_jump (bb_end_insn, label_for_bb (e_fall->dest), 0)) { e_fall->flags &= ~EDGE_FALLTHRU; e_taken->flags |= EDGE_FALLTHRU; update_br_prob_note (bb); continue; } } else if (returnjump_p (bb_end_insn)) continue; else { /* Otherwise we have some switch or computed jump. In the 99% case, there should not have been a fallthru edge. */ if (! e_fall) continue; #ifdef CASE_DROPS_THROUGH /* Except for VAX. Since we didn't have predication for the tablejump, the fallthru block should not have moved. */ if (bb->rbi->next == e_fall->dest) continue; bb_end_insn = skip_insns_after_block (bb); #else abort (); #endif } } else { /* No fallthru implies a noreturn function with EH edges, or something similarly bizarre. In any case, we don't need to do anything. */ if (! e_fall) continue; /* If the fallthru block is still next, nothing to do. */ if (bb->rbi->next == e_fall->dest) continue; /* A fallthru to exit block. */ if (!bb->rbi->next && e_fall->dest == EXIT_BLOCK_PTR) continue; } /* We got here if we need to add a new jump insn. */ nb = force_nonfallthru (e_fall); if (nb) { cfg_layout_initialize_rbi (nb); nb->rbi->visited = 1; nb->rbi->next = bb->rbi->next; bb->rbi->next = nb; /* Don't process this new block. */ bb = nb; } } /* Put basic_block_info in the new order. */ if (rtl_dump_file) { fprintf (rtl_dump_file, "Reordered sequence:\n"); for (bb = ENTRY_BLOCK_PTR->next_bb, index = 0; bb; bb = bb->rbi->next, index ++) { fprintf (rtl_dump_file, " %i ", index); if (bb->rbi->original) fprintf (rtl_dump_file, "duplicate of %i ", bb->rbi->original->index); else if (forwarder_block_p (bb) && GET_CODE (BB_HEAD (bb)) != CODE_LABEL) fprintf (rtl_dump_file, "compensation "); else fprintf (rtl_dump_file, "bb %i ", bb->index); fprintf (rtl_dump_file, " [%i]\n", bb->frequency); } } prev_bb = ENTRY_BLOCK_PTR; bb = ENTRY_BLOCK_PTR->next_bb; index = 0; for (; bb; prev_bb = bb, bb = bb->rbi->next, index ++) { bb->index = index; BASIC_BLOCK (index) = bb; bb->prev_bb = prev_bb; prev_bb->next_bb = bb; } prev_bb->next_bb = EXIT_BLOCK_PTR; EXIT_BLOCK_PTR->prev_bb = prev_bb; /* Annoying special case - jump around dead jumptables left in the code. */ FOR_EACH_BB (bb) { edge e; for (e = bb->succ; e && !(e->flags & EDGE_FALLTHRU); e = e->succ_next) continue; if (e && !can_fallthru (e->src, e->dest)) force_nonfallthru (e); } }
void find_comparison_dom_walker::before_dom_children (basic_block bb) { struct comparison *last_cmp; rtx insn, next, last_clobber; bool last_cmp_valid; bool need_purge = false; bitmap killed; killed = BITMAP_ALLOC (NULL); /* The last comparison that was made. Will be reset to NULL once the flags are clobbered. */ last_cmp = NULL; /* True iff the last comparison has not been clobbered, nor have its inputs. Used to eliminate duplicate compares. */ last_cmp_valid = false; /* The last insn that clobbered the flags, if that insn is of a form that may be valid for eliminating a following compare. To be reset to NULL once the flags are set otherwise. */ last_clobber = NULL; /* Propagate the last live comparison throughout the extended basic block. */ if (single_pred_p (bb)) { last_cmp = (struct comparison *) single_pred (bb)->aux; if (last_cmp) last_cmp_valid = last_cmp->inputs_valid; } for (insn = BB_HEAD (bb); insn; insn = next) { rtx src; next = (insn == BB_END (bb) ? NULL_RTX : NEXT_INSN (insn)); if (!NONDEBUG_INSN_P (insn)) continue; /* Compute the set of registers modified by this instruction. */ bitmap_clear (killed); df_simulate_find_defs (insn, killed); src = conforming_compare (insn); if (src) { enum machine_mode src_mode = GET_MODE (src); rtx eh_note = NULL; if (flag_non_call_exceptions) eh_note = find_reg_note (insn, REG_EH_REGION, NULL); if (!last_cmp_valid) goto dont_delete; /* Take care that it's in the same EH region. */ if (flag_non_call_exceptions && !rtx_equal_p (eh_note, last_cmp->eh_note)) goto dont_delete; /* Make sure the compare is redundant with the previous. */ if (!rtx_equal_p (last_cmp->in_a, XEXP (src, 0)) || !rtx_equal_p (last_cmp->in_b, XEXP (src, 1))) goto dont_delete; /* New mode must be compatible with the previous compare mode. */ { enum machine_mode new_mode = targetm.cc_modes_compatible (last_cmp->orig_mode, src_mode); if (new_mode == VOIDmode) goto dont_delete; if (new_mode != last_cmp->orig_mode) { rtx x, flags = gen_rtx_REG (src_mode, targetm.flags_regnum); /* Generate new comparison for substitution. */ x = gen_rtx_COMPARE (new_mode, XEXP (src, 0), XEXP (src, 1)); x = gen_rtx_SET (VOIDmode, flags, x); if (!validate_change (last_cmp->insn, &PATTERN (last_cmp->insn), x, false)) goto dont_delete; last_cmp->orig_mode = new_mode; } } /* All tests and substitutions succeeded! */ if (eh_note) need_purge = true; delete_insn (insn); continue; dont_delete: last_cmp = XCNEW (struct comparison); last_cmp->insn = insn; last_cmp->prev_clobber = last_clobber; last_cmp->in_a = XEXP (src, 0); last_cmp->in_b = XEXP (src, 1); last_cmp->eh_note = eh_note; last_cmp->orig_mode = src_mode; all_compares.safe_push (last_cmp); /* It's unusual, but be prepared for comparison patterns that also clobber an input, or perhaps a scratch. */ last_clobber = NULL; last_cmp_valid = true; } /* Notice if this instruction kills the flags register. */ else if (bitmap_bit_p (killed, targetm.flags_regnum)) { /* See if this insn could be the "clobber" that eliminates a future comparison. */ last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL); /* In either case, the previous compare is no longer valid. */ last_cmp = NULL; last_cmp_valid = false; continue; } /* Notice if this instruction uses the flags register. */ else if (last_cmp) find_flags_uses_in_insn (last_cmp, insn); /* Notice if any of the inputs to the comparison have changed. */ if (last_cmp_valid && (bitmap_bit_p (killed, REGNO (last_cmp->in_a)) || (REG_P (last_cmp->in_b) && bitmap_bit_p (killed, REGNO (last_cmp->in_b))))) last_cmp_valid = false; }
static rtx skip_insns_after_block (basic_block bb) { rtx insn, last_insn, next_head, prev; next_head = NULL_RTX; if (bb->next_bb != EXIT_BLOCK_PTR) next_head = BB_HEAD (bb->next_bb); for (last_insn = insn = BB_END (bb); (insn = NEXT_INSN (insn)) != 0; ) { if (insn == next_head) break; switch (GET_CODE (insn)) { case BARRIER: last_insn = insn; continue; case NOTE: switch (NOTE_LINE_NUMBER (insn)) { case NOTE_INSN_LOOP_END: case NOTE_INSN_BLOCK_END: last_insn = insn; continue; case NOTE_INSN_DELETED: case NOTE_INSN_DELETED_LABEL: continue; default: continue; break; } break; case CODE_LABEL: if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN && (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC || GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC)) { insn = NEXT_INSN (insn); last_insn = insn; continue; } break; default: break; } break; } /* It is possible to hit contradictory sequence. For instance: jump_insn NOTE_INSN_LOOP_BEG barrier Where barrier belongs to jump_insn, but the note does not. This can be created by removing the basic block originally following NOTE_INSN_LOOP_BEG. In such case reorder the notes. */ for (insn = last_insn; insn != BB_END (bb); insn = prev) { prev = PREV_INSN (insn); if (GET_CODE (insn) == NOTE) switch (NOTE_LINE_NUMBER (insn)) { case NOTE_INSN_LOOP_END: case NOTE_INSN_BLOCK_END: case NOTE_INSN_DELETED: case NOTE_INSN_DELETED_LABEL: continue; default: reorder_insns (insn, insn, last_insn); } } return last_insn; }
void debug_bb_slim (struct basic_block_def *bb) { print_rtl_slim (stderr, BB_HEAD (bb), BB_END (bb), -1, 32); }
static basic_block expand_gimple_basic_block (basic_block bb, FILE * dump_file) { block_stmt_iterator bsi = bsi_start (bb); tree stmt = NULL; rtx note, last; edge e; edge_iterator ei; if (dump_file) { fprintf (dump_file, "\n;; Generating RTL for tree basic block %d\n", bb->index); } if (!bsi_end_p (bsi)) stmt = bsi_stmt (bsi); if (stmt && TREE_CODE (stmt) == LABEL_EXPR) { last = get_last_insn (); expand_expr_stmt (stmt); /* Java emits line number notes in the top of labels. ??? Make this go away once line number notes are obsoleted. */ BB_HEAD (bb) = NEXT_INSN (last); if (NOTE_P (BB_HEAD (bb))) BB_HEAD (bb) = NEXT_INSN (BB_HEAD (bb)); bsi_next (&bsi); note = emit_note_after (NOTE_INSN_BASIC_BLOCK, BB_HEAD (bb)); maybe_dump_rtl_for_tree_stmt (stmt, last); } else note = BB_HEAD (bb) = emit_note (NOTE_INSN_BASIC_BLOCK); NOTE_BASIC_BLOCK (note) = bb; for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) { /* Clear EDGE_EXECUTABLE. This flag is never used in the backend. */ e->flags &= ~EDGE_EXECUTABLE; /* At the moment not all abnormal edges match the RTL representation. It is safe to remove them here as find_sub_basic_blocks will rediscover them. In the future we should get this fixed properly. */ if (e->flags & EDGE_ABNORMAL) remove_edge (e); else ei_next (&ei); } for (; !bsi_end_p (bsi); bsi_next (&bsi)) { tree stmt = bsi_stmt (bsi); basic_block new_bb; if (!stmt) continue; /* Expand this statement, then evaluate the resulting RTL and fixup the CFG accordingly. */ if (TREE_CODE (stmt) == COND_EXPR) { new_bb = expand_gimple_cond_expr (bb, stmt); if (new_bb) return new_bb; } else { tree call = get_call_expr_in (stmt); if (call && CALL_EXPR_TAILCALL (call)) { bool can_fallthru; new_bb = expand_gimple_tailcall (bb, stmt, &can_fallthru); if (new_bb) { if (can_fallthru) bb = new_bb; else return new_bb; } } else { last = get_last_insn (); expand_expr_stmt (stmt); maybe_dump_rtl_for_tree_stmt (stmt, last); } } } do_pending_stack_adjust (); /* Find the block tail. The last insn in the block is the insn before a barrier and/or table jump insn. */ last = get_last_insn (); if (BARRIER_P (last)) last = PREV_INSN (last); if (JUMP_TABLE_DATA_P (last)) last = PREV_INSN (PREV_INSN (last)); BB_END (bb) = last; update_bb_for_insn (bb); return bb; }
static void test_expansion_to_rtl () { /* As above, construct a trivial function, gimplify it, build a CFG, and convert to SSA: */ tree fndecl = build_trivial_high_gimple_function (); function *fun = DECL_STRUCT_FUNCTION (fndecl); ASSERT_TRUE (fun != NULL); build_cfg (fndecl); convert_to_ssa (fndecl); /* We need a cgraph_node for it. */ cgraph_node::get_create (fndecl); /* Normally, cgraph_node::expand () would call init_function_start (and a bunch of other stuff), and invoke the expand pass, but it also runs all of the other passes. So just do the minimum needed to get from gimple-SSA to RTL. */ rtl_opt_pass *expand_pass = make_pass_expand (g); push_cfun (fun); init_function_start (fndecl); expand_pass->execute (fun); pop_cfun (); /* On x86_64, I get this: (note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) (note 2 3 5 2 NOTE_INSN_FUNCTION_BEG) (insn 5 2 6 2 (set (reg:SI 87 [ D.59 ]) (const_int 42 [0x2a])) -1 (nil)) (insn 6 5 10 2 (set (reg:SI 88 [ <retval> ]) (reg:SI 87 [ D.59 ])) -1 (nil)) (insn 10 6 11 2 (set (reg/i:SI 0 ax) (reg:SI 88 [ <retval> ])) -1 (nil)) (insn 11 10 0 2 (use (reg/i:SI 0 ax)) -1 (nil)) On cr16-elf I get this: (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) (insn 2 4 3 2 (set (reg:SI 24) (reg/f:SI 16 virtual-incoming-args)) -1 (nil)) (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG) (insn 6 3 7 2 (set (reg:HI 22 [ _1 ]) (const_int 42 [0x2a])) -1 (nil)) (insn 7 6 11 2 (set (reg:HI 23 [ <retval> ]) (reg:HI 22 [ _1 ])) -1 (nil)) (insn 11 7 12 2 (set (reg/i:HI 0 r0) (reg:HI 23 [ <retval> ])) -1 (nil)) (insn 12 11 0 2 (use (reg/i:HI 0 r0)) -1 (nil)). */ verify_three_block_rtl_cfg (fun); /* Verify as much of the RTL as we can whilst avoiding target-specific behavior. */ basic_block bb2 = get_real_block (fun); /* Expect a NOTE_INSN_BASIC_BLOCK... */ rtx_insn *insn = BB_HEAD (bb2); ASSERT_TRUE (insn != NULL); ASSERT_EQ (NOTE, insn->code); ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (insn)); ASSERT_EQ (bb2, NOTE_BASIC_BLOCK (insn)); /* ...etc; any further checks are likely to over-specify things and run us into target dependencies. */ }
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 int optimize_mode_switching (void) { int e; basic_block bb; bool need_commit = false; static const int num_modes[] = NUM_MODES_FOR_MODE_SWITCHING; #define N_ENTITIES ARRAY_SIZE (num_modes) int entity_map[N_ENTITIES]; struct bb_info *bb_info[N_ENTITIES]; int i, j; int n_entities = 0; int max_num_modes = 0; bool emitted ATTRIBUTE_UNUSED = false; basic_block post_entry = 0; basic_block pre_exit = 0; struct edge_list *edge_list = 0; /* These bitmaps are used for the LCM algorithm. */ sbitmap *kill, *del, *insert, *antic, *transp, *comp; sbitmap *avin, *avout; for (e = N_ENTITIES - 1; e >= 0; e--) if (OPTIMIZE_MODE_SWITCHING (e)) { int entry_exit_extra = 0; /* Create the list of segments within each basic block. If NORMAL_MODE is defined, allow for two extra blocks split from the entry and exit block. */ if (targetm.mode_switching.entry && targetm.mode_switching.exit) entry_exit_extra = 3; bb_info[n_entities] = XCNEWVEC (struct bb_info, last_basic_block_for_fn (cfun) + entry_exit_extra); entity_map[n_entities++] = e; if (num_modes[e] > max_num_modes) max_num_modes = num_modes[e]; } if (! n_entities) return 0; /* Make sure if MODE_ENTRY is defined MODE_EXIT is defined. */ gcc_assert ((targetm.mode_switching.entry && targetm.mode_switching.exit) || (!targetm.mode_switching.entry && !targetm.mode_switching.exit)); if (targetm.mode_switching.entry && targetm.mode_switching.exit) { /* Split the edge from the entry block, so that we can note that there NORMAL_MODE is supplied. */ post_entry = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun))); pre_exit = create_pre_exit (n_entities, entity_map, num_modes); } df_analyze (); /* Create the bitmap vectors. */ antic = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); comp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); kill = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities * max_num_modes); bitmap_vector_ones (transp, last_basic_block_for_fn (cfun)); bitmap_vector_clear (antic, last_basic_block_for_fn (cfun)); bitmap_vector_clear (comp, last_basic_block_for_fn (cfun)); for (j = n_entities - 1; j >= 0; j--) { int e = entity_map[j]; int no_mode = num_modes[e]; struct bb_info *info = bb_info[j]; rtx_insn *insn; /* Determine what the first use (if any) need for a mode of entity E is. This will be the mode that is anticipatable for this block. Also compute the initial transparency settings. */ FOR_EACH_BB_FN (bb, cfun) { struct seginfo *ptr; int last_mode = no_mode; bool any_set_required = false; HARD_REG_SET live_now; info[bb->index].mode_out = info[bb->index].mode_in = no_mode; REG_SET_TO_HARD_REG_SET (live_now, df_get_live_in (bb)); /* Pretend the mode is clobbered across abnormal edges. */ { edge_iterator ei; edge eg; FOR_EACH_EDGE (eg, ei, bb->preds) if (eg->flags & EDGE_COMPLEX) break; if (eg) { rtx_insn *ins_pos = BB_HEAD (bb); if (LABEL_P (ins_pos)) ins_pos = NEXT_INSN (ins_pos); gcc_assert (NOTE_INSN_BASIC_BLOCK_P (ins_pos)); if (ins_pos != BB_END (bb)) ins_pos = NEXT_INSN (ins_pos); ptr = new_seginfo (no_mode, ins_pos, bb->index, live_now); add_seginfo (info + bb->index, ptr); for (i = 0; i < no_mode; i++) clear_mode_bit (transp[bb->index], j, i); } } FOR_BB_INSNS (bb, insn) { if (INSN_P (insn)) { int mode = targetm.mode_switching.needed (e, insn); rtx link; if (mode != no_mode && mode != last_mode) { any_set_required = true; last_mode = mode; ptr = new_seginfo (mode, insn, bb->index, live_now); add_seginfo (info + bb->index, ptr); for (i = 0; i < no_mode; i++) clear_mode_bit (transp[bb->index], j, i); } if (targetm.mode_switching.after) last_mode = targetm.mode_switching.after (e, last_mode, insn); /* Update LIVE_NOW. */ for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == REG_DEAD) reg_dies (XEXP (link, 0), &live_now); note_stores (PATTERN (insn), reg_becomes_live, &live_now); for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == REG_UNUSED) reg_dies (XEXP (link, 0), &live_now); } } info[bb->index].computing = last_mode; /* Check for blocks without ANY mode requirements. N.B. because of MODE_AFTER, last_mode might still be different from no_mode, in which case we need to mark the block as nontransparent. */ if (!any_set_required) { ptr = new_seginfo (no_mode, BB_END (bb), bb->index, live_now); add_seginfo (info + bb->index, ptr); if (last_mode != no_mode) for (i = 0; i < no_mode; i++) clear_mode_bit (transp[bb->index], j, i); } } if (targetm.mode_switching.entry && targetm.mode_switching.exit) { int mode = targetm.mode_switching.entry (e); info[post_entry->index].mode_out = info[post_entry->index].mode_in = no_mode; if (pre_exit) { info[pre_exit->index].mode_out = info[pre_exit->index].mode_in = no_mode; } if (mode != no_mode) { bb = post_entry; /* By always making this nontransparent, we save an extra check in make_preds_opaque. We also need this to avoid confusing pre_edge_lcm when antic is cleared but transp and comp are set. */ for (i = 0; i < no_mode; i++) clear_mode_bit (transp[bb->index], j, i); /* Insert a fake computing definition of MODE into entry blocks which compute no mode. This represents the mode on entry. */ info[bb->index].computing = mode; if (pre_exit) info[pre_exit->index].seginfo->mode = targetm.mode_switching.exit (e); } } /* Set the anticipatable and computing arrays. */ for (i = 0; i < no_mode; i++) { int m = targetm.mode_switching.priority (entity_map[j], i); FOR_EACH_BB_FN (bb, cfun) { if (info[bb->index].seginfo->mode == m) set_mode_bit (antic[bb->index], j, m); if (info[bb->index].computing == m) set_mode_bit (comp[bb->index], j, m); } } } /* Calculate the optimal locations for the placement mode switches to modes with priority I. */ FOR_EACH_BB_FN (bb, cfun) bitmap_not (kill[bb->index], transp[bb->index]); edge_list = pre_edge_lcm_avs (n_entities * max_num_modes, transp, comp, antic, kill, avin, avout, &insert, &del); for (j = n_entities - 1; j >= 0; j--) { int no_mode = num_modes[entity_map[j]]; /* Insert all mode sets that have been inserted by lcm. */ for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--) { edge eg = INDEX_EDGE (edge_list, ed); eg->aux = (void *)(intptr_t)-1; for (i = 0; i < no_mode; i++) { int m = targetm.mode_switching.priority (entity_map[j], i); if (mode_bit_p (insert[ed], j, m)) { eg->aux = (void *)(intptr_t)m; break; } } } FOR_EACH_BB_FN (bb, cfun) { struct bb_info *info = bb_info[j]; int last_mode = no_mode; /* intialize mode in availability for bb. */ for (i = 0; i < no_mode; i++) if (mode_bit_p (avout[bb->index], j, i)) { if (last_mode == no_mode) last_mode = i; if (last_mode != i) { last_mode = no_mode; break; } } info[bb->index].mode_out = last_mode; /* intialize mode out availability for bb. */ last_mode = no_mode; for (i = 0; i < no_mode; i++) if (mode_bit_p (avin[bb->index], j, i)) { if (last_mode == no_mode) last_mode = i; if (last_mode != i) { last_mode = no_mode; break; } } info[bb->index].mode_in = last_mode; for (i = 0; i < no_mode; i++) if (mode_bit_p (del[bb->index], j, i)) info[bb->index].seginfo->mode = no_mode; } /* Now output the remaining mode sets in all the segments. */ /* In case there was no mode inserted. the mode information on the edge might not be complete. Update mode info on edges and commit pending mode sets. */ need_commit |= commit_mode_sets (edge_list, entity_map[j], bb_info[j]); /* Reset modes for next entity. */ clear_aux_for_edges (); FOR_EACH_BB_FN (bb, cfun) { struct seginfo *ptr, *next; int cur_mode = bb_info[j][bb->index].mode_in; for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next) { next = ptr->next; if (ptr->mode != no_mode) { rtx_insn *mode_set; rtl_profile_for_bb (bb); start_sequence (); targetm.mode_switching.emit (entity_map[j], ptr->mode, cur_mode, ptr->regs_live); mode_set = get_insns (); end_sequence (); /* modes kill each other inside a basic block. */ cur_mode = ptr->mode; /* Insert MODE_SET only if it is nonempty. */ if (mode_set != NULL_RTX) { emitted = true; if (NOTE_INSN_BASIC_BLOCK_P (ptr->insn_ptr)) /* We need to emit the insns in a FIFO-like manner, i.e. the first to be emitted at our insertion point ends up first in the instruction steam. Because we made sure that NOTE_INSN_BASIC_BLOCK is only used for initially empty basic blocks, we can achieve this by appending at the end of the block. */ emit_insn_after (mode_set, BB_END (NOTE_BASIC_BLOCK (ptr->insn_ptr))); else emit_insn_before (mode_set, ptr->insn_ptr); } default_rtl_profile (); } free (ptr); } } free (bb_info[j]); } free_edge_list (edge_list); /* Finished. Free up all the things we've allocated. */ sbitmap_vector_free (del); sbitmap_vector_free (insert); sbitmap_vector_free (kill); sbitmap_vector_free (antic); sbitmap_vector_free (transp); sbitmap_vector_free (comp); sbitmap_vector_free (avin); sbitmap_vector_free (avout); if (need_commit) commit_edge_insertions (); if (targetm.mode_switching.entry && targetm.mode_switching.exit) cleanup_cfg (CLEANUP_NO_INSN_DEL); else if (!need_commit && !emitted) return 0; return 1; }
void optimize_sibling_and_tail_recursive_calls (void) { rtx insn, insns; basic_block alternate_exit = EXIT_BLOCK_PTR; bool no_sibcalls_this_function = false; bool successful_replacement = false; bool replaced_call_placeholder = false; edge e; insns = get_insns (); cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP); /* If there are no basic blocks, then there is nothing to do. */ if (n_basic_blocks == 0) return; /* If we are using sjlj exceptions, we may need to add a call to _Unwind_SjLj_Unregister at exit of the function. Which means that we cannot do any sibcall transformations. */ if (USING_SJLJ_EXCEPTIONS && current_function_has_exception_handlers ()) no_sibcalls_this_function = true; return_value_pseudo = NULL_RTX; /* Find the exit block. It is possible that we have blocks which can reach the exit block directly. However, most of the time a block will jump (or fall into) N_BASIC_BLOCKS - 1, which in turn falls into the exit block. */ for (e = EXIT_BLOCK_PTR->pred; e && alternate_exit == EXIT_BLOCK_PTR; e = e->pred_next) { rtx insn; if (e->dest != EXIT_BLOCK_PTR || e->succ_next != NULL) continue; /* Walk forwards through the last normal block and see if it does nothing except fall into the exit block. */ for (insn = BB_HEAD (EXIT_BLOCK_PTR->prev_bb); insn; insn = NEXT_INSN (insn)) { rtx set; /* This should only happen once, at the start of this block. */ if (GET_CODE (insn) == CODE_LABEL) continue; if (GET_CODE (insn) == NOTE) continue; if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE) continue; /* Exit block also may contain copy from pseudo containing return value to hard register. */ if (GET_CODE (insn) == INSN && (set = single_set (insn)) && SET_DEST (set) == current_function_return_rtx && REG_P (SET_SRC (set)) && !return_value_pseudo) { return_value_pseudo = SET_SRC (set); continue; } break; } /* If INSN is zero, then the search walked all the way through the block without hitting anything interesting. This block is a valid alternate exit block. */ if (insn == NULL) alternate_exit = e->src; else return_value_pseudo = NULL; } /* If the function uses ADDRESSOF, we can't (easily) determine at this point if the value will end up on the stack. */ no_sibcalls_this_function |= sequence_uses_addressof (insns); /* Walk the insn chain and find any CALL_PLACEHOLDER insns. We need to select one of the insn sequences attached to each CALL_PLACEHOLDER. The different sequences represent different ways to implement the call, ie, tail recursion, sibling call or normal call. Since we do not create nested CALL_PLACEHOLDERs, the scan continues with the insn that was after a replaced CALL_PLACEHOLDER; we don't rescan the replacement insns. */ for (insn = insns; insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == CALL_INSN && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) { int sibcall = (XEXP (PATTERN (insn), 1) != NULL_RTX); int tailrecursion = (XEXP (PATTERN (insn), 2) != NULL_RTX); basic_block call_block = BLOCK_FOR_INSN (insn); /* alloca (until we have stack slot life analysis) inhibits sibling call optimizations, but not tail recursion. Similarly if we use varargs or stdarg since they implicitly may take the address of an argument. */ if (current_function_calls_alloca || current_function_stdarg) sibcall = 0; /* See if there are any reasons we can't perform either sibling or tail call optimizations. We must be careful with stack slots which are live at potential optimization sites. */ if (no_sibcalls_this_function /* ??? Overly conservative. */ || frame_offset /* Any function that calls setjmp might have longjmp called from any called function. ??? We really should represent this properly in the CFG so that this needn't be special cased. */ || current_function_calls_setjmp /* Can't if more than one successor or single successor is not exit block. These two tests prevent tail call optimization in the presence of active exception handlers. */ || call_block->succ == NULL || call_block->succ->succ_next != NULL || (call_block->succ->dest != EXIT_BLOCK_PTR && call_block->succ->dest != alternate_exit) /* If this call doesn't end the block, there are operations at the end of the block which we must execute after returning. */ || ! call_ends_block_p (insn, BB_END (call_block))) sibcall = 0, tailrecursion = 0; /* Select a set of insns to implement the call and emit them. Tail recursion is the most efficient, so select it over a tail/sibling call. */ if (sibcall || tailrecursion) successful_replacement = true; replaced_call_placeholder = true; replace_call_placeholder (insn, tailrecursion != 0 ? sibcall_use_tail_recursion : sibcall != 0 ? sibcall_use_sibcall : sibcall_use_normal); } } if (successful_replacement) { rtx insn; tree arg; /* A sibling call sequence invalidates any REG_EQUIV notes made for this function's incoming arguments. At the start of RTL generation we know the only REG_EQUIV notes in the rtl chain are those for incoming arguments, so we can safely flush any REG_EQUIV note. This is (slight) overkill. We could keep track of the highest argument we clobber and be more selective in removing notes, but it does not seem to be worth the effort. */ purge_reg_equiv_notes (); /* A sibling call sequence also may invalidate RTX_UNCHANGING_P flag of some incoming arguments MEM RTLs, because it can write into those slots. We clear all those bits now. This is (slight) overkill, we could keep track of which arguments we actually write into. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { if (INSN_P (insn)) purge_mem_unchanging_flag (PATTERN (insn)); } /* Similarly, invalidate RTX_UNCHANGING_P for any incoming arguments passed in registers. */ for (arg = DECL_ARGUMENTS (current_function_decl); arg; arg = TREE_CHAIN (arg)) { if (REG_P (DECL_RTL (arg))) RTX_UNCHANGING_P (DECL_RTL (arg)) = false; } } /* There may have been NOTE_INSN_BLOCK_{BEGIN,END} notes in the CALL_PLACEHOLDER alternatives that we didn't emit. Rebuild the lexical block tree to correspond to the notes that still exist. */ if (replaced_call_placeholder) reorder_blocks (); /* This information will be invalid after inline expansion. Kill it now. */ free_basic_block_vars (0); free_EXPR_LIST_list (&tail_recursion_label_list); }