diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 28 | ||||
-rw-r--r-- | gcc/config/sh/sh.c | 410 | ||||
-rw-r--r-- | gcc/config/sh/sh.h | 6 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 13 | ||||
-rw-r--r-- | gcc/flow.c | 5 | ||||
-rw-r--r-- | gcc/haifa-sched.c | 6 | ||||
-rw-r--r-- | gcc/target-def.h | 4 | ||||
-rw-r--r-- | gcc/target.h | 6 |
8 files changed, 476 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index afbb5256a81..99cbcb3f71c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +2003-02-24 Sanjiv Kumar Gupta <sanjivg@noida.hcltech.com> + + * target-def.h (TARGET_SCHED_INIT_GLOBAL, + TARGET_SCHED_FINISH_GLOBAL): New macros. + + * target.h (md_init_global, md_finish_global): Function + declarations corresponding to new target macros. + + * haifa-sched.c (sched_init, sched_finish): Allow target to + call the new schedular hooks. + + * flow.c (recompute_reg_usage): Add PROP_DEATH_NOTES flag in + call to update_life_info. + + * config/sh/sh.h (OVERRIDE_OPTIONS): Re-enable + flag_schedule_insns for SH4. + + * config/sh/sh.c (sh_md_init_global, sh_md_finish_global, + find_set_regmode_weight, find_insn_regmode_weight, + find_regmode_weight), sh_md_init, sh_dfa_new_cycle, + sh_variable_issue, high_pressure, ready_reorder, + rank_for_reorder, swap_reorder, sh_reorder, sh_reorder2): New + functions used to throttle the insn movement in first + scheduling pass for SH. + + * gcc/doc/tm.texi: Document TARGET_SCHED_INIT_GLOBAL and + TARGET_SCHED_FINISH_GLOBAL. + 2004-02-24 Alexandre Oliva <aoliva@redhat.com> Implement FR-V FDPIC ABI support for frv-uclinux and frv-linux. diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index 1acdeadfd6a..dbb463d9915 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -49,6 +49,7 @@ Boston, MA 02111-1307, USA. */ #include "ra.h" #include "cfglayout.h" #include "intl.h" +#include "sched-int.h" #include "ggc.h" int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch; @@ -101,6 +102,21 @@ int current_function_anonymous_args; /* Which cpu are we scheduling for. */ enum processor_type sh_cpu; +/* Definitions used in ready queue reordering for first scheduling pass. */ + +/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID. */ +static short *regmode_weight[2]; + +/* Total SFmode and SImode weights of scheduled insns. */ +static int curr_regmode_pressure[2]; + +/* If true, skip cycles for Q -> R movement. */ +static int skip_cycles = 0; + +/* Cached value of can_issue_more. This is cached in sh_variable_issue hook + and returned from sh_reorder2. */ +static short cached_can_issue_more; + /* Saved operands from the last compare to use when we generate an scc or bcc insn. */ @@ -211,6 +227,21 @@ static void sh_insert_attributes (tree, tree *); static int sh_adjust_cost (rtx, rtx, rtx, int); static int sh_use_dfa_interface (void); static int sh_issue_rate (void); +static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p); +static short find_set_regmode_weight (rtx, enum machine_mode); +static short find_insn_regmode_weight (rtx, enum machine_mode); +static void find_regmode_weight (int, enum machine_mode); +static void sh_md_init_global (FILE *, int, int); +static void sh_md_finish_global (FILE *, int); +static int rank_for_reorder (const void *, const void *); +static void swap_reorder (rtx *, int); +static void ready_reorder (rtx *, int); +static short high_pressure (enum machine_mode); +static int sh_reorder (FILE *, int, rtx *, int *, int); +static int sh_reorder2 (FILE *, int, rtx *, int *, int); +static void sh_md_init (FILE *, int, int); +static int sh_variable_issue (FILE *, int, rtx, int); + static bool sh_function_ok_for_sibcall (tree, tree); static bool sh_cannot_modify_jumps_p (void); @@ -293,6 +324,62 @@ static tree sh_build_builtin_va_list (void); #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE sh_issue_rate +/* The next 5 hooks have been implemented for reenabling sched1. With the + help of these macros we are limiting the movement of insns in sched1 to + reduce the register pressure. The overall idea is to keep count of SImode + and SFmode regs required by already scheduled insns. When these counts + cross some threshold values; give priority to insns that free registers. + The insn that frees registers is most likely to be the insn with lowest + LUID (original insn order); but such an insn might be there in the stalled + queue (Q) instead of the ready queue (R). To solve this, we skip cycles + upto a max of 8 cycles so that such insns may move from Q -> R. + + The description of the hooks are as below: + + TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic + scheduler; it is called inside the sched_init function just after + find_insn_reg_weights function call. It is used to calculate the SImode + and SFmode weights of insns of basic blocks; much similiar to what + find_insn_reg_weights does. + TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook. + + TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is + indicated by TARGET_SCHED_REORDER2; doing this may move insns from + (Q)->(R). + + TARGET_SCHED_REORDER: If the register pressure for SImode or SFmode is + high; reorder the ready queue so that the insn with lowest LUID will be + issued next. + + TARGET_SCHED_REORDER2: If the register pressure is high, indicate to + TARGET_SCHED_DFA_NEW_CYCLE to skip cycles. + + TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it + can be returned from TARGET_SCHED_REORDER2. + + TARGET_SCHED_INIT: Reset the register pressure counting variables. */ + +#undef TARGET_SCHED_DFA_NEW_CYCLE +#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle + +#undef TARGET_SCHED_INIT_GLOBAL +#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global + +#undef TARGET_SCHED_FINISH_GLOBAL +#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global + +#undef TARGET_SCHED_VARIABLE_ISSUE +#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue + +#undef TARGET_SCHED_REORDER +#define TARGET_SCHED_REORDER sh_reorder + +#undef TARGET_SCHED_REORDER2 +#define TARGET_SCHED_REORDER2 sh_reorder2 + +#undef TARGET_SCHED_INIT +#define TARGET_SCHED_INIT sh_md_init + #undef TARGET_CANNOT_MODIFY_JUMPS_P #define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p #undef TARGET_BRANCH_TARGET_REGISTER_CLASS @@ -354,6 +441,12 @@ static tree sh_build_builtin_va_list (void); #undef TARGET_PCH_VALID_P #define TARGET_PCH_VALID_P sh_pch_valid_p +/* Return regmode weight for insn. */ +#define INSN_REGMODE_WEIGHT(INSN, MODE) regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)] + +/* Return current register pressure for regmode. */ +#define CURR_REGMODE_PRESSURE(MODE) curr_regmode_pressure[((MODE) == SImode) ? 0 : 1] + struct gcc_target targetm = TARGET_INITIALIZER; /* Print the operand address in x to the stream. */ @@ -8264,6 +8357,323 @@ sh_issue_rate(void) return 1; } +/* Functions for ready queue reordering for sched1. */ + +/* Get weight for mode for a set x. */ +static short +find_set_regmode_weight (x, mode) + rtx x; + enum machine_mode mode; +{ + if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode)) + return 1; + if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode)) + { + if (GET_CODE (SET_DEST (x)) == REG) + { + if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x))) + return 1; + else + return 0; + } + return 1; + } + return 0; +} + +/* Get regmode weight for insn. */ +static short +find_insn_regmode_weight (insn, mode) + rtx insn; + enum machine_mode mode; +{ + short reg_weight = 0; + rtx x; + + /* Increment weight for each register born here. */ + x = PATTERN (insn); + reg_weight += find_set_regmode_weight (x, mode); + if (GET_CODE (x) == PARALLEL) + { + int j; + for (j = XVECLEN (x, 0) - 1; j >= 0; j--) + { + x = XVECEXP (PATTERN (insn), 0, j); + reg_weight += find_set_regmode_weight (x, mode); + } + } + /* Decrement weight for each register that dies here. */ + for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) + { + if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED) + { + rtx note = XEXP (x, 0); + if (GET_CODE (note) == REG && GET_MODE (note) == mode) + reg_weight--; + } + } + return reg_weight; +} + +/* Calculate regmode weights for all insns of a basic block. */ +static void +find_regmode_weight (b, mode) + int b; + enum machine_mode mode; +{ + rtx insn, next_tail, head, tail; + + get_block_head_tail (b, &head, &tail); + next_tail = NEXT_INSN (tail); + + for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) + { + /* Handle register life information. */ + if (!INSN_P (insn)) + continue; + + if (mode == SFmode) + INSN_REGMODE_WEIGHT (insn, mode) = + find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode); + else if (mode == SImode) + INSN_REGMODE_WEIGHT (insn, mode) = + find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode); + } +} + +/* Comparison function for ready queue sorting. */ +static int +rank_for_reorder (x, y) + const void *x; + const void *y; +{ + rtx tmp = *(const rtx *) y; + rtx tmp2 = *(const rtx *) x; + + /* The insn in a schedule group should be issued the first. */ + if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2)) + return SCHED_GROUP_P (tmp2) ? 1 : -1; + + /* If insns are equally good, sort by INSN_LUID (original insn order), This + minimizes instruction movement, thus minimizing sched's effect on + register pressure. */ + return INSN_LUID (tmp) - INSN_LUID (tmp2); +} + +/* Resort the array A in which only element at index N may be out of order. */ +static void +swap_reorder (a, n) + rtx *a; + int n; +{ + rtx insn = a[n - 1]; + int i = n - 2; + + while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0) + { + a[i + 1] = a[i]; + i -= 1; + } + a[i + 1] = insn; +} + +#define SCHED_REORDER(READY, N_READY) \ +do { if ((N_READY) == 2) \ + swap_reorder (READY, N_READY); \ + else if ((N_READY) > 2) \ + qsort (READY, N_READY, sizeof (rtx), rank_for_reorder); } \ +while (0) + +/* Sort the ready list READY by ascending priority, using the SCHED_REORDER + macro. */ +static void +ready_reorder (ready, nready) + rtx *ready; + int nready; +{ + SCHED_REORDER (ready, nready); +} + +/* Calculate regmode weights for all insns of all basic block. */ +static void +sh_md_init_global (dump, verbose, old_max_uid) + FILE *dump ATTRIBUTE_UNUSED; + int verbose ATTRIBUTE_UNUSED; + int old_max_uid; +{ + basic_block b; + + regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short)); + regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short)); + + FOR_EACH_BB_REVERSE (b) + { + find_regmode_weight (b->index, SImode); + find_regmode_weight (b->index, SFmode); + } + + CURR_REGMODE_PRESSURE (SImode) = 0; + CURR_REGMODE_PRESSURE (SFmode) = 0; + +} + +/* Cleanup. */ +static void +sh_md_finish_global (dump, verbose) + FILE *dump ATTRIBUTE_UNUSED; + int verbose ATTRIBUTE_UNUSED; +{ + if (regmode_weight[0]) + { + free (regmode_weight[0]); + regmode_weight[0] = NULL; + } + if (regmode_weight[1]) + { + free (regmode_weight[1]); + regmode_weight[1] = NULL; + } +} + +/* Cache the can_issue_more so that we can return it from reorder2. Also, + keep count of register pressures on SImode and SFmode. */ +static int +sh_variable_issue (dump, sched_verbose, insn, can_issue_more) + FILE *dump ATTRIBUTE_UNUSED; + int sched_verbose ATTRIBUTE_UNUSED; + rtx insn; + int can_issue_more; +{ + if (GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER) + cached_can_issue_more = can_issue_more - 1; + else + cached_can_issue_more = can_issue_more; + + if (reload_completed) + return cached_can_issue_more; + + CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode); + CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode); + + return cached_can_issue_more; +} + +static void +sh_md_init (dump, verbose, veclen) + FILE *dump ATTRIBUTE_UNUSED; + int verbose ATTRIBUTE_UNUSED; + int veclen ATTRIBUTE_UNUSED; +{ + CURR_REGMODE_PRESSURE (SImode) = 0; + CURR_REGMODE_PRESSURE (SFmode) = 0; +} + +/* Some magic numbers. */ +/* Pressure on register r0 can lead to spill failures. so avoid sched1 for + functions that already have high pressure on r0. */ +#define R0_MAX_LIFE_REGIONS 2 +#define R0_MAX_LIVE_LENGTH 12 +/* Register Pressure threshols for SImode and SFmode registers. */ +#define SIMODE_MAX_WEIGHT 5 +#define SFMODE_MAX_WEIGHT 10 + +/* Return true if the pressure is high for MODE. */ +static short +high_pressure (mode) + enum machine_mode mode; +{ + /* Pressure on register r0 can lead to spill failures. so avoid sched1 for + functions that already have high pressure on r0. */ + if ((REG_N_SETS (0) - REG_N_DEATHS (0)) >= R0_MAX_LIFE_REGIONS + && REG_LIVE_LENGTH (0) >= R0_MAX_LIVE_LENGTH) + return 1; + + if (mode == SFmode) + return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT); + else + return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT); +} + +/* Reorder ready queue if register pressure is high. */ +static int +sh_reorder (dump, sched_verbose, ready, n_readyp, clock_var) + FILE *dump ATTRIBUTE_UNUSED; + int sched_verbose ATTRIBUTE_UNUSED; + rtx *ready; + int *n_readyp; + int clock_var ATTRIBUTE_UNUSED; +{ + if (reload_completed) + return sh_issue_rate (); + + if (high_pressure (SFmode) || high_pressure (SImode)) + { + ready_reorder (ready, *n_readyp); + } + + return sh_issue_rate (); +} + +/* Skip cycles if the current register pressure is high. */ +static int +sh_reorder2 (dump, sched_verbose, ready, n_readyp, clock_var) + FILE *dump ATTRIBUTE_UNUSED; + int sched_verbose ATTRIBUTE_UNUSED; + rtx *ready ATTRIBUTE_UNUSED; + int *n_readyp ATTRIBUTE_UNUSED; + int clock_var ATTRIBUTE_UNUSED; +{ + if (reload_completed) + return cached_can_issue_more; + + if (high_pressure(SFmode) || high_pressure (SImode)) + skip_cycles = 1; + + return cached_can_issue_more; +} + +/* Skip cycles without sorting the ready queue. This will move insn from + Q->R. If this is the last cycle we are skipping; allow sorting of ready + queue by sh_reorder. */ + +/* Generally, skipping these many cycles are sufficient for all insns to move + from Q -> R. */ +#define MAX_SKIPS 8 + +static int +sh_dfa_new_cycle (sched_dump, sched_verbose, insn, last_clock_var, + clock_var, sort_p) + FILE *sched_dump ATTRIBUTE_UNUSED; + int sched_verbose ATTRIBUTE_UNUSED; + rtx insn ATTRIBUTE_UNUSED; + int last_clock_var; + int clock_var; + int *sort_p; +{ + if (reload_completed) + return 0; + + if (skip_cycles) + { + if ((clock_var - last_clock_var) < MAX_SKIPS) + { + *sort_p = 0; + return 1; + } + /* If this is the last cycle we are skipping, allow reordering of R. */ + if ((clock_var - last_clock_var) == MAX_SKIPS) + { + *sort_p = 1; + return 1; + } + } + + skip_cycles = 0; + + return 0; +} + /* SHmedia requires registers for branches, so we can't generate new branches past reload. */ static bool diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h index 8580cb6d201..6bdfefe7932 100644 --- a/gcc/config/sh/sh.h +++ b/gcc/config/sh/sh.h @@ -516,7 +516,11 @@ do { \ /* Never run scheduling before reload, since that can \ break global alloc, and generates slower code anyway due \ to the pressure on R0. */ \ - flag_schedule_insns = 0; \ + /* Enable sched1 for SH4; ready queue will be reordered by \ + the target hooks when pressure is high. We can not do this for \ + SH3 and lower as they give spill failures for R0. */ \ + if (!TARGET_HARD_SH4) \ + flag_schedule_insns = 0; \ } \ \ if (align_loops == 0) \ diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index e618c8eff81..1ffa5ee413b 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -5571,6 +5571,19 @@ to. @var{verbose} is the verbose level provided by @option{-fsched-verbose-@var{n}}. @end deftypefn +@deftypefn {Target Hook} void TARGET_SCHED_INIT_GLOBAL (FILE *@var{file}, int @var{verbose}, int @var{old_max_uid}) +This hook is executed by the scheduler after function level initializations. +@var{file} is either a null pointer, or a stdio stream to write any debug output to. +@var{verbose} is the verbose level provided by @option{-fsched-verbose-@var{n}}. +@var{old_max_uid} is the maximum insn uid when scheduling begins. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_SCHED_FINISH_GLOBAL (FILE *@var{file}, int @var{verbose}) +This is the cleanup hook corresponding to TARGET_SCHED_INIT_GLOBAL. +@var{file} is either a null pointer, or a stdio stream to write any debug output to. +@var{verbose} is the verbose level provided by @option{-fsched-verbose-@var{n}}. +@end deftypefn + @deftypefn {Target Hook} int TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE (void) This hook is called many times during insn scheduling. If the hook returns nonzero, the automaton based pipeline description is used for diff --git a/gcc/flow.c b/gcc/flow.c index 56a0bfa1db5..736e5e61bcb 100644 --- a/gcc/flow.c +++ b/gcc/flow.c @@ -4260,7 +4260,10 @@ void recompute_reg_usage (rtx f ATTRIBUTE_UNUSED, int loop_step ATTRIBUTE_UNUSED) { allocate_reg_life_data (); - update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO); + /* distribute_notes in combiner fails to convert some of the REG_UNUSED notes + to REG_DEAD notes. This causes CHECK_DEAD_NOTES in sched1 to abort. To + solve this update the DEATH_NOTES here. */ + update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES); } /* Optionally removes all the REG_DEAD and REG_UNUSED notes from a set of diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index 5cfce5936cb..496ea2caa2a 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -2856,6 +2856,9 @@ sched_init (FILE *dump_file) removing death notes. */ FOR_EACH_BB_REVERSE (b) find_insn_reg_weight (b->index); + + if (targetm.sched.md_init_global) + (*targetm.sched.md_init_global) (sched_dump, sched_verbose, old_max_uid); } /* Free global data used during insn scheduling. */ @@ -2875,5 +2878,8 @@ sched_finish (void) end_alias_analysis (); if (write_symbols != NO_DEBUG) free (line_note_head); + + if (targetm.sched.md_finish_global) + (*targetm.sched.md_finish_global) (sched_dump, sched_verbose); } #endif /* INSN_SCHEDULING */ diff --git a/gcc/target-def.h b/gcc/target-def.h index ef4c777378c..0b35bc1855a 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -217,6 +217,8 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TARGET_SCHED_VARIABLE_ISSUE 0 #define TARGET_SCHED_INIT 0 #define TARGET_SCHED_FINISH 0 +#define TARGET_SCHED_INIT_GLOBAL 0 +#define TARGET_SCHED_FINISH_GLOBAL 0 #define TARGET_SCHED_REORDER 0 #define TARGET_SCHED_REORDER2 0 #define TARGET_SCHED_DEPENDENCIES_EVALUATION_HOOK 0 @@ -239,6 +241,8 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. TARGET_SCHED_VARIABLE_ISSUE, \ TARGET_SCHED_INIT, \ TARGET_SCHED_FINISH, \ + TARGET_SCHED_INIT_GLOBAL, \ + TARGET_SCHED_FINISH_GLOBAL, \ TARGET_SCHED_REORDER, \ TARGET_SCHED_REORDER2, \ TARGET_SCHED_DEPENDENCIES_EVALUATION_HOOK, \ diff --git a/gcc/target.h b/gcc/target.h index ed96b02efe4..67cf25da0f6 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -183,6 +183,12 @@ struct gcc_target /* Finalize machine-dependent scheduling code. */ void (* md_finish) (FILE *, int); + /* Initialize machine-dependent function while scheduling code. */ + void (* md_init_global) (FILE *, int, int); + + /* Finalize machine-dependent function wide scheduling code. */ + void (* md_finish_global) (FILE *, int); + /* Reorder insns in a machine-dependent fashion, in two different places. Default does nothing. */ int (* reorder) (FILE *, int, rtx *, int *, int); |