/* Register renaming for the GNU compiler. Copyright (C) 2000 Free Software Foundation, Inc. This file is part of GNU CC. GNU CC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "tree.h" #include "rtl.h" #include "hard-reg-set.h" #include "basic-block.h" #include "insn-config.h" #include "regs.h" #include "flags.h" #include "output.h" #include "function.h" #include "recog.h" #include "resource.h" static const char *const reg_class_names[] = REG_CLASS_NAMES; /* ??? Consider a more sparse data structure? */ typedef struct def_uses { /* high bound of defs and uses */ int high_bound; /* 1 if insn y defines a reg whose use crosses a call y is the ordinal position of the insn within the block */ sbitmap require_call_save_reg; /* REGNO x INSN y 1 if insn y sets reg x */ sbitmap *defs; /* REGNO x INSN y The register class for this def */ enum reg_class *def_class; /* REGNO x INSN y 1 if insn y uses reg x */ sbitmap *uses; /* REGNO x INSN y The register class for this use */ enum reg_class *use_class; } def_uses; #define DU_REG_CLASS(rc,r,high_bound,i) (rc[r * high_bound + i]) typedef struct ext_basic_blocks { /* n_basic_blocks x n_basic_blocks y 1 if bb y is in extended bb having entry x */ sbitmap *basic_block; /* n_basic_blocks x n_basic_blocks y 1 if bb y is an exit block */ sbitmap *exit; } ext_basic_blocks; #define UID_RUID_HIGH_BOUND 64 #define DESTINATION 1 #define SOURCE 2 static void build_def_use PARAMS ((int, ext_basic_blocks *, HARD_REG_SET *, def_uses *, sbitmap *)); static int replace_reg_in_block PARAMS ((def_uses *, varray_type *, int, rtx, unsigned int)); static int consider_def PARAMS ((rtx, int, def_uses *, int)); static int consider_available PARAMS ((rtx, int, HARD_REG_SET *, int, def_uses *, int)); static rtx rr_replace_reg PARAMS ((rtx, rtx, rtx, int, rtx, int *)); static int consider_use PARAMS ((rtx, int, int, int)); static int condmove_p PARAMS ((rtx)); static void dump_def_use_chain PARAMS ((HARD_REG_SET *, def_uses *, varray_type *)); static void dump_ext_bb_info PARAMS ((int, ext_basic_blocks *)); static void find_ext_basic_blocks PARAMS ((ext_basic_blocks *)); static void find_one_ext_basic_block PARAMS ((int, basic_block, sbitmap *, ext_basic_blocks *)); static enum reg_class get_reg_class PARAMS ((rtx, rtx, int, enum reg_class)); static rtx regno_first_use_in PARAMS ((unsigned int, rtx)); void regrename_optimize () { int b, eb, i, inum, r, rc, replace_ok; rtx insn; def_uses du; ext_basic_blocks ebb; /* Registers used in a given class */ HARD_REG_SET class_regs; /* Registers available for use as renaming registers */ HARD_REG_SET avail_regs; /* Registers used in the block */ HARD_REG_SET regs_used; /* Registers which have been used as renaming registers */ HARD_REG_SET renamed_regs; HARD_REG_SET global_live_at_end, global_live_at_start; HARD_REG_SET null_bitmap, tmp_bitmap; /* 1 if insn y sets a register which is live at the end of the block */ sbitmap defs_live_exit; /* Mapping from insn y (ordinal position in block) to INSN_UID */ varray_type uid_ruid; /* Mapping from insn y (ordinal position in block) to block id */ varray_type uid_rbid; /* Ordinal position in block of defining insn */ int *def_idx; VARRAY_RTX_INIT (uid_ruid, UID_RUID_HIGH_BOUND + 1, "uid_ruid"); VARRAY_LONG_INIT (uid_rbid, UID_RUID_HIGH_BOUND + 1, "uid_rbid"); ebb.basic_block = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks); sbitmap_vector_zero (ebb.basic_block, n_basic_blocks); ebb.exit = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks); sbitmap_vector_zero (ebb.exit, n_basic_blocks); find_ext_basic_blocks (&ebb); du.def_class = du.use_class = 0; /* Build uid_ruid and uid_rbid for this extended basic block */ for (b = 0; b < n_basic_blocks; b++) if (TEST_BIT (ebb.basic_block[b], b)) { for (eb = du.high_bound = 0; eb < n_basic_blocks; eb++) if (TEST_BIT (ebb.basic_block[b], eb)) { basic_block bb = BASIC_BLOCK (eb); /* Calculate high bound for uid_ruid and allocate if necessary */ for (insn = bb->head; insn != NEXT_INSN (bb->end); du.high_bound++, insn = NEXT_INSN (insn)) { int uid_ruid_high_bound = VARRAY_SIZE (uid_ruid); if (du.high_bound + 4 >= uid_ruid_high_bound) { VARRAY_GROW (uid_ruid, uid_ruid_high_bound * 2); VARRAY_GROW (uid_rbid, uid_ruid_high_bound * 2); } VARRAY_RTX (uid_ruid, du.high_bound) = insn; VARRAY_LONG (uid_rbid, du.high_bound) = eb; } } CLEAR_HARD_REG_SET (null_bitmap); CLEAR_HARD_REG_SET (class_regs); CLEAR_HARD_REG_SET (regs_used); CLEAR_HARD_REG_SET (avail_regs); CLEAR_HARD_REG_SET (tmp_bitmap); CLEAR_HARD_REG_SET (renamed_regs); du.defs = sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1); sbitmap_vector_zero (du.defs, FIRST_PSEUDO_REGISTER); du.uses = sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1); sbitmap_vector_zero (du.uses, FIRST_PSEUDO_REGISTER); du.require_call_save_reg = sbitmap_alloc (du.high_bound + 1); sbitmap_zero (du.require_call_save_reg); defs_live_exit = sbitmap_alloc (du.high_bound + 1); sbitmap_zero (defs_live_exit); du.def_class = xrealloc (du.def_class, (sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER * du.high_bound)); du.use_class = xrealloc (du.use_class, (sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER * du.high_bound)); build_def_use (b, &ebb, ®s_used, &du, &defs_live_exit); if (rtl_dump_file) { dump_ext_bb_info (b, &ebb); dump_def_use_chain (&global_live_at_end, &du, &uid_ruid); } /* Available registers are not: used in the block, live at the start, live at the end, a register we've renamed to. */ /* ??? The current algorithm is pessimistic for extended basic blocks as it just treats them as a big basic block. */ COPY_HARD_REG_SET (tmp_bitmap, regs_used); REG_SET_TO_HARD_REG_SET (global_live_at_start, BASIC_BLOCK (b)->global_live_at_start); IOR_HARD_REG_SET (tmp_bitmap, global_live_at_start); for (eb = 0; eb < n_basic_blocks; eb++) if (TEST_BIT (ebb.basic_block[b], eb)) { basic_block bb = BASIC_BLOCK (eb); REG_SET_TO_HARD_REG_SET (global_live_at_end, bb->global_live_at_end); IOR_HARD_REG_SET (tmp_bitmap, global_live_at_end); } def_idx = xcalloc (du.high_bound, sizeof (int)); /* Only consider registers in this extended block and in this class that are defined more than once. Replace them if permissible. */ for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) { int avail_reg, ar_idx, def, def_cnt = 0, use_idx, call_idx; if (!TEST_HARD_REG_BIT (regs_used, r) || fixed_regs[r] || r == FRAME_POINTER_REGNUM) continue; /* Find def_idx[N] where hbound of N is the number of definitions of this register in this block. and def_idx is the ordinal position of this insn in the block. */ for (i = 0, def_idx[def_cnt] = 0; i < du.high_bound; i++) if (TEST_BIT (du.defs[r], i) && consider_def (VARRAY_RTX (uid_ruid, i), r, &du, i)) { int first_use = 1; def_idx[def_cnt] = i; /* Only consider definitions that have a use. */ for (use_idx = i + 1; use_idx < du.high_bound; use_idx++) { if (TEST_BIT (du.uses[r], use_idx)) { if (consider_use (VARRAY_RTX (uid_ruid, use_idx), r, VARRAY_LONG (uid_rbid, i), VARRAY_LONG (uid_rbid, use_idx))) { if (first_use) { first_use = 0; def_cnt++; } } else { /* Don't consider def if we don't want this use. */ if (!first_use) def_cnt--; break; } } if (TEST_BIT (du.defs[r], use_idx)) break; } /* Scan until the next def to avoid renaming parameter registers. */ /* ??? consider using CALL_INSN_FUNCTION_USAGE */ for (call_idx = i; call_idx <= use_idx; call_idx++) if (VARRAY_RTX (uid_ruid, call_idx) && (GET_CODE (VARRAY_RTX (uid_ruid, call_idx)) == CALL_INSN)) SET_BIT (du.require_call_save_reg, i); } if (def_cnt < 2) continue; /* We have more than one def so rename until we exhaust renaming registers. */ /* ??? Should we continue renaming round robin when we exhaust renaming registers? */ for (def = 0; def < def_cnt - 1; def++) { if (!TEST_BIT (defs_live_exit, def_idx[def]) && (GET_RTX_CLASS (GET_CODE (VARRAY_RTX (uid_ruid, def_idx[def]))) == 'i')) { rtx reg_use = regno_first_use_in (r, PATTERN (VARRAY_RTX (uid_ruid, def_idx[def]))); if (!reg_use) break; #ifdef STACK_REGS /* Don't bother with stacked float registers */ if (GET_MODE_CLASS (GET_MODE (reg_use)) == MODE_FLOAT) break; #endif rc = (int) DU_REG_CLASS (du.def_class, r, du.high_bound, def_idx[def]); COPY_HARD_REG_SET (avail_regs, reg_class_contents[(enum reg_class) rc]); AND_COMPL_HARD_REG_SET (avail_regs, tmp_bitmap); AND_COMPL_HARD_REG_SET (avail_regs, renamed_regs); /* No available registers in this class */ GO_IF_HARD_REG_EQUAL (avail_regs, null_bitmap, no_available_regs); for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER && TEST_HARD_REG_BIT (avail_regs, ar_idx); ar_idx++) ; if (ar_idx == FIRST_PSEUDO_REGISTER) goto no_available_regs; /* Only try register renaming if there is an available register in this class. */ for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER; ar_idx++) { #ifdef REG_ALLOC_ORDER avail_reg = reg_alloc_order[ar_idx]; #else avail_reg = ar_idx; #endif if (consider_available (reg_use, avail_reg, &avail_regs, rc, &du, def_idx[def])) goto found_avail_reg; } if (rtl_dump_file) { fprintf (rtl_dump_file, "Register %s in class %s", reg_names[r], reg_class_names[rc]); fprintf (rtl_dump_file, " in insn %d", INSN_UID (VARRAY_RTX (uid_ruid, def_idx[def]))); if (TEST_BIT (du.require_call_save_reg, def_idx[def])) fprintf (rtl_dump_file, " crosses a call"); fprintf (rtl_dump_file, ". No available registers\n"); } goto try_next_def; found_avail_reg: SET_HARD_REG_BIT (renamed_regs, avail_reg); CLEAR_HARD_REG_BIT (avail_regs, avail_reg); /* Replace in destination. Replace in source for remainder of block until new register is defined again */ replace_ok = replace_reg_in_block (&du, &uid_ruid, def_idx[def], reg_use, avail_reg); /* Replace failed, so restore previous register */ if (!replace_ok) { replace_reg_in_block (&du, &uid_ruid, def_idx[def], gen_rtx_REG (GET_MODE (reg_use), avail_reg), REGNO (reg_use)); if (rtl_dump_file) { fprintf (rtl_dump_file, "Register %s in class %s Renaming as %s ", reg_names[r], reg_class_names[rc], reg_names[avail_reg]); fprintf (rtl_dump_file, "would not satisfy constraints\n"); } } else if (rtl_dump_file) { fprintf (rtl_dump_file, "Register %s in class %s Renamed as %s ", reg_names[r], reg_class_names[rc], reg_names[avail_reg]); fprintf (rtl_dump_file, "at insn %d\n", INSN_UID (VARRAY_RTX (uid_ruid, def_idx[def]))); } } try_next_def: continue; } sbitmap_zero (du.defs[r]); no_available_regs: continue; } free (def_idx); sbitmap_vector_free (du.defs); sbitmap_vector_free (du.uses); sbitmap_free (du.require_call_save_reg); sbitmap_free (defs_live_exit); CLEAR_HARD_REG_SET (regs_used); CLEAR_HARD_REG_SET (renamed_regs); for (inum = 0; inum < (int) VARRAY_SIZE (uid_ruid); inum++) VARRAY_RTX (uid_ruid, inum) = (rtx) 0; } sbitmap_vector_free (ebb.basic_block); sbitmap_vector_free (ebb.exit); } /* Build def/use chain DU for extended basic block EBB having root B. Also determine which regs are used, REGS_USED, and which insns define a live at exit def, DEFS_LIVE_EXIT */ static void build_def_use (b, ebb, regs_used, du, defs_live_exit) int b; ext_basic_blocks *ebb; HARD_REG_SET *regs_used; def_uses *du; sbitmap *defs_live_exit; { rtx insn; int eb, inum; unsigned int r; inum = 0; for (eb = 0; eb < n_basic_blocks; eb++) { basic_block bb = BASIC_BLOCK (eb); if (!TEST_BIT (ebb->basic_block[b], eb)) continue; for (insn = bb->head; insn != NEXT_INSN (bb->end); inum++, insn = NEXT_INSN (insn)) { struct resources insn_res; struct resources insn_sets; if (! INSN_P (insn)) continue; CLEAR_RESOURCE (&insn_sets); mark_set_resources (insn, &insn_sets, 0, MARK_DEST); for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) { if (!TEST_HARD_REG_BIT (insn_sets.regs, r)) continue; SET_HARD_REG_BIT (*regs_used, r); if (REGNO_REG_SET_P (bb->global_live_at_end, r)) SET_BIT (*defs_live_exit, inum); if (!insn_sets.memory) SET_BIT (du->defs[r], inum); DU_REG_CLASS (du->def_class, r, du->high_bound, inum) = get_reg_class (insn, regno_first_use_in (r, PATTERN (insn)), DESTINATION, NO_REGS); } CLEAR_RESOURCE (&insn_res); mark_referenced_resources (insn, &insn_res, 0); for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) { if (!TEST_HARD_REG_BIT (insn_res.regs, r)) continue; SET_HARD_REG_BIT (*regs_used, r); SET_BIT (du->uses[r], inum); DU_REG_CLASS (du->use_class, r, du->high_bound, inum) = get_reg_class (insn, regno_use_in (r, PATTERN (insn)), SOURCE, NO_REGS); } } } free_resource_info (); } /* Return nonzero if regno AVAIL_REG can replace REG_DEF for insns in UID_RUID starting at insn DEF in def/use chain DU. */ static int replace_reg_in_block (du, uid_ruid, def, reg_def, avail_reg) def_uses *du; varray_type *uid_ruid; int def; rtx reg_def; unsigned int avail_reg; { int du_idx, status = 1; int last_replaced_insn; unsigned int r = REGNO (reg_def); rtx death_note; rtx reg_notes; rtx reg_use = 0; rtx new_reg = gen_rtx_REG (GET_MODE (reg_def), avail_reg); rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, def)), reg_def, new_reg, DESTINATION, VARRAY_RTX (*uid_ruid, def), &status); if (!status) return status; death_note = 0; /* This typically happens if a constraint check failed and the register changes are being reversed. */ for (reg_notes = REG_NOTES (VARRAY_RTX (*uid_ruid, def)); reg_notes; reg_notes = XEXP (reg_notes, 1)) { if (REG_NOTE_KIND (reg_notes) == REG_DEAD && REGNO (XEXP (reg_notes, 0)) == avail_reg) death_note = reg_notes; } if (death_note) remove_note (VARRAY_RTX (*uid_ruid, def), death_note); /* The old destination is now dead if it is also a source. */ if (regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, def)))) REG_NOTES (VARRAY_RTX (*uid_ruid, def)) = gen_rtx_EXPR_LIST (REG_DEAD, reg_def, REG_NOTES (VARRAY_RTX (*uid_ruid, def))); last_replaced_insn = 0; /* Now replace in the uses. */ for (du_idx = def + 1; du_idx < du->high_bound; du_idx++) { if (! INSN_P (VARRAY_RTX (*uid_ruid, du_idx))) continue; reg_use = regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, du_idx))); if (reg_use && TEST_BIT (du->uses[r], du_idx)) { new_reg = gen_rtx_REG (GET_MODE (reg_use), avail_reg); rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, du_idx)), reg_use, new_reg, SOURCE, VARRAY_RTX (*uid_ruid, du_idx), &status); death_note = find_reg_note (VARRAY_RTX (*uid_ruid, du_idx), REG_DEAD, reg_use); if (death_note) { REG_NOTES (VARRAY_RTX (*uid_ruid, du_idx)) = gen_rtx_EXPR_LIST (REG_DEAD, new_reg, REG_NOTES (VARRAY_RTX (*uid_ruid, du_idx))); remove_note (VARRAY_RTX (*uid_ruid, du_idx), find_reg_note (VARRAY_RTX (*uid_ruid, du_idx), REG_DEAD, reg_use)); } } /* This insn may contain shared rtl replaced in the previous iteration. Treat this equivalent to the rr_replace_reg case. */ if (TEST_BIT (du->uses[r], du_idx)) { last_replaced_insn = du_idx; SET_BIT (du->uses[avail_reg], du_idx); RESET_BIT (du->uses[r], du_idx); if (!status) return status; } if (TEST_BIT (du->defs[r], du_idx)) break; } /* Add REG_DEAD note for replaced register at last use. */ if (last_replaced_insn) { new_reg = regno_use_in (avail_reg, PATTERN (VARRAY_RTX (*uid_ruid, last_replaced_insn))); if (new_reg && ! find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn), REG_DEAD, new_reg)) { REG_NOTES (VARRAY_RTX (*uid_ruid, last_replaced_insn)) = gen_rtx_EXPR_LIST (REG_DEAD, new_reg, REG_NOTES (VARRAY_RTX (*uid_ruid, last_replaced_insn))); remove_note (VARRAY_RTX (*uid_ruid, last_replaced_insn), find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn), REG_DEAD, reg_use)); } } return status; } /* Try to replace REG_USE in X with REG_SUB if INSN has a REPLACE_TYPE. STATUS is zero if the resulting pattern is not valid. */ static rtx rr_replace_reg (x, reg_use, reg_sub, replace_type, insn, status) rtx x; rtx reg_use; rtx reg_sub; int replace_type; rtx insn; int *status; { enum rtx_code code; int i; const char *fmt; int n; if (x == 0) return x; code = GET_CODE (x); switch (code) { case REG: if (REGNO (x) == REGNO (reg_use)) { if (GET_MODE (x) == GET_MODE (reg_use)) return reg_sub; else return gen_rtx_REG (GET_MODE (x), REGNO (reg_sub)); } return x; case SET: if (replace_type == DESTINATION) SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub, replace_type, insn, status); else if (replace_type == SOURCE) { unsigned int dest_subregno = 0; int had_subreg = GET_CODE (SET_DEST (x)) == SUBREG; if (had_subreg) dest_subregno = REGNO (XEXP (SET_DEST (x), 0)); SET_SRC (x) = rr_replace_reg (SET_SRC (x), reg_use, reg_sub, replace_type, insn, status); /* If the replacement register is not part of the source then it may be part of a source mem operand. */ if (GET_CODE (SET_DEST (x)) == MEM || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT || GET_CODE (SET_DEST (x)) == SIGN_EXTRACT || GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub, replace_type, insn, status); /* Shared rtl sanity check. */ if (had_subreg && dest_subregno != REGNO (XEXP (SET_DEST (x), 0))) { *status = 0; return x; } } n = recog_memoized (insn); if (n >= 0) { int id; extract_insn (insn); /* Any MATCH_DUP's which are REGs must still match */ for (id = insn_data[n].n_dups - 1; id >= 0; id--) { int opno = recog_data.dup_num[id]; if (GET_CODE (*recog_data.dup_loc[id]) == REG && GET_CODE (*recog_data.operand_loc[opno]) == REG && (REGNO (*recog_data.dup_loc[id]) != REGNO (*recog_data.operand_loc[opno]))) *status = 0; } if (!constrain_operands (1)) { *status = 0; validate_replace_rtx (reg_sub, reg_use, insn); } } else *status = 0; return x; default: break; } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') XEXP (x, i) = rr_replace_reg (XEXP (x, i), reg_use, reg_sub, replace_type, insn, status); if (fmt[i] == 'E') { register int xv; for (xv = 0; xv < XVECLEN (x, i); xv++) { XVECEXP (x, i, xv) = rr_replace_reg (XVECEXP (x, i, xv), reg_use, reg_sub, replace_type, insn, status); n = recog_memoized (insn); if (n >= 0) { extract_insn (insn); if (!constrain_operands (1)) { *status = 0; validate_replace_rtx (reg_sub, reg_use, insn); } } else *status = 0; } } } return x; } /* Can REGNO in INSN be considered for renaming, given def INUM in d/u chain DU? */ static int consider_def (insn, regno, du, inum) rtx insn; int regno; def_uses *du ATTRIBUTE_UNUSED; int inum ATTRIBUTE_UNUSED; { /* Don't rename windowed registers across a call */ #ifdef INCOMING_REGNO if (TEST_BIT (du->require_call_save_reg, inum) && INCOMING_REGNO (regno) != regno) return 0; #endif /* Don't consider conditional moves. Predicate architectures may use two complementary conditional moves and the regno shouldn't change */ if (condmove_p (insn)) return 0; /* Don't rename call used registers across a call */ if (!(GET_CODE (insn) == CALL_INSN && TEST_HARD_REG_BIT (call_used_reg_set, regno))) return 1; else return 0; } /* Can the use of REGNO in INSN of block USE_BLOCK be considered for renaming for a def in def_block? */ static int consider_use (insn, regno, def_block, use_block) rtx insn; int regno; int def_block; int use_block; { rtx reg_use; edge e; basic_block ub = BASIC_BLOCK (use_block); if (! INSN_P (insn)) return 0; /* If a use's basic block is different than the def's basic block, then insure another predecessor does not also define this register */ if (def_block != use_block) for (e = ub->pred; e; e = e->pred_next) if (e->src->index != def_block && e->src->index != -1 && REGNO_REG_SET_P (BASIC_BLOCK (e->src->index)->global_live_at_end, regno)) return 0; /* Don't consider conditional moves. Predicate architectures may use two complementary conditional moves and the regno shouldn't change */ if (condmove_p (insn)) return 0; reg_use = regno_first_use_in (regno, PATTERN (insn)); if (reg_use) { /* Don't consider multi-reg values. */ if (HARD_REGNO_NREGS (regno, GET_MODE (reg_use)) != 1 && GET_MODE (reg_use) != CCmode) return 0; /* Don't consider register if the only use is in a USE */ return ! reg_mentioned_p (gen_rtx_USE (VOIDmode, reg_use), PATTERN (insn)); } else return 0; } /* Can REG_USE be replaced by regno AVAIL_REG if it is in AVAIL_REGS and it is in regclass RC, given insn INUM of def/use chain DU? */ static int consider_available (reg_use, avail_reg, avail_regs, rc, du, inum) rtx reg_use; int avail_reg; HARD_REG_SET *avail_regs; int rc; def_uses *du; int inum; { if (!TEST_HARD_REG_BIT (*avail_regs, avail_reg)) return 0; if (fixed_regs[avail_reg]) return 0; #ifdef HARD_REGNO_RENAME_OK if (!HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg)) return 0; #endif /* Don't consider windowed leaf registers which will be renamed by leaf_renumber_regs */ #ifdef LEAF_REG_REMAP if (current_function_uses_only_leaf_regs) if (LEAF_REG_REMAP (avail_reg) < 0) return 0; #endif /* A register is considered available if it is available at the beginning of the basic block. We may want to refine this to when a register becomes available within the block. We don't consider multi-reg values. */ /* ??? Consider a representation that would allow multi-reg support? */ if (!TEST_HARD_REG_BIT (reg_class_contents[(enum reg_class) rc], avail_reg) || !HARD_REGNO_MODE_OK (avail_reg, GET_MODE (reg_use)) || (HARD_REGNO_NREGS (avail_reg, GET_MODE (reg_use)) != 1 && GET_MODE (reg_use) != CCmode) || (call_fixed_regs[avail_reg] #ifdef HARD_REGNO_RENAME_OK && !HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg) #endif ) || (TEST_BIT (du->require_call_save_reg, inum) && (call_used_regs[avail_reg] || call_used_regs[REGNO (reg_use)]))) return 0; /* If register is a callee-saved register it must be saved in the frame. call saved registers can not be added to regs_ever_live after reload, as it would invalidate most elimination offsets */ return regs_ever_live[avail_reg] || call_used_regs[avail_reg]; } /* Return 1 if INSN is a conditional move */ static int condmove_p (insn) rtx insn; { return (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE); } /* Searches X for the first reference to REGNO, returning the rtx of the reference found if any. Otherwise, returns NULL_RTX. */ static rtx regno_first_use_in (regno, x) unsigned int regno; rtx x; { register const char *fmt; int i, j; rtx tem; if (GET_CODE (x) == REG && REGNO (x) == regno) return x; fmt = GET_RTX_FORMAT (GET_CODE (x)); for (i = 0; i <= GET_RTX_LENGTH (GET_CODE (x)) - 1; i++) { if (fmt[i] == 'e') { if ((tem = regno_first_use_in (regno, XEXP (x, i)))) return tem; } else if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) if ((tem = regno_first_use_in (regno, XVECEXP (x, i, j)))) return tem; } return 0; } /* Dump def/use chain DU to RTL_DUMP_FILE, given insns in UID_RUID and which regs are live at end, GLOBAL_LIVE_AT_END */ static void dump_def_use_chain (global_live_at_end, du, uid_ruid) HARD_REG_SET *global_live_at_end; def_uses *du; varray_type *uid_ruid; { unsigned int r; int inum; for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) { int set = 0; for (inum = 0; inum <= du->high_bound; inum++) { rtx insn = VARRAY_RTX (*uid_ruid, inum); #if 0 if (!insn || GET_RTX_CLASS (GET_CODE (insn)) != 'i') continue; reg_use = regno_first_use_in (r, PATTERN (insn)); if (!reg_use) continue; #endif if (!set && (TEST_BIT (du->defs[r], inum) || TEST_BIT (du->uses[r], inum))) { fprintf (rtl_dump_file, "Register %s: ", reg_names[r]); if (fixed_regs[r]) fprintf (rtl_dump_file, "Fixed "); else if (call_fixed_regs[r]) fprintf (rtl_dump_file, "Call Fixed "); if (TEST_HARD_REG_BIT (*global_live_at_end, r)) fprintf (rtl_dump_file, "Live at Exit "); set = 1; } if (TEST_BIT (du->defs[r], inum)) fprintf (rtl_dump_file, "=%d ", INSN_UID (insn)); if (TEST_BIT (du->uses[r], inum)) fprintf (rtl_dump_file, "%d ", INSN_UID (insn)); } if (set) fprintf (rtl_dump_file, "\n"); } } /* Dump info for extended basic block EBB having root EB */ static void dump_ext_bb_info (eb, ebb) int eb; ext_basic_blocks *ebb; { int b; int have_ebb = 0; for (b = 0; b < n_basic_blocks; b++) { if (TEST_BIT (ebb->basic_block[eb], b)) { if (!have_ebb) { #ifndef RENAME_EXTENDED_BLOCKS fprintf (rtl_dump_file, "\nBasic block %d: ", b); #else fprintf (rtl_dump_file, "\nExtended basic block %d: ", b); #endif have_ebb = 1; } fprintf (rtl_dump_file, "%d ", b); } if (TEST_BIT (ebb->exit[eb], b)) fprintf (rtl_dump_file, "(exit) "); } if (have_ebb) fprintf (rtl_dump_file, "\n"); } /* Initialize EBB with extended basic block info if RENAME_EXTENDED_BLOCKS is defined. Otherwise just use basic blocks */ static void find_ext_basic_blocks (ebb) ext_basic_blocks *ebb; { sbitmap bb_processed; int b; bb_processed = sbitmap_alloc (n_basic_blocks); sbitmap_zero (bb_processed); #ifndef RENAME_EXTENDED_BLOCKS for (b = 0; b < n_basic_blocks; b++) { basic_block bb = BASIC_BLOCK (b); SET_BIT (ebb->basic_block[bb->index], bb->index); } #else for (b = 0; b < n_basic_blocks; b++) { basic_block bb = BASIC_BLOCK (b); if (!TEST_BIT (bb_processed, b)) { find_one_ext_basic_block (bb->index, bb, &bb_processed, ebb); } } #endif sbitmap_free (bb_processed); } /* Find one extended basic block EBB having root ENTRY containing block BB */ static void find_one_ext_basic_block (entry, bb, bb_processed, ebb) int entry; basic_block bb; sbitmap *bb_processed; ext_basic_blocks *ebb; { edge e; if (!TEST_BIT (*bb_processed, bb->index)) { SET_BIT (ebb->basic_block[entry], bb->index); SET_BIT (*bb_processed, bb->index); } for (e = bb->succ; e; e = e->succ_next) if (!TEST_BIT (*bb_processed, e->dest->index)) { if (!e->dest->pred->pred_next && (!TEST_BIT (*bb_processed, e->dest->index))) find_one_ext_basic_block (entry, e->dest, bb_processed, ebb); else SET_BIT (ebb->exit[entry], bb->index); } } /* Find the register class for register REG_USE having TYPE (DESTINATION or SOURCE) in INSN. Use DEFAULT_CLASS if we cannot determine a class. */ static enum reg_class get_reg_class (insn, reg_use, type, default_class) rtx insn; rtx reg_use; int type; enum reg_class default_class; { int alt, id = 0; extract_insn (insn); constrain_operands (1); alt = which_alternative; preprocess_constraints (); if (type == DESTINATION) { for (id = 0; id < recog_data.n_operands; id++) if (rtx_equal_p (recog_data.operand[id], reg_use)) break; } else if (type == SOURCE) for (id = recog_data.n_operands - 1; id >= 0; id--) if (rtx_equal_p (recog_data.operand[id], reg_use)) break; if (id == -1 || id == recog_data.n_operands) return default_class; return recog_op_alt[id][alt].class; }