From ff92f13da31d6148f2e0d8d876bdf6606a00bac5 Mon Sep 17 00:00:00 2001 From: vmakarov Date: Fri, 19 Jan 2007 20:22:01 +0000 Subject: [PATCH] 2007-01-19 Vladimir Makarov * toplev.h (flag_ipra): New external definition. * common.opt (fipra): New option. * doc/invoke.texi (-fipra); Document the option. * cgraph.h (cgraph_node): New member function_used_regs. * cgraph.c (cgraph_create_node): Initialize function_used_regs. * final.c (update_regs_ever_live): New function. (rest_of_handle_final): Set up function_used_regs and call_used_regs. * ira-build.c (ira_build): Clear function call_used_regs. * function.h: Add hard-reg-set.h header. (emit_status): New member call_used_regs. (get_call_invalidated_used_regs): New definition. * function.c (get_call, get_call_invalidated_used_regs): New functions. * Makefile.in (FUNCTION_H): Add hard-reg-set.h. (caller-save.o): Add dependence on output.h and ira.h * regrename.c (copyprop_hardreg_forward_1): Use function get_call_invalidated_used_regs. * ira-conflicts.c (process_bb_node_for_conflicts): Ditto. * postreload-gcse.c (record_opr_changes, reg_set_between_after_reload_p, reg_used_between_after_reload_p): Ditto. * postreload.c (reload_combine, reload_cse_move2add): Ditto. * rtlanal.c (reg_set_between_p): Ditto. * flow.c (propagate_one_insn): Ditto. * df-scan.c (df_insn_refs_record): Ditto. * gcse.c (compute_hash_table_work, compute_store_table): Ditto. * cselib.c (cselib_process_insn): Ditto. * loop-iv.c (simplify_using_assignment): Ditto. * sched-deps.c (sched_analyze): Ditto. * combine.c (record_dead_and_set_regs): Ditto. * resource.c (mark_set_resources, mark_set_resources): Ditto. * var-tracking.c (var-tracking.c): Ditto. * reload1.c (reload): Ditto. * cse.c (invalidate_for_call): Ditto. Add parameter. (cse_insn): Pass the parameter. * ira.h (try_to_migrate): Remove the definition. (collect_pseudo_call_clobbered_regs): New external definition. * ira-color.c (collect_pseudo_call_clobbered_regs): New function. * ira-costs.c (tune_pseudo_costs_and_cover_classes): Add cost calculation when IPRA is used. * caller-save.c: Include headers output.h and ira.h. (saved_hard_reg): New structure. (hard_reg_map, hard_reg_map, all_saved_regs): New variables. (all_saved_regs, new_saved_hard_reg, finish_saved_hard_regs, saved_hard_reg_compare_func): New functions. (setup_save_areas): Add code for sharing stack slots. (save_call_clobbered_regs): Use function get_call_invalidated_used_regs. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/ira@120979 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 81 ++++++++++ gcc/Makefile.in | 4 +- gcc/caller-save.c | 417 ++++++++++++++++++++++++++++++++++++++++++++------ gcc/cgraph.c | 1 + gcc/cgraph.h | 5 + gcc/combine.c | 5 +- gcc/common.opt | 4 + gcc/cse.c | 12 +- gcc/cselib.c | 5 +- gcc/df-scan.c | 10 +- gcc/doc/invoke.texi | 7 +- gcc/final.c | 66 ++++++++ gcc/flow.c | 4 +- gcc/function.c | 78 ++++++++++ gcc/function.h | 7 + gcc/gcse.c | 20 ++- gcc/ira-build.c | 2 + gcc/ira-color.c | 24 +++ gcc/ira-conflicts.c | 4 + gcc/ira-costs.c | 35 ++++- gcc/ira.h | 2 +- gcc/loop-iv.c | 4 +- gcc/postreload-gcse.c | 33 ++-- gcc/postreload.c | 9 +- gcc/regrename.c | 11 +- gcc/reload1.c | 10 +- gcc/resource.c | 10 +- gcc/rtlanal.c | 30 ++-- gcc/sched-deps.c | 5 +- gcc/toplev.h | 1 + gcc/var-tracking.c | 16 +- 31 files changed, 810 insertions(+), 112 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ed13d68a54e..e93ef014ece 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,84 @@ +2007-01-19 Vladimir Makarov + + * toplev.h (flag_ipra): New external definition. + + * common.opt (fipra): New option. + + * doc/invoke.texi (-fipra); Document the option. + + * cgraph.h (cgraph_node): New member function_used_regs. + + * cgraph.c (cgraph_create_node): Initialize function_used_regs. + + * final.c (update_regs_ever_live): New function. + (rest_of_handle_final): Set up function_used_regs and + call_used_regs. + + * ira-build.c (ira_build): Clear function call_used_regs. + + * function.h: Add hard-reg-set.h header. + (emit_status): New member call_used_regs. + (get_call_invalidated_used_regs): New definition. + + * function.c (get_call, get_call_invalidated_used_regs): New + functions. + + * Makefile.in (FUNCTION_H): Add hard-reg-set.h. + (caller-save.o): Add dependence on output.h and ira.h + + * regrename.c (copyprop_hardreg_forward_1): Use function + get_call_invalidated_used_regs. + + * ira-conflicts.c (process_bb_node_for_conflicts): Ditto. + + * postreload-gcse.c (record_opr_changes, + reg_set_between_after_reload_p, reg_used_between_after_reload_p): + Ditto. + + * postreload.c (reload_combine, reload_cse_move2add): Ditto. + + * rtlanal.c (reg_set_between_p): Ditto. + + * flow.c (propagate_one_insn): Ditto. + + * df-scan.c (df_insn_refs_record): Ditto. + + * gcse.c (compute_hash_table_work, compute_store_table): Ditto. + + * cselib.c (cselib_process_insn): Ditto. + + * loop-iv.c (simplify_using_assignment): Ditto. + + * sched-deps.c (sched_analyze): Ditto. + + * combine.c (record_dead_and_set_regs): Ditto. + + * resource.c (mark_set_resources, mark_set_resources): Ditto. + + * var-tracking.c (var-tracking.c): Ditto. + + * reload1.c (reload): Ditto. + + * cse.c (invalidate_for_call): Ditto. Add parameter. + (cse_insn): Pass the parameter. + + * ira.h (try_to_migrate): Remove the definition. + (collect_pseudo_call_clobbered_regs): New external definition. + + * ira-color.c (collect_pseudo_call_clobbered_regs): New function. + + * ira-costs.c (tune_pseudo_costs_and_cover_classes): Add cost + calculation when IPRA is used. + + * caller-save.c: Include headers output.h and ira.h. + (saved_hard_reg): New structure. + (hard_reg_map, hard_reg_map, all_saved_regs): New variables. + (all_saved_regs, new_saved_hard_reg, finish_saved_hard_regs, + saved_hard_reg_compare_func): New functions. + (setup_save_areas): Add code for sharing stack slots. + (save_call_clobbered_regs): Use function + get_call_invalidated_used_regs. + 2007-01-12 Vladimir Makarov * doc/tm.texi (IRA_HARD_REGNO_ADD_COST_MULTIPLIER): New macro. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f9a88999493..b0bc2ece92a 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -768,7 +768,7 @@ RECOG_H = recog.h ALIAS_H = alias.h EMIT_RTL_H = emit-rtl.h FLAGS_H = flags.h options.h -FUNCTION_H = function.h $(TREE_H) $(HASHTAB_H) +FUNCTION_H = function.h $(TREE_H) $(HASHTAB_H) hard-reg-set.h EXPR_H = expr.h insn-config.h $(FUNCTION_H) $(RTL_H) $(FLAGS_H) $(TREE_H) $(MACHMODE_H) $(EMIT_RTL_H) OPTABS_H = optabs.h insn-codes.h REGS_H = regs.h varray.h $(MACHMODE_H) $(OBSTACK_H) $(BASIC_BLOCK_H) $(FUNCTION_H) @@ -2542,7 +2542,7 @@ postreload-gcse.o : postreload-gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(PARAMS_H) $(TIMEVAR_H) tree-pass.h $(REAL_H) caller-save.o : caller-save.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(REGS_H) hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) $(FUNCTION_H) \ - addresses.h $(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H) + addresses.h $(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H) output.h ira.h bt-load.o : bt-load.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) except.h \ $(RTL_H) hard-reg-set.h $(REGS_H) $(TM_P_H) $(FIBHEAP_H) output.h $(EXPR_H) \ $(TARGET_H) $(FLAGS_H) $(INSN_ATTR_H) $(FUNCTION_H) tree-pass.h toplev.h diff --git a/gcc/caller-save.c b/gcc/caller-save.c index 1ca61b47a51..11b414a2cc2 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -36,6 +36,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "toplev.h" #include "tm_p.h" #include "addresses.h" +#include "output.h" +#include "ira.h" /* Call used hard registers which can not be saved because there is no insn for this. */ @@ -254,7 +256,9 @@ init_caller_save (void) } } } + + /* Initialize save areas by showing that we haven't allocated any yet. */ void @@ -267,12 +271,104 @@ init_save_areas (void) regno_save_mem[i][j] = 0; } +/* The structure represents a hard register which should be saved + through the call. */ +struct saved_hard_reg +{ + /* Order number starting with 0. */ + int num; + /* The hard regno. */ + int hard_regno; + /* Execution frequency of all calls through which given hard + register should be saved. */ + int call_freq; + /* Stack slot reserved to save the hard register through calls. */ + rtx slot; + /* True if it is first hard register in the chain of hard registers + sharing the same stack slot. */ + int first_p; + /* Order number of the next hard register with the same slot in the + chain. -1 represents end of the chain. */ + int next; +}; + +/* Map: hard register number to the corresponding structure. */ +static struct saved_hard_reg *hard_reg_map [FIRST_PSEUDO_REGISTER]; + +/* The number of all structures representing hard register should be + saved. */ +static int saved_regs_num; + +/* Pointers to all the structures. Index is the order number of the + structure. */ +static struct saved_hard_reg *all_saved_regs [FIRST_PSEUDO_REGISTER]; + +/* First called function for work with saved hard registers. */ +static void +initiate_saved_hard_regs (void) +{ + int i; + + saved_regs_num = 0; + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + hard_reg_map [i] = NULL; +} + +/* Allocate and return new saved hard register with given REGNO and + CALL_FREQ. */ +static struct saved_hard_reg * +new_saved_hard_reg (int regno, int call_freq) +{ + struct saved_hard_reg *saved_reg; + + saved_reg = xmalloc (sizeof (struct saved_hard_reg)); + hard_reg_map [regno] = all_saved_regs [saved_regs_num] = saved_reg; + saved_reg->num = saved_regs_num++; + saved_reg->hard_regno = regno; + saved_reg->call_freq = call_freq; + saved_reg->first_p = FALSE; + saved_reg->next = -1; + return saved_reg; +} + +/* Free memory allocated for the saved hard registers. */ +static void +finish_saved_hard_regs (void) +{ + int i; + + for (i = 0; i < saved_regs_num; i++) + free (all_saved_regs [i]); +} + +/* The function is used to sort the saved hard registers according + their frequency. */ +static int +saved_hard_reg_compare_func (const void *v1p, const void *v2p) +{ + struct saved_hard_reg *p1 = *(struct saved_hard_reg **) v1p; + struct saved_hard_reg *p2 = *(struct saved_hard_reg **) v2p; + + if (flag_omit_frame_pointer) + { + if (p1->call_freq - p2->call_freq != 0) + return p1->call_freq - p2->call_freq; + } + else if (p2->call_freq - p1->call_freq != 0) + return p2->call_freq - p1->call_freq; + + return p1->num - p2->num; +} + /* Allocate save areas for any hard registers that might need saving. We take a conservative approach here and look for call-clobbered hard registers that are assigned to pseudos that cross calls. This may overestimate slightly (especially if some of these registers are later used as spill registers), but it should not be significant. + For IRA we use priority coloring to decrease stack slots needed for + saving hard registers through calls. + Future work: In the fallback case we should iterate backwards across all possible @@ -304,59 +400,274 @@ setup_save_areas (void) unsigned int regno = reg_renumber[i]; unsigned int endregno = regno + hard_regno_nregs[regno][GET_MODE (regno_reg_rtx[i])]; - - for (r = regno; r < endregno; r++) - if (call_used_regs[r]) - SET_HARD_REG_BIT (hard_regs_used, r); + if (flag_ira && flag_ipra) + { + HARD_REG_SET clobbered_regs; + + collect_pseudo_call_clobbered_regs (i, &clobbered_regs); + for (r = regno; r < endregno; r++) + if (TEST_HARD_REG_BIT (clobbered_regs, r)) + SET_HARD_REG_BIT (hard_regs_used, r); + } + else + for (r = regno; r < endregno; r++) + if (call_used_regs[r]) + SET_HARD_REG_BIT (hard_regs_used, r); } - /* Now run through all the call-used hard-registers and allocate - space for them in the caller-save area. Try to allocate space - in a manner which allows multi-register saves/restores to be done. */ - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - for (j = MOVE_MAX_WORDS; j > 0; j--) - { - int do_save = 1; - - /* If no mode exists for this size, try another. Also break out - if we have already saved this hard register. */ - if (regno_save_mode[i][j] == VOIDmode || regno_save_mem[i][1] != 0) - continue; - - /* See if any register in this group has been saved. */ - for (k = 0; k < j; k++) - if (regno_save_mem[i + k][1]) + if (flag_ira) + { + rtx insn, slot; + struct insn_chain *chain, *next; + char *saved_reg_conflicts; + unsigned int regno; + int next_k, freq; + struct saved_hard_reg *saved_reg, *saved_reg2, *saved_reg3; + int call_saved_regs_num; + struct saved_hard_reg *call_saved_regs [FIRST_PSEUDO_REGISTER]; + HARD_REG_SET hard_regs_to_save, used_regs, this_insn_sets; + reg_set_iterator rsi; + + initiate_saved_hard_regs (); + /* Create hard reg saved regs. */ + for (chain = reload_insn_chain; chain != 0; chain = next) + { + insn = chain->insn; + next = chain->next; + if (GET_CODE (insn) != CALL_INSN + || find_reg_note (insn, REG_NORETURN, NULL)) + continue; + freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)); + REG_SET_TO_HARD_REG_SET (hard_regs_to_save, + &chain->live_throughout); + get_call_invalidated_used_regs (insn, &used_regs, false); + + /* Record all registers set in this call insn. These don't + need to be saved. N.B. the call insn might set a subreg + of a multi-hard-reg pseudo; then the pseudo is considered + live during the call, but the subreg that is set + isn't. */ + CLEAR_HARD_REG_SET (this_insn_sets); + note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets); + /* Sibcalls are considered to set the return value, + compare flow.c:propagate_one_insn. */ + if (SIBLING_CALL_P (insn) && current_function_return_rtx) + mark_set_regs (current_function_return_rtx, NULL_RTX, + &this_insn_sets); + + AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set); + AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets); + AND_HARD_REG_SET (hard_regs_to_save, used_regs); + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (hard_regs_to_save, regno)) + { + if (hard_reg_map [regno] != NULL) + hard_reg_map [regno]->call_freq += freq; + else + saved_reg = new_saved_hard_reg (regno, freq); + } + /* Look through all live pseudos, mark their hard registers. */ + EXECUTE_IF_SET_IN_REG_SET + (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi) { - do_save = 0; - break; + int r = reg_renumber [regno]; + int bound; + + if (r < 0) + continue; + + bound = r + hard_regno_nregs [r] [PSEUDO_REGNO_MODE (regno)]; + for (; r < bound; r++) + if (TEST_HARD_REG_BIT (used_regs, r)) + { + if (hard_reg_map [r] != NULL) + hard_reg_map [r]->call_freq += freq; + else + saved_reg = new_saved_hard_reg (r, freq); + } } - if (! do_save) - continue; - - for (k = 0; k < j; k++) - if (! TEST_HARD_REG_BIT (hard_regs_used, i + k)) + } + /* Find saved hard register conflicts. */ + saved_reg_conflicts = xmalloc (saved_regs_num * saved_regs_num); + memset (saved_reg_conflicts, 0, saved_regs_num * saved_regs_num); + for (chain = reload_insn_chain; chain != 0; chain = next) + { + call_saved_regs_num = 0; + insn = chain->insn; + next = chain->next; + if (GET_CODE (insn) != CALL_INSN + || find_reg_note (insn, REG_NORETURN, NULL)) + continue; + REG_SET_TO_HARD_REG_SET (hard_regs_to_save, + &chain->live_throughout); + get_call_invalidated_used_regs (insn, &used_regs, false); + + /* Record all registers set in this call insn. These don't + need to be saved. N.B. the call insn might set a subreg + of a multi-hard-reg pseudo; then the pseudo is considered + live during the call, but the subreg that is set + isn't. */ + CLEAR_HARD_REG_SET (this_insn_sets); + note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets); + /* Sibcalls are considered to set the return value, + compare flow.c:propagate_one_insn. */ + if (SIBLING_CALL_P (insn) && current_function_return_rtx) + mark_set_regs (current_function_return_rtx, NULL_RTX, + &this_insn_sets); + + AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set); + AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets); + AND_HARD_REG_SET (hard_regs_to_save, used_regs); + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (hard_regs_to_save, regno)) + { + gcc_assert (hard_reg_map [regno] != NULL); + call_saved_regs [call_saved_regs_num++] = hard_reg_map [regno]; + } + /* Look through all live pseudos, mark their hard registers. */ + EXECUTE_IF_SET_IN_REG_SET + (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi) { - do_save = 0; - break; + int r = reg_renumber[regno]; + int bound; + + if (r < 0) + continue; + bound = r + hard_regno_nregs [r] [PSEUDO_REGNO_MODE (regno)]; + for (; r < bound; r++) + if (TEST_HARD_REG_BIT (used_regs, r)) + call_saved_regs [call_saved_regs_num++] = hard_reg_map [r]; } - if (! do_save) - continue; - - /* We have found an acceptable mode to store in. */ - regno_save_mem[i][j] - = assign_stack_local (regno_save_mode[i][j], - GET_MODE_SIZE (regno_save_mode[i][j]), 0); - - /* Setup single word save area just in case... */ - for (k = 0; k < j; k++) - /* This should not depend on WORDS_BIG_ENDIAN. - The order of words in regs is the same as in memory. */ - regno_save_mem[i + k][1] - = adjust_address_nv (regno_save_mem[i][j], - regno_save_mode[i + k][1], - k * UNITS_PER_WORD); - } + for (i = 0; i < call_saved_regs_num; i++) + { + saved_reg = call_saved_regs [i]; + for (j = 0; j < call_saved_regs_num; j++) + if (i != j) + { + saved_reg2 = call_saved_regs [j]; + saved_reg_conflicts [saved_reg->num * saved_regs_num + + saved_reg2->num] + = saved_reg_conflicts [saved_reg2->num * saved_regs_num + + saved_reg->num] + = TRUE; + } + } + } + /* Sort saved hard regs. */ + qsort (all_saved_regs, saved_regs_num, sizeof (struct saved_hard_reg *), + saved_hard_reg_compare_func); + /* Allocate stack slots for the saved hard registers. */ + for (i = 0; i < saved_regs_num; i++) + { + saved_reg = all_saved_regs [i]; + for (j = 0; j < i; j++) + { + saved_reg2 = all_saved_regs [j]; + if (! saved_reg2->first_p) + continue; + slot = saved_reg2->slot; + for (k = j; k >= 0; k = next_k) + { + saved_reg3 = all_saved_regs [k]; + next_k = saved_reg3->next; + if (saved_reg_conflicts [saved_reg->num * saved_regs_num + + saved_reg3->num]) + break; + } + if (k < 0 + && (GET_MODE_SIZE (regno_save_mode + [saved_reg->hard_regno] [1]) + <= GET_MODE_SIZE (regno_save_mode + [saved_reg2->hard_regno] [1]))) + { + saved_reg->slot = slot; + regno_save_mem [saved_reg->hard_regno] [1] = slot; + saved_reg->next = saved_reg2->next; + saved_reg2->next = i; + if (dump_file != NULL) + fprintf (dump_file, "%d uses slot of %d\n", + saved_reg->hard_regno, saved_reg2->hard_regno); + break; + } + } + if (j == i) + { + saved_reg->first_p = TRUE; + if (regno_save_mem [saved_reg->hard_regno] [1] != NULL_RTX) + { + saved_reg->slot = regno_save_mem [saved_reg->hard_regno] [1]; + if (dump_file != NULL) + fprintf (dump_file, + "%d uses slot from prev iteration\n", + saved_reg->hard_regno); + } + else + { + saved_reg->slot + = assign_stack_local + (regno_save_mode [saved_reg->hard_regno] [1], + GET_MODE_SIZE (regno_save_mode + [saved_reg->hard_regno] [1]), 0); + regno_save_mem [saved_reg->hard_regno] [1] = saved_reg->slot; + if (dump_file != NULL) + fprintf (dump_file, "%d uses a new slot\n", + saved_reg->hard_regno); + } + } + } + free (saved_reg_conflicts); + finish_saved_hard_regs (); + } + else + { + /* Now run through all the call-used hard-registers and allocate + space for them in the caller-save area. Try to allocate space + in a manner which allows multi-register saves/restores to be done. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + for (j = MOVE_MAX_WORDS; j > 0; j--) + { + int do_save = 1; + + /* If no mode exists for this size, try another. Also break out + if we have already saved this hard register. */ + if (regno_save_mode[i][j] == VOIDmode || regno_save_mem[i][1] != 0) + continue; + + /* See if any register in this group has been saved. */ + for (k = 0; k < j; k++) + if (regno_save_mem[i + k][1]) + { + do_save = 0; + break; + } + if (! do_save) + continue; + + for (k = 0; k < j; k++) + if (! TEST_HARD_REG_BIT (hard_regs_used, i + k)) + { + do_save = 0; + break; + } + if (! do_save) + continue; + + /* We have found an acceptable mode to store in. */ + regno_save_mem[i][j] + = assign_stack_local (regno_save_mode[i][j], + GET_MODE_SIZE (regno_save_mode[i][j]), 0); + + /* Setup single word save area just in case... */ + for (k = 0; k < j; k++) + /* This should not depend on WORDS_BIG_ENDIAN. + The order of words in regs is the same as in memory. */ + regno_save_mem[i + k][1] + = adjust_address_nv (regno_save_mem[i][j], + regno_save_mode[i + k][1], + k * UNITS_PER_WORD); + } + } /* Now loop again and set the alias set of any save areas we made to the alias set used to represent frame objects. */ @@ -366,6 +677,7 @@ setup_save_areas (void) set_mem_alias_set (regno_save_mem[i][j], get_frame_alias_set ()); } + /* Find the places where hard regs are live across calls and save them. */ void @@ -411,13 +723,14 @@ save_call_clobbered_regs (void) for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (referenced_regs, regno)) - regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode); + regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, + save_mode); } if (code == CALL_INSN && ! find_reg_note (insn, REG_NORETURN, NULL)) { unsigned regno; - HARD_REG_SET hard_regs_to_save; + HARD_REG_SET hard_regs_to_save, used_regs; reg_set_iterator rsi; /* Use the register life information in CHAIN to compute which @@ -467,11 +780,13 @@ save_call_clobbered_regs (void) AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set); AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets); AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved); - AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set); + get_call_invalidated_used_regs (insn, &used_regs, false); + AND_HARD_REG_SET (hard_regs_to_save, used_regs); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (hard_regs_to_save, regno)) - regno += insert_save (chain, 1, regno, &hard_regs_to_save, save_mode); + regno += insert_save (chain, 1, regno, &hard_regs_to_save, + save_mode); /* Must recompute n_regs_saved. */ n_regs_saved = 0; diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 7706098ab3c..c550d76fd35 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -171,6 +171,7 @@ cgraph_create_node (void) node->global.estimated_growth = INT_MIN; cgraph_nodes = node; cgraph_n_nodes++; + COPY_HARD_REG_SET (node->function_used_regs, call_used_reg_set); return node; } diff --git a/gcc/cgraph.h b/gcc/cgraph.h index b60239c3662..2e42418cc8f 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -180,6 +180,11 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) into clone before compiling so the function in original form can be inlined later. This pointer points to the clone. */ tree inline_decl; + + /* Call unsaved hard registers really used by the corresponding + function (including ones used by functions called by the + function). */ + HARD_REG_SET function_used_regs; }; struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) diff --git a/gcc/combine.c b/gcc/combine.c index 32117bee219..6ab644f044b 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -11035,8 +11035,11 @@ record_dead_and_set_regs (rtx insn) if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) + if (TEST_HARD_REG_BIT (clobbered_regs, i)) { reg_stat[i].last_set_value = 0; reg_stat[i].last_set_mode = 0; diff --git a/gcc/common.opt b/gcc/common.opt index c792542bd37..33370dd1bdc 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -557,6 +557,10 @@ fipa-type-escape Common Report Var(flag_ipa_type_escape) Init(0) Type based escape and alias analysis +fipra +Common Report Var(flag_ipra) Init(0) +Inter-procedural register allocation for IRA + fira Common Report Var(flag_ira) Use integrated register allocator. diff --git a/gcc/cse.c b/gcc/cse.c index 015e1de66d4..86e0c77aea7 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -586,7 +586,7 @@ static void remove_invalid_subreg_refs (unsigned int, unsigned int, enum machine_mode); static void rehash_using_reg (rtx); static void invalidate_memory (void); -static void invalidate_for_call (void); +static void invalidate_for_call (rtx); static rtx use_related_value (rtx, struct table_elt *); static inline unsigned canon_hash (rtx, enum machine_mode); @@ -1941,21 +1941,23 @@ rehash_using_reg (rtx x) register. Also update their TICK values. */ static void -invalidate_for_call (void) +invalidate_for_call (rtx call_insn) { unsigned int regno, endregno; unsigned int i; unsigned hash; struct table_elt *p, *next; int in_table = 0; + HARD_REG_SET clobbered_regs; /* Go through all the hard registers. For each that is clobbered in a CALL_INSN, remove the register from quantity chains and update reg_tick if defined. Also see if any of these registers is currently in the table. */ + get_call_invalidated_used_regs (call_insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + if (TEST_HARD_REG_BIT (clobbered_regs, regno)) { delete_reg_equiv (regno); if (REG_TICK (regno) >= 0) @@ -1985,7 +1987,7 @@ invalidate_for_call (void) endregno = regno + hard_regno_nregs[regno][GET_MODE (p->exp)]; for (i = regno; i < endregno; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) + if (TEST_HARD_REG_BIT (clobbered_regs, i)) { remove_from_table (p, hash); break; @@ -5291,7 +5293,7 @@ cse_insn (rtx insn, rtx libcall_insn) { if (! CONST_OR_PURE_CALL_P (insn)) invalidate_memory (); - invalidate_for_call (); + invalidate_for_call (insn); } /* Now invalidate everything set by this instruction. diff --git a/gcc/cselib.c b/gcc/cselib.c index 46053885721..6991f3519c3 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -1418,8 +1418,11 @@ cselib_process_insn (rtx insn) memory. */ if (CALL_P (insn)) { + HARD_REG_SET used_regs; + + get_call_invalidated_used_regs (insn, &used_regs, false); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] + if (TEST_HARD_REG_BIT (used_regs, i) || (REG_VALUES (i) && REG_VALUES (i)->elt && HARD_REGNO_CALL_PART_CLOBBERED (i, GET_MODE (REG_VALUES (i)->elt->u.val_rtx)))) diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 46dfb072cce..38c12b12e16 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -1582,6 +1582,8 @@ df_insn_refs_record (struct dataflow *dflow, basic_block bb, rtx insn) { bitmap_iterator bi; unsigned int ui; + HARD_REG_SET clobbered_regs; + /* Calls may also reference any of the global registers, so they are recorded as used. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) @@ -1589,9 +1591,13 @@ df_insn_refs_record (struct dataflow *dflow, basic_block bb, rtx insn) df_uses_record (dflow, ®no_reg_rtx[i], DF_REF_REG_USE, bb, insn, 0); + get_call_invalidated_used_regs (insn, &clobbered_regs, true); EXECUTE_IF_SET_IN_BITMAP (df_invalidated_by_call, 0, ui, bi) - df_ref_record (dflow, regno_reg_rtx[ui], ®no_reg_rtx[ui], bb, - insn, DF_REF_REG_DEF, DF_REF_MAY_CLOBBER, false); + if (ui >= FIRST_PSEUDO_REGISTER + || TEST_HARD_REG_BIT (clobbered_regs, ui)) + df_ref_record (dflow, regno_reg_rtx[ui], ®no_reg_rtx[ui], + bb, insn, DF_REF_REG_DEF, DF_REF_MAY_CLOBBER, + false); } } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index cf3a8c64047..72da102177f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -313,7 +313,7 @@ Objective-C and Objective-C++ Dialects}. -fgcse -fgcse-lm -fgcse-sm -fgcse-las -fgcse-after-reload @gol -fcrossjumping -fif-conversion -fif-conversion2 @gol -finline-functions -finline-functions-called-once @gol --finline-limit=@var{n} -fira -fira-alogirthm=@var{algorithm} @gol +-finline-limit=@var{n} -fipra -fira -fira-alogirthm=@var{algorithm} @gol -fira-biased-coloring -fkeep-inline-functions @gol -fkeep-static-consts -fmerge-constants -fmerge-all-constants @gol -fmodulo-sched -fno-branch-count-reg @gol @@ -5023,6 +5023,11 @@ size register set, the second one is faster and generates decent code and the smallest size code, and the priority-based one is the fastest one. +@item -fipra +@opindex fipra +Switch on a simple form of inter-procedural register allocation when +the integrated register allocator (@acronym{IRA}) is used. + @item -fdelayed-branch @opindex fdelayed-branch If supported for the target machine, attempt to reorder instructions diff --git a/gcc/final.c b/gcc/final.c index ac81cdcfe6d..84c8b8b77e4 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -3893,12 +3893,78 @@ debug_free_queue (void) } } + +/* Update REGS_EVER_LIVE which might be changed after the register + allocator. */ +static void +update_regs_ever_live (rtx x) +{ + int i; + const char *fmt; + RTX_CODE code = GET_CODE (x); + + if (code == REG) + { + if (HARD_REGISTER_P (x)) + regs_ever_live [REGNO (x)] = 1; + return; + } + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + update_regs_ever_live (XEXP (x, i)); + else if (fmt[i] == 'E') + { + int j; + + for (j = 0; j < XVECLEN (x, i); j++) + update_regs_ever_live (XVECEXP (x, i, j)); + } +} + /* Turn the RTL into assembly. */ static unsigned int rest_of_handle_final (void) { + int i; rtx x; const char *fnname; + struct cgraph_node *node; + + gcc_assert (cfun->decl != NULL); + node = cgraph_node (cfun->decl); + if (node != NULL) + { + rtx insn; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + update_regs_ever_live (PATTERN (insn)); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (fixed_regs [i]) + SET_HARD_REG_BIT (cfun->emit->call_used_regs, i); + else if (call_used_regs [i] + && (regs_ever_live [i] +#ifdef STACK_REGS + || (i >= FIRST_STACK_REG && i <= LAST_STACK_REG) +#endif + )) + SET_HARD_REG_BIT (cfun->emit->call_used_regs, i); + COPY_HARD_REG_SET (node->function_used_regs, cfun->emit->call_used_regs); + if (dump_file != NULL) + { + GO_IF_HARD_REG_EQUAL (cfun->emit->call_used_regs, + call_used_reg_set, ok); + fprintf (dump_file, "unused unsaved registers: "); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (call_used_reg_set, i) + && ! TEST_HARD_REG_BIT (cfun->emit->call_used_regs, i)) + fprintf (dump_file, "%s ", reg_names [i]); + fprintf (dump_file, "\n"); + ok: + ; + } + } /* Get the function's name, as described by its RTL. This may be different from the DECL_NAME name used in the source file. */ diff --git a/gcc/flow.c b/gcc/flow.c index 2b5d3032bf7..fe07ec8add8 100644 --- a/gcc/flow.c +++ b/gcc/flow.c @@ -1869,6 +1869,7 @@ propagate_one_insn (struct propagate_block_info *pbi, rtx insn) bool sibcall_p; rtx note, cond; int i; + HARD_REG_SET clobbered_regs; cond = NULL_RTX; if (GET_CODE (PATTERN (insn)) == COND_EXEC) @@ -1899,8 +1900,9 @@ propagate_one_insn (struct propagate_block_info *pbi, rtx insn) sibcall_p = SIBLING_CALL_P (insn); live_at_end = EXIT_BLOCK_PTR->il.rtl->global_live_at_start; + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i) + if (TEST_HARD_REG_BIT (clobbered_regs, i) && ! (sibcall_p && REGNO_REG_SET_P (live_at_end, i) && ! refers_to_regno_p (i, i+1, diff --git a/gcc/function.c b/gcc/function.c index b61b9004a78..fa74b80f527 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -64,6 +64,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-pass.h" #include "predict.h" #include "vecprim.h" +#include "cgraph.h" #ifndef LOCAL_ALIGNMENT #define LOCAL_ALIGNMENT(TYPE, ALIGNMENT) ALIGNMENT @@ -5452,6 +5453,83 @@ current_function_name (void) { return lang_hooks.decl_printable_name (cfun->decl, 2); } + + + +/* This recursive function finds and returns CALL expression in X. */ +static rtx +get_call (rtx x) +{ + int i; + rtx call_rtx; + const char *fmt; + enum rtx_code code = GET_CODE (x); + + /* Ignore registers in memory. */ + if (code == CALL) + return x; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt [i] == 'e') + { + if ((call_rtx = get_call (XEXP (x, i))) != NULL_RTX) + return call_rtx; + } + else if (fmt [i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if ((call_rtx = get_call (XVECEXP (x, i, j))) != NULL_RTX) + return call_rtx; + } + } + return NULL_RTX; +} + +/* This function returns call unsaved registers invalidated (if + CLOBBERED_P) or used by function called by INSN through REGS. */ +void +get_call_invalidated_used_regs (rtx insn, HARD_REG_SET *regs, bool clobbered_p) +{ + rtx x; + struct cgraph_node *node; + tree decl = NULL; + + gcc_assert (CALL_P (insn)); + + x = get_call (PATTERN (insn)); + if (x != NULL_RTX) + { + x = XEXP (x, 0); + gcc_assert (GET_CODE (x) == MEM); + x = XEXP (x, 0); + if (GET_CODE (x) == SYMBOL_REF) + decl = SYMBOL_REF_DECL (x); + if (decl != NULL && TREE_CODE (decl) != FUNCTION_DECL) + decl = NULL; + } + node = decl == NULL ? NULL : cgraph_node (decl); + if (! flag_ira || ! flag_ipra || node == NULL + /* This is a call of the function itself. We don't know used + register yet. So take the worst case. */ + || node->decl == cfun->decl) + { + if (clobbered_p) + COPY_HARD_REG_SET (*regs, regs_invalidated_by_call); + else + COPY_HARD_REG_SET (*regs, call_used_reg_set); + } + else + { + COPY_HARD_REG_SET (*regs, node->function_used_regs); + if (clobbered_p) + AND_HARD_REG_SET (*regs, regs_invalidated_by_call); + } +} + static unsigned int diff --git a/gcc/function.h b/gcc/function.h index 27e5c02cbba..22abed5e9ca 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -24,6 +24,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree.h" #include "hashtab.h" +#include "hard-reg-set.h" struct var_refs_queue GTY(()) { @@ -102,6 +103,10 @@ struct emit_status GTY(()) /* Indexed by pseudo register number, gives the rtx for that pseudo. Allocated in parallel with regno_pointer_align. */ rtx * GTY ((length ("%h.x_reg_rtx_no"))) x_regno_reg_rtx; + + /* Call unsaved hard registers really used by given function + (including ones used by functions called by given function). */ + HARD_REG_SET call_used_regs; }; /* For backward compatibility... eventually these should all go away. */ @@ -586,6 +591,8 @@ extern rtx get_arg_pointer_save_area (struct function *); /* Returns the name of the current function. */ extern const char *current_function_name (void); +extern void get_call_invalidated_used_regs (rtx, HARD_REG_SET *, bool); + extern void do_warn_unused_parameter (tree); extern bool pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, diff --git a/gcc/gcse.c b/gcc/gcse.c index 0518e4c597a..e6fef41ab62 100644 --- a/gcc/gcse.c +++ b/gcc/gcse.c @@ -2063,8 +2063,11 @@ compute_hash_table_work (struct hash_table *table) if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + if (TEST_HARD_REG_BIT (clobbered_regs, regno)) record_last_reg_set_info (insn, regno); mark_call (insn); @@ -5765,8 +5768,11 @@ compute_store_table (void) if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + if (TEST_HARD_REG_BIT (clobbered_regs, regno)) { last_set_in[regno] = INSN_UID (insn); SET_BIT (reg_set_in_block[bb->index], regno); @@ -5788,8 +5794,11 @@ compute_store_table (void) if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + if (TEST_HARD_REG_BIT (clobbered_regs, regno)) already_set[regno] = 1; } @@ -5804,8 +5813,11 @@ compute_store_table (void) note_stores (pat, reg_clear_last_set, last_set_in); if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno) + if (TEST_HARD_REG_BIT (clobbered_regs, regno) && last_set_in[regno] == INSN_UID (insn)) last_set_in[regno] = 0; } diff --git a/gcc/ira-build.c b/gcc/ira-build.c index d21630bdf81..387314eb201 100644 --- a/gcc/ira-build.c +++ b/gcc/ira-build.c @@ -856,6 +856,8 @@ ira_build (int loops_p) df_ri_add_problem (build_df, DF_RI_LIFE); df_analyze (build_df); + CLEAR_HARD_REG_SET (cfun->emit->call_used_regs); + initiate_pseudos (); initiate_copies (); ira_assert (current_loops == NULL); diff --git a/gcc/ira-color.c b/gcc/ira-color.c index 14eca58897e..fc57b1434fe 100644 --- a/gcc/ira-color.c +++ b/gcc/ira-color.c @@ -1278,6 +1278,30 @@ mark_new_stack_slot (rtx x, int regno, unsigned int total_size) +/* The function returns (through CALL_CLOBBERED_REGS) hard registers + changed by all function calls in REGNO live range. */ +void +collect_pseudo_call_clobbered_regs (int regno, + HARD_REG_SET (*call_clobbered_regs)) +{ + int i; + pseudo_t p; + HARD_REG_SET clobbered_regs; + rtx call, *pseudo_calls; + + p = regno_pseudo_map [regno]; + CLEAR_HARD_REG_SET (*call_clobbered_regs); + pseudo_calls = PSEUDO_CALLS_CROSSED (p); + for (i = PSEUDO_CALLS_CROSSED_NUM (p) - 1; i >= 0; i--) + { + call = pseudo_calls [i]; + get_call_invalidated_used_regs (call, &clobbered_regs, FALSE); + IOR_HARD_REG_SET (*call_clobbered_regs, clobbered_regs); + } +} + + + /* Update avaliable hard registers for each pseudo. */ static void update_pseudo_avaialable_regs (void) diff --git a/gcc/ira-conflicts.c b/gcc/ira-conflicts.c index 1f02a9c1667..f3da654c6ac 100644 --- a/gcc/ira-conflicts.c +++ b/gcc/ira-conflicts.c @@ -1156,6 +1156,10 @@ process_bb_node_for_conflicts (struct ira_loop_tree_node *loop_tree_node) if (CALL_P (insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, FALSE); + IOR_HARD_REG_SET (cfun->emit->call_used_regs, clobbered_regs); EXECUTE_IF_SET_IN_PSEUDO_SET (pseudos_live, i, { int freq; diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c index bb3598f9166..60caf162910 100644 --- a/gcc/ira-costs.c +++ b/gcc/ira-costs.c @@ -1344,10 +1344,12 @@ ira_costs (void) void tune_pseudo_costs_and_cover_classes (void) { - int i, j, n, regno, cost, min_cost, *reg_costs; + int i, j, k, n, regno, cost, min_cost, *reg_costs, freq; enum reg_class cover_class, class; enum machine_mode mode; pseudo_t p; + rtx call, *pseudo_calls; + HARD_REG_SET clobbered_regs; for (i = 0; i < pseudos_num; i++) { @@ -1365,11 +1367,32 @@ tune_pseudo_costs_and_cover_classes (void) regno = class_hard_regs [cover_class] [j]; class = REGNO_REG_CLASS (regno); cost = 0; - /* ??? If only part is call clobbered. */ - if (! hard_reg_not_in_set_p (regno, mode, call_used_reg_set)) - cost += (PSEUDO_CALL_FREQ (p) - * (memory_move_cost [mode] [class] [0] - + memory_move_cost [mode] [class] [1])); + if (! flag_ipra) + { + /* ??? If only part is call clobbered. */ + if (! hard_reg_not_in_set_p (regno, mode, call_used_reg_set)) + cost += (PSEUDO_CALL_FREQ (p) + * (memory_move_cost [mode] [class] [0] + + memory_move_cost [mode] [class] [1])); + } + else + { + pseudo_calls = PSEUDO_CALLS_CROSSED (p); + ira_assert (pseudo_calls != NULL); + for (k = PSEUDO_CALLS_CROSSED_NUM (p) - 1; k >= 0; k--) + { + call = pseudo_calls [k]; + freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (call)); + if (freq == 0) + freq = 1; + get_call_invalidated_used_regs (call, &clobbered_regs, + FALSE); + /* ??? If only part is call clobbered. */ + if (! hard_reg_not_in_set_p (regno, mode, clobbered_regs)) + cost += freq * (memory_move_cost [mode] [class] [0] + + memory_move_cost [mode] [class] [1]); + } + } #ifdef IRA_HARD_REGNO_ADD_COST_MULTIPLIER cost += ((memory_move_cost [mode] [class] [0] + memory_move_cost [mode] [class] [1]) * PSEUDO_FREQ (p) diff --git a/gcc/ira.h b/gcc/ira.h index 045b7c939b3..e97af44f79f 100644 --- a/gcc/ira.h +++ b/gcc/ira.h @@ -57,7 +57,7 @@ extern rtx ira_eliminate_regs (rtx, enum machine_mode); extern void ira (FILE *); extern void mark_allocation_change (int); -extern void try_to_migrate (int, HARD_REG_SET, void *, HARD_REG_SET *); extern void retry_ira_color (int, HARD_REG_SET); extern rtx reuse_stack_slot (int, unsigned int, unsigned int); extern void mark_new_stack_slot (rtx, int, unsigned int); +extern void collect_pseudo_call_clobbered_regs (int, HARD_REG_SET *); diff --git a/gcc/loop-iv.c b/gcc/loop-iv.c index fcf4a2525af..a2753386a75 100644 --- a/gcc/loop-iv.c +++ b/gcc/loop-iv.c @@ -1404,10 +1404,12 @@ simplify_using_assignment (rtx insn, rtx *expr, regset altered) if (CALL_P (insn)) { int i; + HARD_REG_SET clobbered_regs; /* Kill all call clobbered registers. */ + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) + if (TEST_HARD_REG_BIT (clobbered_regs, i)) SET_REGNO_REG_SET (altered, i); } diff --git a/gcc/postreload-gcse.c b/gcc/postreload-gcse.c index 76b7b8c9ae0..599ed2190fc 100644 --- a/gcc/postreload-gcse.c +++ b/gcc/postreload-gcse.c @@ -718,9 +718,11 @@ record_opr_changes (rtx insn) if (CALL_P (insn)) { unsigned int regno; - + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + if (TEST_HARD_REG_BIT (clobbered_regs, regno)) record_last_reg_set_info (insn, regno); if (! CONST_OR_PURE_CALL_P (insn)) @@ -879,9 +881,15 @@ reg_set_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) { if (set_of (reg, insn) != NULL_RTX) return insn; - if ((CALL_P (insn) - && call_used_regs[REGNO (reg)]) - || find_reg_fusage (insn, CLOBBER, reg)) + if (CALL_P (insn)) + { + HARD_REG_SET used_regs; + + get_call_invalidated_used_regs (insn, &used_regs, false); + if (TEST_HARD_REG_BIT (used_regs, REGNO (reg))) + return insn; + } + if (find_reg_fusage (insn, CLOBBER, reg)) return insn; if (FIND_REG_INC_NOTE (insn, reg)) @@ -911,10 +919,17 @@ reg_used_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) insn = NEXT_INSN (insn)) if (INSN_P (insn)) { - if (reg_overlap_mentioned_p (reg, PATTERN (insn)) - || (CALL_P (insn) - && call_used_regs[REGNO (reg)]) - || find_reg_fusage (insn, USE, reg) + if (reg_overlap_mentioned_p (reg, PATTERN (insn))) + return insn; + if (CALL_P (insn)) + { + HARD_REG_SET used_regs; + + get_call_invalidated_used_regs (insn, &used_regs, false); + if (TEST_HARD_REG_BIT (used_regs, REGNO (reg))) + return insn; + } + if (find_reg_fusage (insn, USE, reg) || find_reg_fusage (insn, CLOBBER, reg)) return insn; diff --git a/gcc/postreload.c b/gcc/postreload.c index 5f4ae4f5b18..b5efbbbf282 100644 --- a/gcc/postreload.c +++ b/gcc/postreload.c @@ -917,9 +917,11 @@ reload_combine (void) if (CALL_P (insn)) { rtx link; + HARD_REG_SET used_regs; + get_call_invalidated_used_regs (insn, &used_regs, false); for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) - if (call_used_regs[r]) + if (TEST_HARD_REG_BIT (used_regs, r)) { reg_state[r].use_index = RELOAD_COMBINE_MAX_USES; reg_state[r].store_ruid = reload_combine_ruid; @@ -1411,9 +1413,12 @@ reload_cse_move2add (rtx first) unknown values. */ if (CALL_P (insn)) { + HARD_REG_SET used_regs; + + get_call_invalidated_used_regs (insn, &used_regs, false); for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) { - if (call_used_regs[i]) + if (TEST_HARD_REG_BIT (used_regs, i)) /* Reset the information about this register. */ reg_set_luid[i] = 0; } diff --git a/gcc/regrename.c b/gcc/regrename.c index c55f9c4baeb..6f93a597a0a 100644 --- a/gcc/regrename.c +++ b/gcc/regrename.c @@ -1789,9 +1789,14 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd) did_replacement: /* Clobber call-clobbered registers. */ if (CALL_P (insn)) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) - kill_value_regno (i, 1, vd); + { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (clobbered_regs, i)) + kill_value_regno (i, 1, vd); + } /* Notice stores. */ note_stores (PATTERN (insn), kill_set_value, vd); diff --git a/gcc/reload1.c b/gcc/reload1.c index d99b0e3953c..f839d8ffe51 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -1213,8 +1213,14 @@ reload (rtx first, int global) } if (CALL_P (insn)) - replace_pseudos_in (& CALL_INSN_FUNCTION_USAGE (insn), - VOIDmode, CALL_INSN_FUNCTION_USAGE (insn)); + { + HARD_REG_SET used_function_regs; + + get_call_invalidated_used_regs (insn, &used_function_regs, false); + IOR_HARD_REG_SET (cfun->emit->call_used_regs, used_function_regs); + replace_pseudos_in (& CALL_INSN_FUNCTION_USAGE (insn), + VOIDmode, CALL_INSN_FUNCTION_USAGE (insn)); + } if ((GET_CODE (PATTERN (insn)) == USE /* We mark with QImode USEs introduced by reload itself. */ diff --git a/gcc/resource.c b/gcc/resource.c index cd4eb10628c..215ae5a49e5 100644 --- a/gcc/resource.c +++ b/gcc/resource.c @@ -662,10 +662,12 @@ mark_set_resources (rtx x, struct resources *res, int in_dest, if (mark_type == MARK_SRC_DEST_CALL) { rtx link; + HARD_REG_SET used_regs; + get_call_invalidated_used_regs (x, &used_regs, false); res->cc = res->memory = 1; for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) - if (call_used_regs[r] || global_regs[r]) + if (TEST_HARD_REG_BIT (used_regs, r) || global_regs[r]) SET_HARD_REG_BIT (res->regs, r); for (link = CALL_INSN_FUNCTION_USAGE (x); @@ -1024,11 +1026,13 @@ mark_target_live_regs (rtx insns, rtx target, struct resources *res) if (CALL_P (real_insn)) { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); /* CALL clobbers all call-used regs that aren't fixed except sp, ap, and fp. Do this before setting the result of the call live. */ - AND_COMPL_HARD_REG_SET (current_live_regs, - regs_invalidated_by_call); + AND_COMPL_HARD_REG_SET (current_live_regs, clobbered_regs); /* A CALL_INSN sets any global register live, since it may have been modified by the call. */ diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 4b965f8ca1c..0644cdc5763 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -747,18 +747,24 @@ reg_set_between_p (rtx reg, rtx from_insn, rtx to_insn) int reg_set_p (rtx reg, rtx insn) { - /* We can be passed an insn or part of one. If we are passed an insn, - check if a side-effect of the insn clobbers REG. */ - if (INSN_P (insn) - && (FIND_REG_INC_NOTE (insn, reg) - || (CALL_P (insn) - && ((REG_P (reg) - && REGNO (reg) < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (regs_invalidated_by_call, - REGNO (reg))) - || MEM_P (reg) - || find_reg_fusage (insn, CLOBBER, reg))))) - return 1; + if (INSN_P (insn)) + { + if (FIND_REG_INC_NOTE (insn, reg)) + return 1; + if (CALL_P (insn)) + { + if (REG_P (reg) && REGNO (reg) < FIRST_PSEUDO_REGISTER) + { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); + if (TEST_HARD_REG_BIT (clobbered_regs, REGNO (reg))) + return 1; + } + if (MEM_P (reg) || find_reg_fusage (insn, CLOBBER, reg)) + return 1; + } + } return set_of (reg, insn) != NULL_RTX; } diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c index 0cde4f2e94f..c252e986f2f 100644 --- a/gcc/sched-deps.c +++ b/gcc/sched-deps.c @@ -1509,6 +1509,9 @@ sched_analyze (struct deps *deps, rtx head, rtx tail) } else { + HARD_REG_SET clobbered_regs; + + get_call_invalidated_used_regs (insn, &clobbered_regs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) /* A call may read and modify global register variables. */ if (global_regs[i]) @@ -1521,7 +1524,7 @@ sched_analyze (struct deps *deps, rtx head, rtx tail) and 'definitely not clobbered', we must include all partly call-clobbered registers here. */ else if (HARD_REGNO_CALL_PART_CLOBBERED (i, reg_raw_mode[i]) - || TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) + || TEST_HARD_REG_BIT (clobbered_regs, i)) SET_REGNO_REG_SET (reg_pending_clobbers, i); /* We don't know what set of fixed registers might be used by the function, but it is certain that the stack pointer diff --git a/gcc/toplev.h b/gcc/toplev.h index 7bc779b7635..651b571d871 100644 --- a/gcc/toplev.h +++ b/gcc/toplev.h @@ -132,6 +132,7 @@ extern int flag_unroll_all_loops; extern int flag_unswitch_loops; extern int flag_cprop_registers; extern int time_report; +extern int flag_ipra; extern int flag_ira; extern int flag_ira_biased_coloring; diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index 815facae182..5c5f989ed44 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -1724,9 +1724,15 @@ compute_bb_dataflow (basic_block bb) switch (VTI (bb)->mos[i].type) { case MO_CALL: - for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) - if (TEST_HARD_REG_BIT (call_used_reg_set, r)) - var_regno_delete (out, r); + { + HARD_REG_SET used_regs; + + get_call_invalidated_used_regs (VTI (bb)->mos[i].insn, + &used_regs, false); + for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) + if (TEST_HARD_REG_BIT (used_regs, r)) + var_regno_delete (out, r); + } break; case MO_USE: @@ -2589,9 +2595,11 @@ emit_notes_in_bb (basic_block bb) case MO_CALL: { int r; + HARD_REG_SET used_regs; + get_call_invalidated_used_regs (insn, &used_regs, false); for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) - if (TEST_HARD_REG_BIT (call_used_reg_set, r)) + if (TEST_HARD_REG_BIT (used_regs, r)) { var_regno_delete (&set, r); } -- 2.11.4.GIT