diff options
author | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
---|---|---|
committer | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
commit | 1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch) | |
tree | c607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/gcc/regstat.c | |
parent | 283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff) | |
download | toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.gz toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.bz2 toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.zip |
Initial checkin of GCC 4.9.0 from trunk (r208799).
Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba
Diffstat (limited to 'gcc-4.9/gcc/regstat.c')
-rw-r--r-- | gcc-4.9/gcc/regstat.c | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/regstat.c b/gcc-4.9/gcc/regstat.c new file mode 100644 index 000000000..75d9cb446 --- /dev/null +++ b/gcc-4.9/gcc/regstat.c @@ -0,0 +1,611 @@ +/* Scanning of rtl for dataflow analysis. + Copyright (C) 2007-2014 Free Software Foundation, Inc. + Contributed by Kenneth Zadeck (zadeck@naturalbridge.com). + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "regs.h" +#include "except.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "timevar.h" +#include "df.h" + + +struct regstat_n_sets_and_refs_t *regstat_n_sets_and_refs; + +/*---------------------------------------------------------------------------- + REG_N_SETS and REG_N_REFS. + ----------------------------------------------------------------------------*/ + +/* If a pass need to change these values in some magical way or the + pass needs to have accurate values for these and is not using + incremental df scanning, then it should use REG_N_SETS and + REG_N_USES. If the pass is doing incremental scanning then it + should be getting the info from DF_REG_DEF_COUNT and + DF_REG_USE_COUNT. */ + +void +regstat_init_n_sets_and_refs (void) +{ + unsigned int i; + unsigned int max_regno = max_reg_num (); + + timevar_push (TV_REG_STATS); + df_grow_reg_info (); + gcc_assert (!regstat_n_sets_and_refs); + + regstat_n_sets_and_refs = XNEWVEC (struct regstat_n_sets_and_refs_t, max_regno); + + if (MAY_HAVE_DEBUG_INSNS) + for (i = 0; i < max_regno; i++) + { + int use_count; + df_ref use; + + use_count = DF_REG_USE_COUNT (i); + for (use = DF_REG_USE_CHAIN (i); use; use = DF_REF_NEXT_REG (use)) + if (DF_REF_INSN_INFO (use) && DEBUG_INSN_P (DF_REF_INSN (use))) + use_count--; + + + SET_REG_N_SETS (i, DF_REG_DEF_COUNT (i)); + SET_REG_N_REFS (i, use_count + REG_N_SETS (i)); + } + else + for (i = 0; i < max_regno; i++) + { + SET_REG_N_SETS (i, DF_REG_DEF_COUNT (i)); + SET_REG_N_REFS (i, DF_REG_USE_COUNT (i) + REG_N_SETS (i)); + } + timevar_pop (TV_REG_STATS); + +} + + +/* Free the array that holds the REG_N_SETS and REG_N_REFS. */ + +void +regstat_free_n_sets_and_refs (void) +{ + gcc_assert (regstat_n_sets_and_refs); + free (regstat_n_sets_and_refs); + regstat_n_sets_and_refs = NULL; +} + + +/*---------------------------------------------------------------------------- + REGISTER INFORMATION + + Process REG_N_DEATHS, REG_LIVE_LENGTH, REG_N_CALLS_CROSSED, + REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK. + + ----------------------------------------------------------------------------*/ + +static bitmap setjmp_crosses; +struct reg_info_t *reg_info_p; + +/* The number allocated elements of reg_info_p. */ +size_t reg_info_p_size; + +/* Compute register info: lifetime, bb, and number of defs and uses + for basic block BB. The three bitvectors are scratch regs used + here. */ + +static void +regstat_bb_compute_ri (unsigned int bb_index, + bitmap live, bitmap artificial_uses, + bitmap local_live, bitmap local_processed, + int *local_live_last_luid) +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_index); + rtx insn; + df_ref *def_rec; + df_ref *use_rec; + int luid = 0; + bitmap_iterator bi; + unsigned int regno; + + bitmap_copy (live, df_get_live_out (bb)); + bitmap_clear (artificial_uses); + + /* Process the regs live at the end of the block. Mark them as + not local to any one basic block. */ + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; + + /* Process the artificial defs and uses at the bottom of the block + to begin processing. */ + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + df_ref def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + df_ref use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + { + regno = DF_REF_REGNO (use); + bitmap_set_bit (live, regno); + bitmap_set_bit (artificial_uses, regno); + } + } + + FOR_BB_INSNS_REVERSE (bb, insn) + { + unsigned int uid = INSN_UID (insn); + bitmap_iterator bi; + struct df_mw_hardreg **mws_rec; + rtx link; + + if (!NONDEBUG_INSN_P (insn)) + continue; + + luid++; + + link = REG_NOTES (insn); + while (link) + { + if (REG_NOTE_KIND (link) == REG_DEAD) + REG_N_DEATHS (REGNO (XEXP (link, 0)))++; + link = XEXP (link, 1); + } + + /* Process the defs. */ + if (CALL_P (insn)) + { + bool can_throw = can_throw_internal (insn); + bool set_jump = (find_reg_note (insn, REG_SETJMP, NULL) != NULL); + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + { + REG_N_CALLS_CROSSED (regno)++; + REG_FREQ_CALLS_CROSSED (regno) += REG_FREQ_FROM_BB (bb); + REG_FREQ_CALLS_CROSSED (regno) = + MIN (REG_FREQ_CALLS_CROSSED (regno), REG_FREQ_MAX); + if (can_throw) + REG_N_THROWING_CALLS_CROSSED (regno)++; + + /* We have a problem with any pseudoreg that lives + across the setjmp. ANSI says that if a user variable + does not change in value between the setjmp and the + longjmp, then the longjmp preserves it. This + includes longjmp from a place where the pseudo + appears dead. (In principle, the value still exists + if it is in scope.) If the pseudo goes in a hard + reg, some other value may occupy that hard reg where + this pseudo is dead, thus clobbering the pseudo. + Conclusion: such a pseudo must not go in a hard + reg. */ + if (set_jump) + bitmap_set_bit (setjmp_crosses, regno); + } + } + + /* We only care about real sets for calls. Clobbers cannot + be depended on. + Only do this if the value is totally dead. */ + for (mws_rec = DF_INSN_UID_MWS (uid); *mws_rec; mws_rec++) + { + struct df_mw_hardreg *mws = *mws_rec; + if (DF_MWS_REG_DEF_P (mws)) + { + bool all_dead = true; + unsigned int r; + + for (r = mws->start_regno; r <= mws->end_regno; r++) + if (bitmap_bit_p (artificial_uses, r) + || bitmap_bit_p (live, r)) + { + all_dead = false; + break; + } + + if (all_dead) + { + regno = mws->start_regno; + REG_LIVE_LENGTH (regno)++; + } + } + } + + /* All of the defs except the return value are some sort of + clobber. This code is for the return. */ + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + df_ref def = *def_rec; + if ((!CALL_P (insn)) + || (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)))) + { + unsigned int dregno = DF_REF_REGNO (def); + + if (bitmap_bit_p (live, dregno)) + { + /* If we have seen a use of DREGNO somewhere before (i.e. + later in this basic block), and DEF is not a subreg + store or conditional store, then kill the register + here and add the proper length to its REG_LIVE_LENGTH. + + If we have not seen a use of DREGNO later in this basic + block, then we need to add the length from here to the + end of the block to the live length. */ + if (bitmap_bit_p (local_live, dregno)) + { + /* Note that LOCAL_LIVE implies LOCAL_PROCESSED, so + we don't have to set LOCAL_PROCESSED in this clause. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + { + REG_LIVE_LENGTH (dregno) += + (luid - local_live_last_luid[dregno]); + local_live_last_luid[dregno] = luid; + bitmap_clear_bit (local_live, dregno); + } + } + else + { + bitmap_set_bit (local_processed, dregno); + REG_LIVE_LENGTH (dregno) += luid; + local_live_last_luid[dregno] = luid; + } + + /* Kill this register if it is not a subreg store or + conditional store. + ??? This means that any partial store is live from + the last use in a basic block to the start of this + basic block. This results in poor calculations of + REG_LIVE_LENGTH in large basic blocks. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, dregno); + } + else if ((!(DF_REF_FLAGS (def) & DF_REF_MW_HARDREG)) + && (!bitmap_bit_p (artificial_uses, dregno))) + { + REG_LIVE_LENGTH (dregno)++; + } + + if (dregno >= FIRST_PSEUDO_REGISTER) + { + REG_FREQ (dregno) += REG_FREQ_FROM_BB (bb); + REG_FREQ (dregno) = + MIN (REG_FREQ (dregno), REG_FREQ_MAX); + + if (REG_BASIC_BLOCK (dregno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (dregno) = bb->index; + else if (REG_BASIC_BLOCK (dregno) != bb->index) + REG_BASIC_BLOCK (dregno) = REG_BLOCK_GLOBAL; + } + } + } + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + df_ref use = *use_rec; + unsigned int uregno = DF_REF_REGNO (use); + + if (uregno >= FIRST_PSEUDO_REGISTER) + { + REG_FREQ (uregno) += REG_FREQ_FROM_BB (bb); + REG_FREQ (uregno) = + MIN (REG_FREQ (uregno), REG_FREQ_MAX); + + if (REG_BASIC_BLOCK (uregno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (uregno) = bb->index; + else if (REG_BASIC_BLOCK (uregno) != bb->index) + REG_BASIC_BLOCK (uregno) = REG_BLOCK_GLOBAL; + } + + if (bitmap_set_bit (live, uregno)) + { + /* This register is now live. Begin to process it locally. + + Note that we don't even get here if the variable was live + at the end of the block since just a ref inside the block + does not effect the calculations. */ + REG_LIVE_LENGTH (uregno) ++; + local_live_last_luid[uregno] = luid; + bitmap_set_bit (local_live, uregno); + bitmap_set_bit (local_processed, uregno); + } + } + } + + /* Add the liveness length to all registers that were used somewhere + in this bock, but not between that use and the head of this block. */ + EXECUTE_IF_SET_IN_BITMAP (local_live, 0, regno, bi) + { + REG_LIVE_LENGTH (regno) += (luid - local_live_last_luid[regno]); + } + + /* Add the length of the block to all of the registers that were not + referenced, but still live in this block. */ + bitmap_and_compl_into (live, local_processed); + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + REG_LIVE_LENGTH (regno) += luid; + + bitmap_clear (local_processed); + bitmap_clear (local_live); +} + + +/* Compute register info: lifetime, bb, and number of defs and uses. */ +void +regstat_compute_ri (void) +{ + basic_block bb; + bitmap live = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap artificial_uses = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap local_live = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap local_processed = BITMAP_ALLOC (&df_bitmap_obstack); + unsigned int regno; + bitmap_iterator bi; + int *local_live_last_luid; + + /* Initialize everything. */ + + gcc_assert (!reg_info_p); + + timevar_push (TV_REG_STATS); + setjmp_crosses = BITMAP_ALLOC (&df_bitmap_obstack); + max_regno = max_reg_num (); + reg_info_p_size = max_regno; + reg_info_p = XCNEWVEC (struct reg_info_t, max_regno); + local_live_last_luid = XNEWVEC (int, max_regno); + + FOR_EACH_BB_FN (bb, cfun) + { + regstat_bb_compute_ri (bb->index, live, artificial_uses, + local_live, local_processed, + local_live_last_luid); + } + + BITMAP_FREE (live); + BITMAP_FREE (artificial_uses); + BITMAP_FREE (local_live); + BITMAP_FREE (local_processed); + free (local_live_last_luid); + + /* See the setjmp comment in regstat_bb_compute_ri. */ + EXECUTE_IF_SET_IN_BITMAP (setjmp_crosses, FIRST_PSEUDO_REGISTER, regno, bi) + { + REG_BASIC_BLOCK (regno) = REG_BLOCK_UNKNOWN; + REG_LIVE_LENGTH (regno) = -1; + } + + timevar_pop (TV_REG_STATS); +} + + +/* Free all storage associated with the problem. */ + +void +regstat_free_ri (void) +{ + gcc_assert (reg_info_p); + reg_info_p_size = 0; + free (reg_info_p); + reg_info_p = NULL; + + BITMAP_FREE (setjmp_crosses); +} + + +/* Return a bitmap containing the set of registers that cross a setjmp. + The client should not change or delete this bitmap. */ + +bitmap +regstat_get_setjmp_crosses (void) +{ + return setjmp_crosses; +} + +/*---------------------------------------------------------------------------- + Process REG_N_CALLS_CROSSED. + + This is used by sched_deps. A good implementation of sched-deps + would really process the blocks directly rather than going through + lists of insns. If it did this, it could use the exact regs that + cross an individual call rather than using this info that merges + the info for all calls. + + ----------------------------------------------------------------------------*/ + + + +/* Compute calls crossed for BB. Live is a scratch bitvector. */ + +static void +regstat_bb_compute_calls_crossed (unsigned int bb_index, bitmap live) +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_index); + rtx insn; + df_ref *def_rec; + df_ref *use_rec; + + bitmap_copy (live, df_get_live_out (bb)); + + /* Process the artificial defs and uses at the bottom of the block + to begin processing. */ + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + df_ref def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + df_ref use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (live, DF_REF_REGNO (use)); + } + + FOR_BB_INSNS_REVERSE (bb, insn) + { + unsigned int uid = INSN_UID (insn); + unsigned int regno; + + if (!INSN_P (insn)) + continue; + + /* Process the defs. */ + if (CALL_P (insn)) + { + bitmap_iterator bi; + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + { + REG_N_CALLS_CROSSED (regno)++; + REG_FREQ_CALLS_CROSSED (regno) += REG_FREQ_FROM_BB (bb); + REG_FREQ_CALLS_CROSSED (regno) = + MIN (REG_FREQ_CALLS_CROSSED (regno), REG_FREQ_MAX); + } + } + + /* All of the defs except the return value are some sort of + clobber. This code is for the return. */ + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + df_ref def = *def_rec; + if ((!CALL_P (insn)) + || (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)))) + { + /* Kill this register if it is not a subreg store or conditional store. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + } + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + df_ref use = *use_rec; + bitmap_set_bit (live, DF_REF_REGNO (use)); + } + } +} + + +/* Compute register info: lifetime, bb, and number of defs and uses. */ +void +regstat_compute_calls_crossed (void) +{ + basic_block bb; + bitmap live = BITMAP_ALLOC (&df_bitmap_obstack); + + /* Initialize everything. */ + gcc_assert (!reg_info_p); + + timevar_push (TV_REG_STATS); + max_regno = max_reg_num (); + reg_info_p_size = max_regno; + reg_info_p = XCNEWVEC (struct reg_info_t, max_regno); + + FOR_EACH_BB_FN (bb, cfun) + { + regstat_bb_compute_calls_crossed (bb->index, live); + } + + BITMAP_FREE (live); + timevar_pop (TV_REG_STATS); +} + + +/* Free all storage associated with the problem. */ + +void +regstat_free_calls_crossed (void) +{ + gcc_assert (reg_info_p); + reg_info_p_size = 0; + free (reg_info_p); + reg_info_p = NULL; +} + +/* Dump the register info to FILE. */ + +void +dump_reg_info (FILE *file) +{ + unsigned int i, max = max_reg_num (); + if (reload_completed) + return; + + if (reg_info_p_size < max) + max = reg_info_p_size; + + fprintf (file, "%d registers.\n", max); + for (i = FIRST_PSEUDO_REGISTER; i < max; i++) + { + enum reg_class rclass, altclass; + + if (regstat_n_sets_and_refs) + fprintf (file, "\nRegister %d used %d times across %d insns", + i, REG_N_REFS (i), REG_LIVE_LENGTH (i)); + else if (df) + fprintf (file, "\nRegister %d used %d times across %d insns", + i, DF_REG_USE_COUNT (i) + DF_REG_DEF_COUNT (i), REG_LIVE_LENGTH (i)); + + if (REG_BASIC_BLOCK (i) >= NUM_FIXED_BLOCKS) + fprintf (file, " in block %d", REG_BASIC_BLOCK (i)); + if (regstat_n_sets_and_refs) + fprintf (file, "; set %d time%s", REG_N_SETS (i), + (REG_N_SETS (i) == 1) ? "" : "s"); + else if (df) + fprintf (file, "; set %d time%s", DF_REG_DEF_COUNT (i), + (DF_REG_DEF_COUNT (i) == 1) ? "" : "s"); + if (regno_reg_rtx[i] != NULL && REG_USERVAR_P (regno_reg_rtx[i])) + fputs ("; user var", file); + if (REG_N_DEATHS (i) != 1) + fprintf (file, "; dies in %d places", REG_N_DEATHS (i)); + if (REG_N_CALLS_CROSSED (i) == 1) + fputs ("; crosses 1 call", file); + else if (REG_N_CALLS_CROSSED (i)) + fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i)); + if (REG_FREQ_CALLS_CROSSED (i)) + fprintf (file, "; crosses call with %d frequency", REG_FREQ_CALLS_CROSSED (i)); + if (regno_reg_rtx[i] != NULL + && PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD) + fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i)); + + rclass = reg_preferred_class (i); + altclass = reg_alternate_class (i); + if (rclass != GENERAL_REGS || altclass != ALL_REGS) + { + if (altclass == ALL_REGS || rclass == ALL_REGS) + fprintf (file, "; pref %s", reg_class_names[(int) rclass]); + else if (altclass == NO_REGS) + fprintf (file, "; %s or none", reg_class_names[(int) rclass]); + else + fprintf (file, "; pref %s, else %s", + reg_class_names[(int) rclass], + reg_class_names[(int) altclass]); + } + + if (regno_reg_rtx[i] != NULL && REG_POINTER (regno_reg_rtx[i])) + fputs ("; pointer", file); + fputs (".\n", file); + } +} + |