[multiple changes]
2008-08-26 Vladimir Makarov <vmakarov@redhat.com> * ira-build.c, ira-color.c, ira-costs.c, ira.h, ira-lives.c, ira.c, ira-conflicts.c, ira-emit.c, ira-int.h: New files. * doc/passes.texi: Describe IRA. * doc/tm.texi (IRA_COVER_CLASSES, IRA_HARD_REGNO_ADD_COST_MULTIPLIER): Describe the new macros. * doc/invoke.texi (ira-max-loops-num): Describe the new parameter. (-fira, -fira-algorithm, -fira-coalesce, -fno-ira-move-spills, -fira-propagate-cost, -fno-ira-share-save-slots, -fno-ira-share-spill-slots, -fira-verbose): Describe new options. * flags.h (ira_algorithm): New enumeration. (flag_ira_algorithm, flag_ira_verbose): New external variable declarations. * postreload.c (gate_handle_postreload): Don't do post reload optimizations unless the reload is completed. * reload.c (push_reload, find_dummy_reload): Use DF_LR_OUT for IRA. * tree-pass.h (pass_ira): New external variable declaration. * reload.h: Add 2008 to the Copyright. * cfgloopanal.c: Include params.h. (estimate_reg_pressure_cost): Decrease cost for IRA optimization mode. * params.h (IRA_MAX_LOOPS_NUM): New macro. * toplev.c (ira.h): New include. (flag_ira_algorithm, flag_ira_verbose): New external variables. (backend_init_target): Call ira_init. (backend_init): Call ira_init_once. (finalize): Call finish_ira_once. * toplev.h (flag_ira, flag_ira_coalesce, flag_ira_move_spills, flag_ira_share_save_slots, flag_ira_share_spill_slots): New external variables. * regs.h (contains_reg_of_mode, move_cost, may_move_in_cost, may_move_out_cost): New external variable declarations. (move_table): New typedef. * caller-save.c: Include headers output.h and ira.h. (no_caller_save_reg_set): New global variable. (save_slots_num, save_slots): New variables. (reg_save_code, reg_restore_code, add_stored_regs): Add prototypes. (init_caller_save): Set up no_caller_save_reg_set. (init_save_areas): Reset save_slots_num. (saved_hard_reg): New structure. (hard_reg_map, saved_regs_num, all_saved_regs): New variables. (initiate_saved_hard_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. (all_blocks): New variable. (save_call_clobbered_regs): Process pseudo-register too. (mark_set_regs): Process pseudo-register too. (insert_one_insn): Put the insn after bb note in a empty basic block. Add insn check. * global.c (eliminable_regset): Make it external. (mark_elimination): Use DF_LR_IN for IRA. (pseudo_for_reload_consideration_p): New. (build_insn_chain): Make it external. Don't ignore spilled pseudos for IRA. Use pseudo_for_reload_consideration_p. (gate_handle_global_alloc): New function. (pass_global_alloc): Add the gate function. * opts.c (decode_options): Set up flag_ira. Print the warning for -fira. (common_handle_option): Process -fira-algorithm and -fira-verbose. * timevar.def (TV_IRA, TV_RELOAD): New passes. * regmove.c (regmove_optimize): Don't do replacement of output for IRA. * hard-reg-set.h (no_caller_save_reg_set, reg_class_subclasses): New external variable declarations. * local-alloc.c (update_equiv_regs): Make it external. Return true if jump label rebuilding should be done. Rescan new_insn for notes. (gate_handle_local_alloc): New function. (pass_local_alloc): Add the gate function. * alias.c (value_addr_p, stack_addr_p): New functions. (nonoverlapping_memrefs_p): Use them for IRA. * common.opt (fira, fira-algorithm, fira-coalesce, fira-move-spills, fira-share-save-slots, fira-share-spill-slots, fira-verbose): New options. * regclass.c (reg_class_subclasses, contains_reg_of_mode, move_cost, may_move_in_cost, may_move_out_cost): Make the variables external. (move_table): Remove typedef. (init_move_cost): Make it external. (allocate_reg_info, resize_reg_info, setup_reg_classes): New functions. * rtl.h (init_move_cost, allocate_reg_info, resize_reg_info, setup_reg_classes): New function prototypes. (eliminable_regset): New external variable declaration. (build_insn_chain, update_equiv_regs): New function prototypes. * Makefile.in (IRA_INT_H): New definition. (OBJS-common): Add ira.o, ira-build.o, ira-costs.o, ira-conflicts.o, ira-color.o, ira-emit.o, and ira-lives.o. (reload1.o, toplev.o): Add dependence on ira.h. (cfgloopanal.o): Add PARAMS_H. (caller-save.o): Add dependence on output.h and ira.h. (ira.o, ira-build.o, ira-costs.o, ira-conflicts.o, ira-color.o, ira-emit.o, ira-lives.o): New entries. * passes.c (pass_ira): New pass. * params.def (PARAM_IRA_MAX_LOOPS_NUM): New parameter. * reload1.c (ira.h): Include the header. (changed_allocation_pseudos): New bitmap. (init_reload): Initiate the bitmap. (compute_use_by_pseudos): Permits spilled registers in FROM. (temp_pseudo_reg_arr): New variable. (reload): Allocate and free temp_pseudo_reg_arr. Sort pseudos for IRA. Call alter_reg with the additional parameter. Don't clear spilled_pseudos for IRA. Restore original insn chain for IRA. Clear changed_allocation_pseudos at the end of reload. (calculate_needs_all_insns): Call IRA's mark_memory_move_deletion. (hard_regno_to_pseudo_regno): New variable. (count_pseudo): Check spilled pseudos. Set up hard_regno_to_pseudo_regno. (count_spilled_pseudo): Check spilled pseudos. Update hard_regno_to_pseudo_regno. (find_reg): Use better_spill_reload_regno_p. Check hard_regno_to_pseudo_regno. (alter_reg): Set up spilled_pseudos. Add a new parameter. Add code for IRA. (eliminate_regs_1): Use additional parameter for alter_reg. (finish_spills): Set up pseudo_previous_regs only for spilled pseudos. Call reassign_pseudos once for all spilled pseudos, pass more arguments. Don't clear live_throughout and dead_or_set for spilled pseudos. Use additional parameter for alter_reg. Call mark_allocation_change. Set up changed_allocation_pseudos. Remove sanity check. (emit_input_reload_insns, delete_output_reload): Use additional parameter for alter_reg. Call mark_allocation_change. (substitute, gen_reload_chain_without_interm_reg_p): New functions. (reloads_conflict): Use gen_reload_chain_without_interm_reg_p. * testsuite/gcc.dg/20080410-1.c: New file. * config/s390/s390.h (IRA_COVER_CLASSES, IRA_HARD_REGNO_ADD_COST_MULTIPLIER): Define. * config/sparc/sparc.h (IRA_COVER_CLASSES): New macro. * config/i386/i386.h (IRA_COVER_CLASSES): Ditto. * config/ia64/ia64.h (IRA_COVER_CLASSES): Ditto. * config/rs6000/rs6000.h (IRA_COVER_CLASSES): Ditto. * config/arm/arm.h (IRA_COVER_CLASSES): Ditto. * config/alpha/alpha.h (IRA_COVER_CLASSES): Ditto. 2008-08-24 Jeff Law <law@redhat.com> * ira.c (setup_reg_class_intersect_union): Prefer smallest class when ignoring unavailable registers. 2008-08-24 Jeff Law <law@redhat.com> * ira-color.c (coalesced_pseudo_reg_slot_compare): Check FRAME_GROWS_DOWNWARD and STACK_GROWS_DOWNWARD. * ira.c (setup_eliminable_regset): Check stack_realign_needed. * config/mn10300/mn10300.h (IRA_COVER_CLASSES): New macro. 2008-06-03 Steve Chamberlain <steve.chamberlain@gmail.com> * ira-build.c (allocno_range_compare_func): Stabilize sort. 2008-05-29 Andy Hutchinson <hutchinsonandy@aim.com> * config/avr/avr.h (IRA_COVER_CLASSES): New macro. * reload1.c (find_reg): Process registers in register allocation order. 2008-05-10 Richard Sandiford <rsandifo@nildram.co.uk> * toplev.c (backend_init_target): Move ira_init call from here... (lang_dependent_init_target): ...to here. 2008-05-10 Richard Sandiford <rsandifo@nildram.co.uk> * ira.c (setup_class_subset_and_memory_move_costs): Don't calculate memory move costs for NO_REGS. 2008-05-05 Kaz Kojima <kkojima@gcc.gnu.org> * ira-color.c (ira_fast_allocation): Use no_stack_reg_p only if STACK_REGS is defined. 2008-04-08 Andrew Pinski <andrew_pinski@playstation.sony.com> * config/spu/spu.h (IRA_COVER_CLASSES): New macro. 2008-04-04 Bernd Schmidt <bernd.schmidt@analog.com> * config/bfin/bfin.h (IRA_COVER_CLASSES): New macro. 2008-04-04 Kaz Kojima <kkojima@gcc.gnu.org> * config/sh/sh.h (IRA_COVER_CLASSES): Define. * config/sh/sh.md (movsicc_true+3): Check if emit returns a barrier. From-SVN: r139590
This commit is contained in:
parent
8ff27c248c
commit
058e97ecf3
52 changed files with 14704 additions and 176 deletions
217
gcc/ChangeLog
217
gcc/ChangeLog
|
@ -1,3 +1,220 @@
|
|||
2008-08-26 Vladimir Makarov <vmakarov@redhat.com>
|
||||
|
||||
* ira-build.c, ira-color.c, ira-costs.c, ira.h, ira-lives.c,
|
||||
ira.c, ira-conflicts.c, ira-emit.c, ira-int.h: New files.
|
||||
|
||||
* doc/passes.texi: Describe IRA.
|
||||
|
||||
* doc/tm.texi (IRA_COVER_CLASSES,
|
||||
IRA_HARD_REGNO_ADD_COST_MULTIPLIER): Describe the new macros.
|
||||
|
||||
* doc/invoke.texi (ira-max-loops-num): Describe the new parameter.
|
||||
(-fira, -fira-algorithm, -fira-coalesce, -fno-ira-move-spills,
|
||||
-fira-propagate-cost, -fno-ira-share-save-slots,
|
||||
-fno-ira-share-spill-slots, -fira-verbose): Describe new options.
|
||||
|
||||
* flags.h (ira_algorithm): New enumeration.
|
||||
(flag_ira_algorithm, flag_ira_verbose): New external variable
|
||||
declarations.
|
||||
|
||||
* postreload.c (gate_handle_postreload): Don't do post reload
|
||||
optimizations unless the reload is completed.
|
||||
|
||||
* reload.c (push_reload, find_dummy_reload): Use DF_LR_OUT for
|
||||
IRA.
|
||||
|
||||
* tree-pass.h (pass_ira): New external variable declaration.
|
||||
|
||||
* reload.h: Add 2008 to the Copyright.
|
||||
|
||||
* cfgloopanal.c: Include params.h.
|
||||
(estimate_reg_pressure_cost): Decrease cost for IRA optimization
|
||||
mode.
|
||||
|
||||
* params.h (IRA_MAX_LOOPS_NUM): New macro.
|
||||
|
||||
* toplev.c (ira.h): New include.
|
||||
(flag_ira_algorithm, flag_ira_verbose): New external variables.
|
||||
(backend_init_target): Call ira_init.
|
||||
(backend_init): Call ira_init_once.
|
||||
(finalize): Call finish_ira_once.
|
||||
|
||||
* toplev.h (flag_ira, flag_ira_coalesce, flag_ira_move_spills,
|
||||
flag_ira_share_save_slots, flag_ira_share_spill_slots): New
|
||||
external variables.
|
||||
|
||||
* regs.h (contains_reg_of_mode, move_cost, may_move_in_cost,
|
||||
may_move_out_cost): New external variable declarations.
|
||||
(move_table): New typedef.
|
||||
|
||||
* caller-save.c: Include headers output.h and ira.h.
|
||||
(no_caller_save_reg_set): New global variable.
|
||||
(save_slots_num, save_slots): New variables.
|
||||
(reg_save_code, reg_restore_code, add_stored_regs): Add
|
||||
prototypes.
|
||||
(init_caller_save): Set up no_caller_save_reg_set.
|
||||
(init_save_areas): Reset save_slots_num.
|
||||
(saved_hard_reg): New structure.
|
||||
(hard_reg_map, saved_regs_num, all_saved_regs): New variables.
|
||||
(initiate_saved_hard_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.
|
||||
(all_blocks): New variable.
|
||||
(save_call_clobbered_regs): Process pseudo-register too.
|
||||
(mark_set_regs): Process pseudo-register too.
|
||||
(insert_one_insn): Put the insn after bb note in a empty basic
|
||||
block. Add insn check.
|
||||
|
||||
* global.c (eliminable_regset): Make it external.
|
||||
(mark_elimination): Use DF_LR_IN for IRA.
|
||||
(pseudo_for_reload_consideration_p): New.
|
||||
(build_insn_chain): Make it external. Don't ignore spilled
|
||||
pseudos for IRA. Use pseudo_for_reload_consideration_p.
|
||||
(gate_handle_global_alloc): New function.
|
||||
(pass_global_alloc): Add the gate function.
|
||||
|
||||
* opts.c (decode_options): Set up flag_ira. Print the warning for
|
||||
-fira.
|
||||
(common_handle_option): Process -fira-algorithm and -fira-verbose.
|
||||
|
||||
* timevar.def (TV_IRA, TV_RELOAD): New passes.
|
||||
|
||||
* regmove.c (regmove_optimize): Don't do replacement of output for
|
||||
IRA.
|
||||
|
||||
* hard-reg-set.h (no_caller_save_reg_set, reg_class_subclasses):
|
||||
New external variable declarations.
|
||||
|
||||
* local-alloc.c (update_equiv_regs): Make it external. Return
|
||||
true if jump label rebuilding should be done. Rescan new_insn for
|
||||
notes.
|
||||
(gate_handle_local_alloc): New function.
|
||||
(pass_local_alloc): Add the gate function.
|
||||
|
||||
* alias.c (value_addr_p, stack_addr_p): New functions.
|
||||
(nonoverlapping_memrefs_p): Use them for IRA.
|
||||
|
||||
* common.opt (fira, fira-algorithm, fira-coalesce,
|
||||
fira-move-spills, fira-share-save-slots, fira-share-spill-slots,
|
||||
fira-verbose): New options.
|
||||
|
||||
* regclass.c (reg_class_subclasses, contains_reg_of_mode,
|
||||
move_cost, may_move_in_cost, may_move_out_cost): Make the
|
||||
variables external.
|
||||
(move_table): Remove typedef.
|
||||
(init_move_cost): Make it external.
|
||||
(allocate_reg_info, resize_reg_info, setup_reg_classes): New
|
||||
functions.
|
||||
|
||||
* rtl.h (init_move_cost, allocate_reg_info, resize_reg_info,
|
||||
setup_reg_classes): New function prototypes.
|
||||
(eliminable_regset): New external variable declaration.
|
||||
(build_insn_chain, update_equiv_regs): New function prototypes.
|
||||
|
||||
* Makefile.in (IRA_INT_H): New definition.
|
||||
(OBJS-common): Add ira.o, ira-build.o, ira-costs.o,
|
||||
ira-conflicts.o, ira-color.o, ira-emit.o, and ira-lives.o.
|
||||
(reload1.o, toplev.o): Add dependence on ira.h.
|
||||
(cfgloopanal.o): Add PARAMS_H.
|
||||
(caller-save.o): Add dependence on output.h and ira.h.
|
||||
(ira.o, ira-build.o, ira-costs.o, ira-conflicts.o, ira-color.o,
|
||||
ira-emit.o, ira-lives.o): New entries.
|
||||
|
||||
* passes.c (pass_ira): New pass.
|
||||
|
||||
* params.def (PARAM_IRA_MAX_LOOPS_NUM): New parameter.
|
||||
|
||||
* reload1.c (ira.h): Include the header.
|
||||
(changed_allocation_pseudos): New bitmap.
|
||||
(init_reload): Initiate the bitmap.
|
||||
(compute_use_by_pseudos): Permits spilled registers in FROM.
|
||||
(temp_pseudo_reg_arr): New variable.
|
||||
(reload): Allocate and free temp_pseudo_reg_arr. Sort pseudos for
|
||||
IRA. Call alter_reg with the additional parameter. Don't clear
|
||||
spilled_pseudos for IRA. Restore original insn chain for IRA.
|
||||
Clear changed_allocation_pseudos at the end of reload.
|
||||
(calculate_needs_all_insns): Call IRA's mark_memory_move_deletion.
|
||||
(hard_regno_to_pseudo_regno): New variable.
|
||||
(count_pseudo): Check spilled pseudos. Set up
|
||||
hard_regno_to_pseudo_regno.
|
||||
(count_spilled_pseudo): Check spilled pseudos. Update
|
||||
hard_regno_to_pseudo_regno.
|
||||
(find_reg): Use better_spill_reload_regno_p. Check
|
||||
hard_regno_to_pseudo_regno.
|
||||
(alter_reg): Set up spilled_pseudos. Add a new parameter. Add
|
||||
code for IRA.
|
||||
(eliminate_regs_1): Use additional parameter for alter_reg.
|
||||
(finish_spills): Set up pseudo_previous_regs only for spilled
|
||||
pseudos. Call reassign_pseudos once for all spilled pseudos, pass
|
||||
more arguments. Don't clear live_throughout and dead_or_set for
|
||||
spilled pseudos. Use additional parameter for alter_reg. Call
|
||||
mark_allocation_change. Set up changed_allocation_pseudos.
|
||||
Remove sanity check.
|
||||
(emit_input_reload_insns, delete_output_reload): Use additional
|
||||
parameter for alter_reg. Call mark_allocation_change.
|
||||
(substitute, gen_reload_chain_without_interm_reg_p): New
|
||||
functions.
|
||||
(reloads_conflict): Use gen_reload_chain_without_interm_reg_p.
|
||||
|
||||
* testsuite/gcc.dg/20080410-1.c: New file.
|
||||
|
||||
* config/s390/s390.h (IRA_COVER_CLASSES,
|
||||
IRA_HARD_REGNO_ADD_COST_MULTIPLIER): Define.
|
||||
|
||||
* config/sparc/sparc.h (IRA_COVER_CLASSES): New macro.
|
||||
|
||||
* config/i386/i386.h (IRA_COVER_CLASSES): Ditto.
|
||||
|
||||
* config/ia64/ia64.h (IRA_COVER_CLASSES): Ditto.
|
||||
|
||||
* config/rs6000/rs6000.h (IRA_COVER_CLASSES): Ditto.
|
||||
|
||||
* config/arm/arm.h (IRA_COVER_CLASSES): Ditto.
|
||||
|
||||
* config/alpha/alpha.h (IRA_COVER_CLASSES): Ditto.
|
||||
|
||||
2008-08-24 Jeff Law <law@redhat.com>
|
||||
* ira.c (setup_reg_class_intersect_union): Prefer smallest class
|
||||
when ignoring unavailable registers.
|
||||
|
||||
2008-08-24 Jeff Law <law@redhat.com>
|
||||
* ira-color.c (coalesced_pseudo_reg_slot_compare): Check
|
||||
FRAME_GROWS_DOWNWARD and STACK_GROWS_DOWNWARD.
|
||||
* ira.c (setup_eliminable_regset): Check stack_realign_needed.
|
||||
* config/mn10300/mn10300.h (IRA_COVER_CLASSES): New macro.
|
||||
|
||||
2008-06-03 Steve Chamberlain <steve.chamberlain@gmail.com>
|
||||
* ira-build.c (allocno_range_compare_func): Stabilize sort.
|
||||
|
||||
2008-05-29 Andy Hutchinson <hutchinsonandy@aim.com>
|
||||
* config/avr/avr.h (IRA_COVER_CLASSES): New macro.
|
||||
* reload1.c (find_reg): Process registers in register allocation order.
|
||||
|
||||
2008-05-10 Richard Sandiford <rsandifo@nildram.co.uk>
|
||||
* toplev.c (backend_init_target): Move ira_init call from
|
||||
here...
|
||||
(lang_dependent_init_target): ...to here.
|
||||
|
||||
2008-05-10 Richard Sandiford <rsandifo@nildram.co.uk>
|
||||
* ira.c (setup_class_subset_and_memory_move_costs): Don't
|
||||
calculate memory move costs for NO_REGS.
|
||||
|
||||
2008-05-05 Kaz Kojima <kkojima@gcc.gnu.org>
|
||||
* ira-color.c (ira_fast_allocation): Use no_stack_reg_p only if
|
||||
STACK_REGS is defined.
|
||||
|
||||
2008-04-08 Andrew Pinski <andrew_pinski@playstation.sony.com>
|
||||
* config/spu/spu.h (IRA_COVER_CLASSES): New macro.
|
||||
|
||||
2008-04-04 Bernd Schmidt <bernd.schmidt@analog.com>
|
||||
* config/bfin/bfin.h (IRA_COVER_CLASSES): New macro.
|
||||
|
||||
2008-04-04 Kaz Kojima <kkojima@gcc.gnu.org>
|
||||
* config/sh/sh.h (IRA_COVER_CLASSES): Define.
|
||||
* config/sh/sh.md (movsicc_true+3): Check if emit returns a
|
||||
barrier.
|
||||
|
||||
2008-08-26 Victor Kaplansky <victork@il.ibm.com>
|
||||
Dorit Nuzman <dorit@il.ibm.com>
|
||||
|
||||
|
|
|
@ -849,6 +849,7 @@ TREE_DATA_REF_H = tree-data-ref.h $(LAMBDA_H) omega.h graphds.h tree-chrec.h
|
|||
VARRAY_H = varray.h $(MACHMODE_H) $(SYSTEM_H) coretypes.h $(TM_H)
|
||||
TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h
|
||||
REAL_H = real.h $(MACHMODE_H)
|
||||
IRA_INT_H = ira.h ira-int.h $(CFGLOOP_H) alloc-pool.h
|
||||
DBGCNT_H = dbgcnt.h dbgcnt.def
|
||||
EBIMAP_H = ebitmap.h sbitmap.h
|
||||
IPA_PROP_H = ipa-prop.h $(TREE_H) vec.h $(CGRAPH_H)
|
||||
|
@ -1097,6 +1098,13 @@ OBJS-common = \
|
|||
init-regs.o \
|
||||
integrate.o \
|
||||
intl.o \
|
||||
ira.o \
|
||||
ira-build.o \
|
||||
ira-costs.o \
|
||||
ira-conflicts.o \
|
||||
ira-color.o \
|
||||
ira-emit.o \
|
||||
ira-lives.o \
|
||||
jump.o \
|
||||
lambda-code.o \
|
||||
lambda-mat.o \
|
||||
|
@ -2408,7 +2416,7 @@ toplev.o : toplev.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
|
|||
$(INSN_ATTR_H) output.h $(DIAGNOSTIC_H) debug.h insn-config.h intl.h \
|
||||
$(RECOG_H) Makefile $(TOPLEV_H) dwarf2out.h sdbout.h dbxout.h $(EXPR_H) \
|
||||
hard-reg-set.h $(BASIC_BLOCK_H) graph.h except.h $(REGS_H) $(TIMEVAR_H) \
|
||||
value-prof.h $(PARAMS_H) $(TM_P_H) reload.h dwarf2asm.h $(TARGET_H) \
|
||||
value-prof.h $(PARAMS_H) $(TM_P_H) reload.h ira.h dwarf2asm.h $(TARGET_H) \
|
||||
langhooks.h insn-flags.h $(CFGLAYOUT_H) $(CFGLOOP_H) hosthooks.h \
|
||||
$(CGRAPH_H) $(COVERAGE_H) alloc-pool.h $(GGC_H) $(INTEGRATE_H) \
|
||||
opts.h params.def tree-mudflap.h $(REAL_H) tree-pass.h $(GIMPLE_H)
|
||||
|
@ -2771,7 +2779,7 @@ cfgloop.o : cfgloop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) coretypes.h $(TM_H) \
|
|||
$(GGC_H)
|
||||
cfgloopanal.o : cfgloopanal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
|
||||
$(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(EXPR_H) coretypes.h $(TM_H) \
|
||||
$(OBSTACK_H) output.h graphds.h
|
||||
$(OBSTACK_H) output.h graphds.h $(PARAMS_H)
|
||||
graphds.o : graphds.c graphds.h $(CONFIG_H) $(SYSTEM_H) $(BITMAP_H) $(OBSTACK_H) \
|
||||
coretypes.h vec.h vecprim.h
|
||||
loop-iv.o : loop-iv.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(BASIC_BLOCK_H) \
|
||||
|
@ -2835,7 +2843,7 @@ reload1.o : reload1.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
|||
$(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
|
||||
$(BASIC_BLOCK_H) $(RECOG_H) output.h $(FUNCTION_H) $(TOPLEV_H) $(TM_P_H) \
|
||||
addresses.h except.h $(TREE_H) $(REAL_H) $(FLAGS_H) $(MACHMODE_H) \
|
||||
$(OBSTACK_H) $(DF_H) $(TARGET_H) dse.h
|
||||
$(OBSTACK_H) $(DF_H) $(TARGET_H) dse.h ira.h
|
||||
rtlhooks.o : rtlhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
rtlhooks-def.h $(EXPR_H) $(RECOG_H)
|
||||
postreload.o : postreload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
|
@ -2851,7 +2859,7 @@ postreload-gcse.o : postreload-gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.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) $(DF_H) \
|
||||
gt-caller-save.h $(GGC_H)
|
||||
output.h ira.h gt-caller-save.h $(GGC_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) \
|
||||
|
@ -2872,6 +2880,37 @@ stack-ptr-mod.o : stack-ptr-mod.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
|||
init-regs.o : init-regs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||
$(TM_H) $(TREE_H) $(RTL_H) $(REGS_H) $(EXPR_H) tree-pass.h \
|
||||
$(BASIC_BLOCK_H) $(FLAGS_H) $(DF_H)
|
||||
ira-build.o: ira-build.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
|
||||
insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) \
|
||||
$(PARAMS_H) $(DF_H) sparseset.h $(IRA_INT_H)
|
||||
ira-costs.o: ira-costs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) insn-config.h $(RECOG_H) \
|
||||
$(REGS_H) hard-reg-set.h $(FLAGS_H) errors.h \
|
||||
$(EXPR_H) $(BASIC_BLOCK_H) $(TM_P_H) \
|
||||
$(IRA_INT_H)
|
||||
ira-conflicts.o: ira-conflicts.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
|
||||
insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
|
||||
$(DF_H) sparseset.h $(IRA_INT_H)
|
||||
ira-color.o: ira-color.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
|
||||
$(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
|
||||
$(DF_H) $(SPLAY_TREE_H) $(IRA_INT_H)
|
||||
ira-emit.o: ira-emit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
|
||||
$(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
|
||||
$(IRA_INT_H)
|
||||
ira-lives.o: ira-lives.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
|
||||
$(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
|
||||
insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
|
||||
$(DF_H) sparseset.h $(IRA_INT_H)
|
||||
ira.o: ira.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||
$(TARGET_H) $(TM_H) $(RTL_H) $(RECOG_H) \
|
||||
$(REGS_H) hard-reg-set.h $(FLAGS_H) $(OBSTACK_H) \
|
||||
$(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) \
|
||||
$(DF_H) $(IRA_INT_H) $(PARAMS_H) $(TIMEVAR_H) $(INTEGRATE_H) \
|
||||
tree-pass.h output.h
|
||||
regmove.o : regmove.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
|
||||
insn-config.h $(TIMEVAR_H) tree-pass.h $(DF_H)\
|
||||
$(RECOG_H) output.h $(REGS_H) hard-reg-set.h $(FLAGS_H) $(FUNCTION_H) \
|
||||
|
|
46
gcc/alias.c
46
gcc/alias.c
|
@ -1975,6 +1975,34 @@ adjust_offset_for_component_ref (tree x, rtx offset)
|
|||
return GEN_INT (ioffset);
|
||||
}
|
||||
|
||||
/* The function returns nonzero if X is an address containg VALUE. */
|
||||
static int
|
||||
value_addr_p (rtx x)
|
||||
{
|
||||
if (GET_CODE (x) == VALUE)
|
||||
return 1;
|
||||
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == VALUE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The function returns nonzero if X is a stack address. */
|
||||
static int
|
||||
stack_addr_p (rtx x)
|
||||
{
|
||||
if (x == hard_frame_pointer_rtx || x == frame_pointer_rtx
|
||||
|| x == arg_pointer_rtx || x == stack_pointer_rtx)
|
||||
return 1;
|
||||
if (GET_CODE (x) == PLUS
|
||||
&& (XEXP (x, 0) == hard_frame_pointer_rtx
|
||||
|| XEXP (x, 0) == frame_pointer_rtx
|
||||
|| XEXP (x, 0) == arg_pointer_rtx
|
||||
|| XEXP (x, 0) == stack_pointer_rtx)
|
||||
&& CONSTANT_P (XEXP (x, 1)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return nonzero if we can determine the exprs corresponding to memrefs
|
||||
X and Y and they do not overlap. */
|
||||
|
||||
|
@ -1984,9 +2012,27 @@ nonoverlapping_memrefs_p (const_rtx x, const_rtx y)
|
|||
tree exprx = MEM_EXPR (x), expry = MEM_EXPR (y);
|
||||
rtx rtlx, rtly;
|
||||
rtx basex, basey;
|
||||
rtx x_addr, y_addr;
|
||||
rtx moffsetx, moffsety;
|
||||
HOST_WIDE_INT offsetx = 0, offsety = 0, sizex, sizey, tem;
|
||||
|
||||
if (flag_ira && optimize && reload_completed)
|
||||
{
|
||||
/* We need this code for IRA because of stack slot sharing. RTL
|
||||
in decl can be different than RTL used in insns. It is a
|
||||
safe code although it can be conservative sometime. */
|
||||
x_addr = canon_rtx (get_addr (XEXP (x, 0)));
|
||||
y_addr = canon_rtx (get_addr (XEXP (y, 0)));
|
||||
|
||||
if (value_addr_p (x_addr) || value_addr_p (y_addr))
|
||||
return 0;
|
||||
|
||||
if (stack_addr_p (x_addr) && stack_addr_p (y_addr)
|
||||
&& memrefs_conflict_p (SIZE_FOR_MODE (y), y_addr,
|
||||
SIZE_FOR_MODE (x), x_addr, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unless both have exprs, we can't tell anything. */
|
||||
if (exprx == 0 || expry == 0)
|
||||
return 0;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* Save and restore call-clobbered registers which are live across a call.
|
||||
Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998, 1999, 2000,
|
||||
2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
|
@ -35,9 +35,14 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "toplev.h"
|
||||
#include "tm_p.h"
|
||||
#include "addresses.h"
|
||||
#include "output.h"
|
||||
#include "df.h"
|
||||
#include "ggc.h"
|
||||
|
||||
/* Call used hard registers which can not be saved because there is no
|
||||
insn for this. */
|
||||
HARD_REG_SET no_caller_save_reg_set;
|
||||
|
||||
#ifndef MAX_MOVE_MAX
|
||||
#define MAX_MOVE_MAX MOVE_MAX
|
||||
#endif
|
||||
|
@ -62,6 +67,12 @@ static enum machine_mode
|
|||
static rtx
|
||||
regno_save_mem[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
|
||||
|
||||
/* The number of elements in the subsequent array. */
|
||||
static int save_slots_num;
|
||||
|
||||
/* Allocated slots so far. */
|
||||
static rtx save_slots[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* We will only make a register eligible for caller-save if it can be
|
||||
saved in its widest mode with a simple SET insn as long as the memory
|
||||
address is valid. We record the INSN_CODE is those insns here since
|
||||
|
@ -86,7 +97,17 @@ static int n_regs_saved;
|
|||
static HARD_REG_SET referenced_regs;
|
||||
|
||||
|
||||
static int reg_save_code (int, enum machine_mode);
|
||||
static int reg_restore_code (int, enum machine_mode);
|
||||
|
||||
struct saved_hard_reg;
|
||||
static void initiate_saved_hard_regs (void);
|
||||
static struct saved_hard_reg *new_saved_hard_reg (int, int);
|
||||
static void finish_saved_hard_regs (void);
|
||||
static int saved_hard_reg_compare_func (const void *, const void *);
|
||||
|
||||
static void mark_set_regs (rtx, const_rtx, void *);
|
||||
static void add_stored_regs (rtx, const_rtx, void *);
|
||||
static void mark_referenced_regs (rtx);
|
||||
static int insert_save (struct insn_chain *, int, int, HARD_REG_SET *,
|
||||
enum machine_mode *);
|
||||
|
@ -95,7 +116,9 @@ static int insert_restore (struct insn_chain *, int, int, int,
|
|||
static struct insn_chain *insert_one_insn (struct insn_chain *, int, int,
|
||||
rtx);
|
||||
static void add_stored_regs (rtx, const_rtx, void *);
|
||||
|
||||
|
||||
|
||||
static GTY(()) rtx savepat;
|
||||
static GTY(()) rtx restpat;
|
||||
static GTY(()) rtx test_reg;
|
||||
|
@ -180,6 +203,7 @@ init_caller_save (void)
|
|||
rtx address;
|
||||
int i, j;
|
||||
|
||||
CLEAR_HARD_REG_SET (no_caller_save_reg_set);
|
||||
/* First find all the registers that we need to deal with and all
|
||||
the modes that they can have. If we can't find a mode to use,
|
||||
we can't have the register live over calls. */
|
||||
|
@ -217,7 +241,7 @@ init_caller_save (void)
|
|||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (TEST_HARD_REG_BIT
|
||||
(reg_class_contents
|
||||
[(int) base_reg_class (regno_save_mode [i][1], PLUS, CONST_INT)], i))
|
||||
[(int) base_reg_class (regno_save_mode[i][1], PLUS, CONST_INT)], i))
|
||||
break;
|
||||
|
||||
gcc_assert (i < FIRST_PSEUDO_REGISTER);
|
||||
|
@ -264,10 +288,14 @@ init_caller_save (void)
|
|||
{
|
||||
call_fixed_regs[i] = 1;
|
||||
SET_HARD_REG_BIT (call_fixed_reg_set, i);
|
||||
if (call_used_regs[i])
|
||||
SET_HARD_REG_BIT (no_caller_save_reg_set, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Initialize save areas by showing that we haven't allocated any yet. */
|
||||
|
||||
void
|
||||
|
@ -278,6 +306,100 @@ init_save_areas (void)
|
|||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
for (j = 1; j <= MOVE_MAX_WORDS; j++)
|
||||
regno_save_mem[i][j] = 0;
|
||||
save_slots_num = 0;
|
||||
|
||||
}
|
||||
|
||||
/* The structure represents a hard register which should be saved
|
||||
through the call. It is used when the integrated register
|
||||
allocator (IRA) is used and sharing save slots is on. */
|
||||
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 structure 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 registers should be
|
||||
saved, in order words, the number of used elements in the following
|
||||
array. */
|
||||
static int saved_regs_num;
|
||||
|
||||
/* Pointers to all the structures. Index is the order number of the
|
||||
corresponding 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
|
||||
= (struct saved_hard_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 register structures
|
||||
according their frequency. */
|
||||
static int
|
||||
saved_hard_reg_compare_func (const void *v1p, const void *v2p)
|
||||
{
|
||||
const struct saved_hard_reg *p1 = *(struct saved_hard_reg * const *) v1p;
|
||||
const struct saved_hard_reg *p2 = *(struct saved_hard_reg * const *) 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.
|
||||
|
@ -286,6 +408,10 @@ init_save_areas (void)
|
|||
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. We build conflicts for them
|
||||
to do coloring.
|
||||
|
||||
Future work:
|
||||
|
||||
In the fallback case we should iterate backwards across all possible
|
||||
|
@ -317,65 +443,297 @@ setup_save_areas (void)
|
|||
unsigned int regno = reg_renumber[i];
|
||||
unsigned int endregno
|
||||
= end_hard_regno (GET_MODE (regno_reg_rtx[i]), regno);
|
||||
|
||||
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. */
|
||||
if (flag_ira && optimize && flag_ira_share_save_slots)
|
||||
{
|
||||
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;
|
||||
int best_slot_num;
|
||||
int prev_save_slots_num;
|
||||
rtx prev_save_slots[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
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);
|
||||
COPY_HARD_REG_SET (used_regs, call_used_reg_set);
|
||||
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
for (j = MOVE_MAX_WORDS; j > 0; j--)
|
||||
{
|
||||
int do_save = 1;
|
||||
/* 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. */
|
||||
if (SIBLING_CALL_P (insn) && crtl->return_rtx)
|
||||
mark_set_regs (crtl->return_rtx, NULL_RTX, &this_insn_sets);
|
||||
|
||||
/* 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])
|
||||
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);
|
||||
SET_HARD_REG_BIT (hard_regs_to_save, r);
|
||||
}
|
||||
}
|
||||
if (! do_save)
|
||||
continue;
|
||||
}
|
||||
/* Find saved hard register conflicts. */
|
||||
saved_reg_conflicts = (char *) 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);
|
||||
COPY_HARD_REG_SET (used_regs, call_used_reg_set);
|
||||
|
||||
for (k = 0; k < j; k++)
|
||||
if (! TEST_HARD_REG_BIT (hard_regs_used, i + k))
|
||||
/* 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) && crtl->return_rtx)
|
||||
mark_set_regs (crtl->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. Since hard
|
||||
register is always saved in the widest mode available,
|
||||
the mode may be wider than necessary, it is OK to reduce
|
||||
the alignment of spill space. We will verify that it is
|
||||
equal to or greater than required when we restore and save
|
||||
the hard register in insert_restore and insert_save. */
|
||||
regno_save_mem[i][j]
|
||||
= assign_stack_local_1 (regno_save_mode[i][j],
|
||||
GET_MODE_SIZE (regno_save_mode[i][j]),
|
||||
0, true);
|
||||
|
||||
/* 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);
|
||||
/* Initiate slots available from the previous reload
|
||||
iteration. */
|
||||
prev_save_slots_num = save_slots_num;
|
||||
memcpy (prev_save_slots, save_slots, save_slots_num * sizeof (rtx));
|
||||
save_slots_num = 0;
|
||||
/* Allocate stack slots for the saved hard registers. */
|
||||
for (i = 0; i < saved_regs_num; i++)
|
||||
{
|
||||
saved_reg = all_saved_regs[i];
|
||||
regno = saved_reg->hard_regno;
|
||||
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[regno][1])
|
||||
<= GET_MODE_SIZE (regno_save_mode
|
||||
[saved_reg2->hard_regno][1])))
|
||||
{
|
||||
saved_reg->slot
|
||||
= adjust_address_nv
|
||||
(slot, regno_save_mode[saved_reg->hard_regno][1], 0);
|
||||
regno_save_mem[regno][1] = saved_reg->slot;
|
||||
saved_reg->next = saved_reg2->next;
|
||||
saved_reg2->next = i;
|
||||
if (dump_file != NULL)
|
||||
fprintf (dump_file, "%d uses slot of %d\n",
|
||||
regno, saved_reg2->hard_regno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == i)
|
||||
{
|
||||
saved_reg->first_p = TRUE;
|
||||
for (best_slot_num = -1, j = 0; j < prev_save_slots_num; j++)
|
||||
{
|
||||
slot = prev_save_slots[j];
|
||||
if (slot == NULL_RTX)
|
||||
continue;
|
||||
if (GET_MODE_SIZE (regno_save_mode[regno][1])
|
||||
<= GET_MODE_SIZE (GET_MODE (slot))
|
||||
&& best_slot_num < 0)
|
||||
best_slot_num = j;
|
||||
if (GET_MODE (slot) == regno_save_mode[regno][1])
|
||||
break;
|
||||
}
|
||||
if (best_slot_num >= 0)
|
||||
{
|
||||
saved_reg->slot = prev_save_slots[best_slot_num];
|
||||
saved_reg->slot
|
||||
= adjust_address_nv
|
||||
(saved_reg->slot,
|
||||
regno_save_mode[saved_reg->hard_regno][1], 0);
|
||||
if (dump_file != NULL)
|
||||
fprintf (dump_file,
|
||||
"%d uses a slot from prev iteration\n", regno);
|
||||
prev_save_slots[best_slot_num] = NULL_RTX;
|
||||
if (best_slot_num + 1 == prev_save_slots_num)
|
||||
prev_save_slots_num--;
|
||||
}
|
||||
else
|
||||
{
|
||||
saved_reg->slot
|
||||
= assign_stack_local_1
|
||||
(regno_save_mode[regno][1],
|
||||
GET_MODE_SIZE (regno_save_mode[regno][1]), 0, true);
|
||||
if (dump_file != NULL)
|
||||
fprintf (dump_file, "%d uses a new slot\n", regno);
|
||||
}
|
||||
regno_save_mem[regno][1] = saved_reg->slot;
|
||||
save_slots[save_slots_num++] = saved_reg->slot;
|
||||
}
|
||||
}
|
||||
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. Since
|
||||
hard register is always saved in the widest mode
|
||||
available, the mode may be wider than necessary, it is
|
||||
OK to reduce the alignment of spill space. We will
|
||||
verify that it is equal to or greater than required
|
||||
when we restore and save the hard register in
|
||||
insert_restore and insert_save. */
|
||||
regno_save_mem[i][j]
|
||||
= assign_stack_local_1 (regno_save_mode[i][j],
|
||||
GET_MODE_SIZE (regno_save_mode[i][j]),
|
||||
0, true);
|
||||
|
||||
/* 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. */
|
||||
|
@ -384,7 +742,9 @@ setup_save_areas (void)
|
|||
if (regno_save_mem[i][j] != 0)
|
||||
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
|
||||
|
@ -461,7 +821,8 @@ save_call_clobbered_regs (void)
|
|||
int nregs;
|
||||
enum machine_mode mode;
|
||||
|
||||
gcc_assert (r >= 0);
|
||||
if (r < 0)
|
||||
continue;
|
||||
nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
|
||||
mode = HARD_REGNO_CALLER_SAVE_MODE
|
||||
(r, nregs, PSEUDO_REGNO_MODE (regno));
|
||||
|
@ -497,7 +858,7 @@ save_call_clobbered_regs (void)
|
|||
}
|
||||
}
|
||||
|
||||
if (chain->next == 0 || chain->next->block > chain->block)
|
||||
if (chain->next == 0 || chain->next->block != chain->block)
|
||||
{
|
||||
int regno;
|
||||
/* At the end of the basic block, we must restore any registers that
|
||||
|
@ -713,7 +1074,8 @@ insert_restore (struct insn_chain *chain, int before_p, int regno,
|
|||
|
||||
/* Verify that the alignment of spill space is equal to or greater
|
||||
than required. */
|
||||
gcc_assert (GET_MODE_ALIGNMENT (GET_MODE (mem)) <= MEM_ALIGN (mem));
|
||||
gcc_assert (MIN (MAX_SUPPORTED_STACK_ALIGNMENT,
|
||||
GET_MODE_ALIGNMENT (GET_MODE (mem))) <= MEM_ALIGN (mem));
|
||||
|
||||
pat = gen_rtx_SET (VOIDmode,
|
||||
gen_rtx_REG (GET_MODE (mem),
|
||||
|
@ -790,7 +1152,8 @@ insert_save (struct insn_chain *chain, int before_p, int regno,
|
|||
|
||||
/* Verify that the alignment of spill space is equal to or greater
|
||||
than required. */
|
||||
gcc_assert (GET_MODE_ALIGNMENT (GET_MODE (mem)) <= MEM_ALIGN (mem));
|
||||
gcc_assert (MIN (MAX_SUPPORTED_STACK_ALIGNMENT,
|
||||
GET_MODE_ALIGNMENT (GET_MODE (mem))) <= MEM_ALIGN (mem));
|
||||
|
||||
pat = gen_rtx_SET (VOIDmode, mem,
|
||||
gen_rtx_REG (GET_MODE (mem),
|
||||
|
|
|
@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "expr.h"
|
||||
#include "output.h"
|
||||
#include "graphds.h"
|
||||
#include "params.h"
|
||||
|
||||
/* Checks whether BB is executed exactly once in each LOOP iteration. */
|
||||
|
||||
|
@ -372,6 +373,7 @@ init_set_costs (void)
|
|||
unsigned
|
||||
estimate_reg_pressure_cost (unsigned n_new, unsigned n_old)
|
||||
{
|
||||
unsigned cost;
|
||||
unsigned regs_needed = n_new + n_old;
|
||||
|
||||
/* If we have enough registers, we should use them and not restrict
|
||||
|
@ -379,12 +381,25 @@ estimate_reg_pressure_cost (unsigned n_new, unsigned n_old)
|
|||
if (regs_needed + target_res_regs <= target_avail_regs)
|
||||
return 0;
|
||||
|
||||
/* If we are close to running out of registers, try to preserve them. */
|
||||
if (regs_needed <= target_avail_regs)
|
||||
return target_reg_cost * n_new;
|
||||
|
||||
/* If we run out of registers, it is very expensive to add another one. */
|
||||
return target_spill_cost * n_new;
|
||||
/* If we are close to running out of registers, try to preserve
|
||||
them. */
|
||||
cost = target_reg_cost * n_new;
|
||||
else
|
||||
/* If we run out of registers, it is very expensive to add another
|
||||
one. */
|
||||
cost = target_spill_cost * n_new;
|
||||
|
||||
if (optimize && flag_ira && (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
|
||||
|| flag_ira_algorithm == IRA_ALGORITHM_MIXED)
|
||||
&& number_of_loops () <= (unsigned) IRA_MAX_LOOPS_NUM)
|
||||
/* IRA regional allocation deals with high register pressure
|
||||
better. So decrease the cost (to do more accurate the cost
|
||||
calculation for IRA, we need to know how many registers lives
|
||||
through the loop transparently). */
|
||||
cost /= 2;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/* Sets EDGE_LOOP_EXIT flag for all loop exits. */
|
||||
|
|
|
@ -653,6 +653,30 @@ Common Report Var(flag_ipa_struct_reorg)
|
|||
Perform structure layout optimizations based
|
||||
on profiling information.
|
||||
|
||||
fira
|
||||
Common Report Var(flag_ira) Init(0)
|
||||
Use integrated register allocator.
|
||||
|
||||
fira-algorithm=
|
||||
Common Joined RejectNegative
|
||||
-fira-algorithm=[regional|CB|mixed] Set the used IRA algorithm
|
||||
|
||||
fira-coalesce
|
||||
Common Report Var(flag_ira_coalesce) Init(0)
|
||||
Do optimistic coalescing.
|
||||
|
||||
fira-share-save-slots
|
||||
Common Report Var(flag_ira_share_save_slots) Init(1)
|
||||
Share slots for saving different hard registers.
|
||||
|
||||
fira-share-spill-slots
|
||||
Common Report Var(flag_ira_share_spill_slots) Init(1)
|
||||
Share stack slots for spilled pseudo-registers.
|
||||
|
||||
fira-verbose=
|
||||
Common RejectNegative Joined UInteger
|
||||
-fira-verbose=<number> Control IRA's level of diagnostic messages.
|
||||
|
||||
fivopts
|
||||
Common Report Var(flag_ivopts) Init(1) Optimization
|
||||
Optimize induction variables on trees
|
||||
|
|
|
@ -553,6 +553,19 @@ enum reg_class {
|
|||
{0x00000000, 0x7fffffff}, /* FLOAT_REGS */ \
|
||||
{0xffffffff, 0xffffffff} }
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FLOAT_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* The same information, inverted:
|
||||
Return the class number of the smallest class containing
|
||||
reg number REGNO. This could be a conditional expression
|
||||
|
|
|
@ -1185,6 +1185,20 @@ enum reg_class
|
|||
or could index an array. */
|
||||
#define REGNO_REG_CLASS(REGNO) arm_regno_class (REGNO)
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FPA_REGS, CIRRUS_REGS, VFP_REGS, IWMMXT_GR_REGS, IWMMXT_REGS,\
|
||||
LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* FPA registers can't do subreg as all values are reformatted to internal
|
||||
precision. VFP registers may only be accessed in the mode they
|
||||
were set. */
|
||||
|
|
|
@ -291,6 +291,19 @@ enum reg_class {
|
|||
|
||||
#define REGNO_REG_CLASS(R) avr_regno_reg_class(R)
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
#define BASE_REG_CLASS (reload_completed ? BASE_POINTER_REGS : POINTER_REGS)
|
||||
|
||||
#define INDEX_REG_CLASS NO_REGS
|
||||
|
|
|
@ -711,6 +711,19 @@ enum reg_class
|
|||
: (REGNO) >= REG_RETS ? PROLOGUE_REGS \
|
||||
: NO_REGS)
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
MOST_REGS, AREGS, CCREGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* When defined, the compiler allows registers explicitly used in the
|
||||
rtl to be used as spill registers but prevents the compiler from
|
||||
extending the lifetime of these registers. */
|
||||
|
|
|
@ -1274,6 +1274,19 @@ enum reg_class
|
|||
{ 0xffffffff,0x1fffff } \
|
||||
}
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FLOAT_REGS, MMX_REGS, SSE_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* The same information, inverted:
|
||||
Return the class number of the smallest class containing
|
||||
reg number REGNO. This could be a conditional expression
|
||||
|
|
|
@ -800,6 +800,19 @@ enum reg_class
|
|||
0xFFFFFFFF, 0xFFFFFFFF, 0x3FFF }, \
|
||||
}
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
PR_REGS, BR_REGS, AR_M_REGS, AR_I_REGS, GR_REGS, FR_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* A C expression whose value is a register class containing hard register
|
||||
REGNO. In general there is more than one such class; choose a class which
|
||||
is "minimal", meaning that no smaller class also contains the register. */
|
||||
|
|
|
@ -295,6 +295,19 @@ enum reg_class {
|
|||
{ 0xffffffff, 0x3ffff } /* ALL_REGS */ \
|
||||
}
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FP_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* The same information, inverted:
|
||||
Return the class number of the smallest class containing
|
||||
reg number REGNO. This could be a conditional expression
|
||||
|
|
|
@ -1128,6 +1128,22 @@ enum reg_class
|
|||
{ 0xffffffff, 0xffffffff, 0xffffffff, 0x0003ffff } /* ALL_REGS */ \
|
||||
}
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, SPECIAL_REGS, FLOAT_REGS, ALTIVEC_REGS, \
|
||||
/*VRSAVE_REGS,*/ VSCR_REGS, SPE_ACC_REGS, SPEFSCR_REGS, \
|
||||
/* MQ_REGS, LINK_REGS, CTR_REGS, */ \
|
||||
CR_REGS, XER_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* The same information, inverted:
|
||||
Return the class number of the smallest class containing
|
||||
reg number REGNO. This could be a conditional expression
|
||||
|
|
|
@ -478,6 +478,30 @@ enum reg_class
|
|||
{ 0xffffffff, 0x0000003f }, /* ALL_REGS */ \
|
||||
}
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FP_REGS, CC_REGS, ACCESS_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* In some case register allocation order is not enough for IRA to
|
||||
generate a good code. The following macro (if defined) increases
|
||||
cost of REGNO for a pseudo approximately by pseudo usage frequency
|
||||
multiplied by the macro value.
|
||||
|
||||
We avoid usage of BASE_REGNUM by nonzero macro value because the
|
||||
reload can decide not to use the hard register because some
|
||||
constant was forced to be in memory. */
|
||||
#define IRA_HARD_REGNO_ADD_COST_MULTIPLIER(regno) \
|
||||
(regno == BASE_REGNUM ? 0.0 : 0.5)
|
||||
|
||||
/* Register -> class mapping. */
|
||||
extern const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER];
|
||||
#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO])
|
||||
|
|
|
@ -1499,6 +1499,20 @@ enum reg_class
|
|||
extern enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
|
||||
#define REGNO_REG_CLASS(REGNO) regno_reg_class[(REGNO)]
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, FP_REGS, PR_REGS, T_REGS, MAC_REGS, TARGET_REGS, \
|
||||
LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* When defined, the compiler allows registers explicitly used in the
|
||||
rtl to be used as spill registers but prevents the compiler from
|
||||
extending the lifetime of these registers. */
|
||||
|
|
|
@ -1143,7 +1143,7 @@
|
|||
(set (match_dup 4) (match_dup 5))]
|
||||
"
|
||||
{
|
||||
rtx set1, set2;
|
||||
rtx set1, set2, insn2;
|
||||
rtx replacements[4];
|
||||
|
||||
/* We want to replace occurrences of operands[0] with operands[1] and
|
||||
|
@ -1173,7 +1173,10 @@
|
|||
extract_insn (emit_insn (set1));
|
||||
if (! constrain_operands (1))
|
||||
goto failure;
|
||||
extract_insn (emit (set2));
|
||||
insn2 = emit (set2);
|
||||
if (GET_CODE (insn2) == BARRIER)
|
||||
goto failure;
|
||||
extract_insn (insn2);
|
||||
if (! constrain_operands (1))
|
||||
{
|
||||
rtx tmp;
|
||||
|
|
|
@ -1078,6 +1078,19 @@ enum reg_class { NO_REGS, FPCC_REGS, I64_REGS, GENERAL_REGS, FP_REGS,
|
|||
{-1, -1, -1, 0x20}, /* GENERAL_OR_EXTRA_FP_REGS */ \
|
||||
{-1, -1, -1, 0x3f}} /* ALL_REGS */
|
||||
|
||||
/* The following macro defines cover classes for Integrated Register
|
||||
Allocator. Cover classes is a set of non-intersected register
|
||||
classes covering all hard registers used for register allocation
|
||||
purpose. Any move between two registers of a cover class should be
|
||||
cheaper than load or store of the registers. The macro value is
|
||||
array of register classes with LIM_REG_CLASSES used as the end
|
||||
marker. */
|
||||
|
||||
#define IRA_COVER_CLASSES \
|
||||
{ \
|
||||
GENERAL_REGS, EXTRA_FP_REGS, FPCC_REGS, LIM_REG_CLASSES \
|
||||
}
|
||||
|
||||
/* Defines invalid mode changes. Borrowed from pa64-regs.h.
|
||||
|
||||
SImode loads to floating-point registers are not zero-extended.
|
||||
|
|
|
@ -196,6 +196,9 @@ enum reg_class {
|
|||
LIM_REG_CLASSES
|
||||
};
|
||||
|
||||
/* SPU is simple, it really only has one class of registers. */
|
||||
#define IRA_COVER_CLASSES { GENERAL_REGS, LIM_REG_CLASSES }
|
||||
|
||||
#define N_REG_CLASSES (int) LIM_REG_CLASSES
|
||||
|
||||
#define REG_CLASS_NAMES \
|
||||
|
|
|
@ -274,7 +274,8 @@ Objective-C and Objective-C++ Dialects}.
|
|||
@xref{Debugging Options,,Options for Debugging Your Program or GCC}.
|
||||
@gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol
|
||||
-fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
|
||||
-fdump-noaddr -fdump-unnumbered -fdump-translation-unit@r{[}-@var{n}@r{]} @gol
|
||||
-fdump-noaddr -fdump-unnumbered @gol
|
||||
-fdump-translation-unit@r{[}-@var{n}@r{]} @gol
|
||||
-fdump-class-hierarchy@r{[}-@var{n}@r{]} @gol
|
||||
-fdump-ipa-all -fdump-ipa-cgraph -fdump-ipa-inline @gol
|
||||
-fdump-statistics @gol
|
||||
|
@ -332,7 +333,10 @@ Objective-C and Objective-C++ Dialects}.
|
|||
-finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
|
||||
-finline-small-functions -fipa-cp -fipa-cp-clone -fipa-marix-reorg -fipa-pta @gol
|
||||
-fipa-pure-const -fipa-reference -fipa-struct-reorg @gol
|
||||
-fipa-type-escape -fivopts -fkeep-inline-functions -fkeep-static-consts @gol
|
||||
-fipa-type-escape -fira -fira-algorithm=@var{algorithm} @gol
|
||||
-fira-coalesce -fno-ira-share-save-slots @gol
|
||||
-fno-ira-share-spill-slots -fira-verbose=@var{n} @gol
|
||||
-fivopts -fkeep-inline-functions -fkeep-static-consts @gol
|
||||
-fmerge-all-constants -fmerge-constants -fmodulo-sched @gol
|
||||
-fmodulo-sched-allow-regmoves -fmove-loop-invariants -fmudflap @gol
|
||||
-fmudflapir -fmudflapth -fno-branch-count-reg -fno-default-inline @gol
|
||||
|
@ -5673,6 +5677,49 @@ optimization.
|
|||
|
||||
Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
|
||||
|
||||
@item -fira
|
||||
@opindex fira
|
||||
Use the integrated register allocator (@acronym{IRA}) for register
|
||||
allocation. It is a default if @acronym{IRA} has been ported for the
|
||||
target.
|
||||
|
||||
@item -fira-algorithm=@var{algorithm}
|
||||
Use specified algorithm for the integrated register allocator. The
|
||||
@var{algorithm} argument should be one of @code{regional}, @code{CB},
|
||||
or @code{mixed}. The second algorithm specifies Chaitin-Briggs
|
||||
coloring, the first one specifies regional coloring based on
|
||||
Chaitin-Briggs coloring, and the third one which is the default
|
||||
specifies a mix of Chaitin-Briggs and regional algorithms where loops
|
||||
with small register pressure are ignored. The first algorithm can
|
||||
give best result for machines with small size and irregular register
|
||||
set, the second one is faster and generates decent code and the
|
||||
smallest size code, and the mixed algorithm usually give the best
|
||||
results in most cases and for most architectures.
|
||||
|
||||
@item -fira-coalesce
|
||||
@opindex fira-coalesce
|
||||
Do optimistic register coalescing. This option might be profitable for
|
||||
architectures with big regular register files.
|
||||
|
||||
@item -fno-ira-share-save-slots
|
||||
@opindex fno-ira-share-save-slots
|
||||
Switch off sharing stack slots used for saving call used hard
|
||||
registers living through a call. Each hard register will get a
|
||||
separate stack slot and as a result function stack frame will be
|
||||
bigger.
|
||||
|
||||
@item -fno-ira-share-spill-slots
|
||||
@opindex fno-ira-share-spill-slots
|
||||
Switch off sharing stack slots allocated for pseudo-registers. Each
|
||||
pseudo-register which did not get a hard register will get a separate
|
||||
stack slot and as a result function stack frame will be bigger.
|
||||
|
||||
@item -fira-verbose=@var{n}
|
||||
@opindex fira-verbose
|
||||
Set up how verbose dump file for the integrated register allocator
|
||||
will be. Default value is 5. If the value is greater or equal to 10,
|
||||
the dump file will be stderr as if the value were @var{n} minus 10.
|
||||
|
||||
@item -fdelayed-branch
|
||||
@opindex fdelayed-branch
|
||||
If supported for the target machine, attempt to reorder instructions
|
||||
|
@ -7384,6 +7431,13 @@ processing. If this limit is hit, SCCVN processing for the whole
|
|||
function will not be done and optimizations depending on it will
|
||||
be disabled. The default maximum SCC size is 10000.
|
||||
|
||||
@item ira-max-loops-num
|
||||
IRA uses a regional register allocation by default. If a function
|
||||
contains loops more than number given by the parameter, non-regional
|
||||
register allocator will be used even when option
|
||||
@option{-fira-algorithm} is given. The default value of the parameter
|
||||
is 20.
|
||||
|
||||
@end table
|
||||
@end table
|
||||
|
||||
|
|
|
@ -841,6 +841,28 @@ Global register allocation. This pass allocates hard registers for
|
|||
the remaining pseudo registers (those whose life spans are not
|
||||
contained in one basic block). The pass is located in @file{global.c}.
|
||||
|
||||
@item
|
||||
The optional integrated register allocator (@acronym{IRA}). It can be
|
||||
used instead of the local and global allocator. It is called
|
||||
integrated because coalescing, register live range splitting, and hard
|
||||
register preferencing are done on-the-fly during coloring. It also
|
||||
has better integration with the reload pass. Pseudo-registers spilled
|
||||
by the allocator or the reload have still a chance to get
|
||||
hard-registers if the reload evicts some pseudo-registers from
|
||||
hard-registers. The allocator helps to choose better pseudos for
|
||||
spilling based on their live ranges and to coalesce stack slots
|
||||
allocated for the spilled pseudo-registers. IRA is a regional
|
||||
register allocator which is transformed into Chaitin-Briggs allocator
|
||||
if there is one region. By default, IRA chooses regions using
|
||||
register pressure but the user can force it to use one region or
|
||||
regions corresponding to all loops.
|
||||
|
||||
Source files of the allocator are @file{ira.c}, @file{ira-build.c},
|
||||
@file{ira-costs.c}, @file{ira-conflicts.c}, @file{ira-color.c},
|
||||
@file{ira-emit.c}, @file{ira-lives}, plus header files @file{ira.h}
|
||||
and @file{ira-int.h} used for the communication between the allocator
|
||||
and the rest of the compiler and between the IRA files.
|
||||
|
||||
@cindex reloading
|
||||
@item
|
||||
Reloading. This pass renumbers pseudo registers with the hardware
|
||||
|
|
|
@ -2026,6 +2026,18 @@ The macro body should not assume anything about the contents of
|
|||
On most machines, it is not necessary to define this macro.
|
||||
@end defmac
|
||||
|
||||
@defmac IRA_HARD_REGNO_ADD_COST_MULTIPLIER (@var{regno})
|
||||
In some case register allocation order is not enough for the
|
||||
Integrated Register Allocator (@acronym{IRA}) to generate a good code.
|
||||
If this macro is defined, it should return a floating point value
|
||||
based on @var{regno}. The cost of using @var{regno} for a pseudo will
|
||||
be increased by approximately the pseudo's usage frequency times the
|
||||
value returned by this macro. Not defining this macro is equivalent
|
||||
to having it always return @code{0.0}.
|
||||
|
||||
On most machines, it is not necessary to define this macro.
|
||||
@end defmac
|
||||
|
||||
@node Values in Registers
|
||||
@subsection How Values Fit in Registers
|
||||
|
||||
|
@ -2814,6 +2826,19 @@ as below:
|
|||
@end smallexample
|
||||
@end defmac
|
||||
|
||||
@defmac IRA_COVER_CLASSES
|
||||
The macro defines cover classes for the Integrated Register Allocator
|
||||
(@acronym{IRA}). Cover classes are a set of non-intersecting register
|
||||
classes covering all hard registers used for register allocation
|
||||
purposes. Any move between two registers in the same cover class
|
||||
should be cheaper than load or store of the registers. The macro
|
||||
value should be the initializer for an array of register class values,
|
||||
with @code{LIM_REG_CLASSES} used as the end marker.
|
||||
|
||||
You must define this macro in order to use the integrated register
|
||||
allocator for the target.
|
||||
@end defmac
|
||||
|
||||
@node Old Constraints
|
||||
@section Obsolete Macros for Defining Constraints
|
||||
@cindex defining constraints, obsolete method
|
||||
|
|
13
gcc/flags.h
13
gcc/flags.h
|
@ -205,6 +205,19 @@ extern int flag_debug_asm;
|
|||
extern int flag_next_runtime;
|
||||
|
||||
extern int flag_dump_rtl_in_asm;
|
||||
|
||||
/* The algorithm used for the integrated register allocator (IRA). */
|
||||
enum ira_algorithm
|
||||
{
|
||||
IRA_ALGORITHM_REGIONAL,
|
||||
IRA_ALGORITHM_CB,
|
||||
IRA_ALGORITHM_MIXED
|
||||
};
|
||||
|
||||
extern enum ira_algorithm flag_ira_algorithm;
|
||||
|
||||
extern unsigned int flag_ira_verbose;
|
||||
|
||||
|
||||
/* Other basic status info about current function. */
|
||||
|
||||
|
|
41
gcc/global.c
41
gcc/global.c
|
@ -188,7 +188,7 @@ compute_regs_asm_clobbered (char *regs_asm_clobbered)
|
|||
|
||||
/* All registers that can be eliminated. */
|
||||
|
||||
static HARD_REG_SET eliminable_regset;
|
||||
HARD_REG_SET eliminable_regset;
|
||||
|
||||
static int regno_compare (const void *, const void *);
|
||||
static int allocno_compare (const void *, const void *);
|
||||
|
@ -197,7 +197,6 @@ static void prune_preferences (void);
|
|||
static void set_preferences (void);
|
||||
static void find_reg (int, HARD_REG_SET, int, int, int);
|
||||
static void dump_conflicts (FILE *);
|
||||
static void build_insn_chain (void);
|
||||
|
||||
|
||||
/* Look through the list of eliminable registers. Set ELIM_SET to the
|
||||
|
@ -1355,7 +1354,8 @@ mark_elimination (int from, int to)
|
|||
|
||||
FOR_EACH_BB (bb)
|
||||
{
|
||||
regset r = DF_LIVE_IN (bb);
|
||||
/* We don't use LIVE info in IRA. */
|
||||
regset r = (flag_ira ? DF_LR_IN (bb) : DF_LIVE_IN (bb));
|
||||
if (REGNO_REG_SET_P (r, from))
|
||||
{
|
||||
CLEAR_REGNO_REG_SET (r, from);
|
||||
|
@ -1385,11 +1385,21 @@ print_insn_chains (FILE *file)
|
|||
print_insn_chain (file, c);
|
||||
}
|
||||
|
||||
/* Return true if pseudo REGNO should be added to set live_throughout
|
||||
or dead_or_set of the insn chains for reload consideration. */
|
||||
|
||||
static bool
|
||||
pseudo_for_reload_consideration_p (int regno)
|
||||
{
|
||||
/* Consider spilled pseudos too for IRA because they still have a
|
||||
chance to get hard-registers in the reload when IRA is used. */
|
||||
return reg_renumber[regno] >= 0 || (flag_ira && optimize);
|
||||
}
|
||||
|
||||
/* Walk the insns of the current function and build reload_insn_chain,
|
||||
and record register life information. */
|
||||
|
||||
static void
|
||||
void
|
||||
build_insn_chain (void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -1412,7 +1422,6 @@ build_insn_chain (void)
|
|||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (TEST_HARD_REG_BIT (eliminable_regset, i))
|
||||
bitmap_set_bit (elim_regset, i);
|
||||
|
||||
FOR_EACH_BB_REVERSE (bb)
|
||||
{
|
||||
bitmap_iterator bi;
|
||||
|
@ -1430,7 +1439,7 @@ build_insn_chain (void)
|
|||
|
||||
EXECUTE_IF_SET_IN_BITMAP (df_get_live_out (bb), FIRST_PSEUDO_REGISTER, i, bi)
|
||||
{
|
||||
if (reg_renumber[i] >= 0)
|
||||
if (pseudo_for_reload_consideration_p (i))
|
||||
bitmap_set_bit (live_relevant_regs, i);
|
||||
}
|
||||
|
||||
|
@ -1467,11 +1476,13 @@ build_insn_chain (void)
|
|||
if (!fixed_regs[regno])
|
||||
bitmap_set_bit (&c->dead_or_set, regno);
|
||||
}
|
||||
else if (reg_renumber[regno] >= 0)
|
||||
else if (pseudo_for_reload_consideration_p (regno))
|
||||
bitmap_set_bit (&c->dead_or_set, regno);
|
||||
}
|
||||
|
||||
if ((regno < FIRST_PSEUDO_REGISTER || reg_renumber[regno] >= 0)
|
||||
if ((regno < FIRST_PSEUDO_REGISTER
|
||||
|| reg_renumber[regno] >= 0
|
||||
|| (flag_ira && optimize))
|
||||
&& (!DF_REF_FLAGS_IS_SET (def, DF_REF_CONDITIONAL)))
|
||||
{
|
||||
rtx reg = DF_REF_REG (def);
|
||||
|
@ -1567,11 +1578,12 @@ build_insn_chain (void)
|
|||
if (!fixed_regs[regno])
|
||||
bitmap_set_bit (&c->dead_or_set, regno);
|
||||
}
|
||||
else if (reg_renumber[regno] >= 0)
|
||||
else if (pseudo_for_reload_consideration_p (regno))
|
||||
bitmap_set_bit (&c->dead_or_set, regno);
|
||||
}
|
||||
|
||||
if (regno < FIRST_PSEUDO_REGISTER || reg_renumber[regno] >= 0)
|
||||
if (regno < FIRST_PSEUDO_REGISTER
|
||||
|| pseudo_for_reload_consideration_p (regno))
|
||||
{
|
||||
if (GET_CODE (reg) == SUBREG
|
||||
&& !DF_REF_FLAGS_IS_SET (use,
|
||||
|
@ -1748,6 +1760,13 @@ dump_global_regs (FILE *file)
|
|||
fprintf (file, "\n\n");
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
gate_handle_global_alloc (void)
|
||||
{
|
||||
return ! flag_ira;
|
||||
}
|
||||
|
||||
/* Run old register allocator. Return TRUE if we must exit
|
||||
rest_of_compilation upon return. */
|
||||
static unsigned int
|
||||
|
@ -1811,7 +1830,7 @@ struct rtl_opt_pass pass_global_alloc =
|
|||
{
|
||||
RTL_PASS,
|
||||
"greg", /* name */
|
||||
NULL, /* gate */
|
||||
gate_handle_global_alloc, /* gate */
|
||||
rest_of_handle_global_alloc, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
|
|
|
@ -538,6 +538,11 @@ extern char global_regs[FIRST_PSEUDO_REGISTER];
|
|||
|
||||
extern HARD_REG_SET regs_invalidated_by_call;
|
||||
|
||||
/* Call used hard registers which can not be saved because there is no
|
||||
insn for this. */
|
||||
|
||||
extern HARD_REG_SET no_caller_save_reg_set;
|
||||
|
||||
#ifdef REG_ALLOC_ORDER
|
||||
/* Table of register numbers in the order in which to try to use them. */
|
||||
|
||||
|
@ -556,6 +561,10 @@ extern HARD_REG_SET reg_class_contents[N_REG_CLASSES];
|
|||
|
||||
extern unsigned int reg_class_size[N_REG_CLASSES];
|
||||
|
||||
/* For each reg class, table listing all the classes contained in it. */
|
||||
|
||||
extern enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
|
||||
|
||||
/* For each pair of reg classes,
|
||||
a largest reg class contained in their union. */
|
||||
|
||||
|
|
2449
gcc/ira-build.c
Normal file
2449
gcc/ira-build.c
Normal file
File diff suppressed because it is too large
Load diff
2955
gcc/ira-color.c
Normal file
2955
gcc/ira-color.c
Normal file
File diff suppressed because it is too large
Load diff
777
gcc/ira-conflicts.c
Normal file
777
gcc/ira-conflicts.c
Normal file
|
@ -0,0 +1,777 @@
|
|||
/* IRA conflict builder.
|
||||
Copyright (C) 2006, 2007, 2008
|
||||
Free Software Foundation, Inc.
|
||||
Contributed by Vladimir Makarov <vmakarov@redhat.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 "regs.h"
|
||||
#include "rtl.h"
|
||||
#include "tm_p.h"
|
||||
#include "target.h"
|
||||
#include "flags.h"
|
||||
#include "hard-reg-set.h"
|
||||
#include "basic-block.h"
|
||||
#include "insn-config.h"
|
||||
#include "recog.h"
|
||||
#include "toplev.h"
|
||||
#include "params.h"
|
||||
#include "df.h"
|
||||
#include "sparseset.h"
|
||||
#include "ira-int.h"
|
||||
|
||||
/* This file contains code responsible for allocno conflict creation,
|
||||
allocno copy creation and allocno info accumulation on upper level
|
||||
regions. */
|
||||
|
||||
/* ira_allocnos_num array of arrays of bits, recording whether two
|
||||
allocno's conflict (can't go in the same hardware register).
|
||||
|
||||
Some arrays will be used as conflict bit vector of the
|
||||
corresponding allocnos see function build_allocno_conflicts. */
|
||||
static IRA_INT_TYPE **conflicts;
|
||||
|
||||
/* Macro to test a conflict of A1 and A2 in `conflicts'. */
|
||||
#define CONFLICT_ALLOCNO_P(A1, A2) \
|
||||
(ALLOCNO_MIN (A1) <= ALLOCNO_CONFLICT_ID (A2) \
|
||||
&& ALLOCNO_CONFLICT_ID (A2) <= ALLOCNO_MAX (A1) \
|
||||
&& TEST_ALLOCNO_SET_BIT (conflicts[ALLOCNO_NUM (A1)], \
|
||||
ALLOCNO_CONFLICT_ID (A2), \
|
||||
ALLOCNO_MIN (A1), \
|
||||
ALLOCNO_MAX (A1)))
|
||||
|
||||
|
||||
|
||||
/* Build allocno conflict table by processing allocno live ranges. */
|
||||
static void
|
||||
build_conflict_bit_table (void)
|
||||
{
|
||||
int i, num, id, allocated_words_num, conflict_bit_vec_words_num;
|
||||
unsigned int j;
|
||||
enum reg_class cover_class;
|
||||
ira_allocno_t allocno, live_a;
|
||||
allocno_live_range_t r;
|
||||
ira_allocno_iterator ai;
|
||||
sparseset allocnos_live;
|
||||
int allocno_set_words;
|
||||
|
||||
allocno_set_words = (ira_allocnos_num + IRA_INT_BITS - 1) / IRA_INT_BITS;
|
||||
allocnos_live = sparseset_alloc (ira_allocnos_num);
|
||||
conflicts = (IRA_INT_TYPE **) ira_allocate (sizeof (IRA_INT_TYPE *)
|
||||
* ira_allocnos_num);
|
||||
allocated_words_num = 0;
|
||||
FOR_EACH_ALLOCNO (allocno, ai)
|
||||
{
|
||||
num = ALLOCNO_NUM (allocno);
|
||||
if (ALLOCNO_MAX (allocno) < ALLOCNO_MIN (allocno))
|
||||
{
|
||||
conflicts[num] = NULL;
|
||||
continue;
|
||||
}
|
||||
conflict_bit_vec_words_num
|
||||
= ((ALLOCNO_MAX (allocno) - ALLOCNO_MIN (allocno) + IRA_INT_BITS)
|
||||
/ IRA_INT_BITS);
|
||||
allocated_words_num += conflict_bit_vec_words_num;
|
||||
conflicts[num]
|
||||
= (IRA_INT_TYPE *) ira_allocate (sizeof (IRA_INT_TYPE)
|
||||
* conflict_bit_vec_words_num);
|
||||
memset (conflicts[num], 0,
|
||||
sizeof (IRA_INT_TYPE) * conflict_bit_vec_words_num);
|
||||
}
|
||||
if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
|
||||
fprintf
|
||||
(ira_dump_file,
|
||||
"+++Allocating %ld bytes for conflict table (uncompressed size %ld)\n",
|
||||
(long) allocated_words_num * sizeof (IRA_INT_TYPE),
|
||||
(long) allocno_set_words * ira_allocnos_num * sizeof (IRA_INT_TYPE));
|
||||
for (i = 0; i < ira_max_point; i++)
|
||||
{
|
||||
for (r = ira_start_point_ranges[i]; r != NULL; r = r->start_next)
|
||||
{
|
||||
allocno = r->allocno;
|
||||
num = ALLOCNO_NUM (allocno);
|
||||
id = ALLOCNO_CONFLICT_ID (allocno);
|
||||
cover_class = ALLOCNO_COVER_CLASS (allocno);
|
||||
sparseset_set_bit (allocnos_live, num);
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, j)
|
||||
{
|
||||
live_a = ira_allocnos[j];
|
||||
if (cover_class == ALLOCNO_COVER_CLASS (live_a)
|
||||
/* Don't set up conflict for the allocno with itself. */
|
||||
&& num != (int) j)
|
||||
{
|
||||
SET_ALLOCNO_SET_BIT (conflicts[num],
|
||||
ALLOCNO_CONFLICT_ID (live_a),
|
||||
ALLOCNO_MIN (allocno),
|
||||
ALLOCNO_MAX (allocno));
|
||||
SET_ALLOCNO_SET_BIT (conflicts[j], id,
|
||||
ALLOCNO_MIN (live_a),
|
||||
ALLOCNO_MAX (live_a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (r = ira_finish_point_ranges[i]; r != NULL; r = r->finish_next)
|
||||
sparseset_clear_bit (allocnos_live, ALLOCNO_NUM (r->allocno));
|
||||
}
|
||||
sparseset_free (allocnos_live);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return TRUE if the operand constraint STR is commutative. */
|
||||
static bool
|
||||
commutative_constraint_p (const char *str)
|
||||
{
|
||||
bool ignore_p;
|
||||
int c;
|
||||
|
||||
for (ignore_p = false;;)
|
||||
{
|
||||
c = *str;
|
||||
if (c == '\0')
|
||||
break;
|
||||
str += CONSTRAINT_LEN (c, str);
|
||||
if (c == '#')
|
||||
ignore_p = true;
|
||||
else if (c == ',')
|
||||
ignore_p = false;
|
||||
else if (! ignore_p)
|
||||
{
|
||||
/* Usually `%' is the first constraint character but the
|
||||
documentation does not require this. */
|
||||
if (c == '%')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the number of the operand which should be the same in any
|
||||
case as operand with number OP_NUM (or negative value if there is
|
||||
no such operand). If USE_COMMUT_OP_P is TRUE, the function makes
|
||||
temporarily commutative operand exchange before this. The function
|
||||
takes only really possible alternatives into consideration. */
|
||||
static int
|
||||
get_dup_num (int op_num, bool use_commut_op_p)
|
||||
{
|
||||
int curr_alt, c, original, dup;
|
||||
bool ignore_p, commut_op_used_p;
|
||||
const char *str;
|
||||
rtx op;
|
||||
|
||||
if (op_num < 0 || recog_data.n_alternatives == 0)
|
||||
return -1;
|
||||
op = recog_data.operand[op_num];
|
||||
ira_assert (REG_P (op));
|
||||
commut_op_used_p = true;
|
||||
if (use_commut_op_p)
|
||||
{
|
||||
if (commutative_constraint_p (recog_data.constraints[op_num]))
|
||||
op_num++;
|
||||
else if (op_num > 0 && commutative_constraint_p (recog_data.constraints
|
||||
[op_num - 1]))
|
||||
op_num--;
|
||||
else
|
||||
commut_op_used_p = false;
|
||||
}
|
||||
str = recog_data.constraints[op_num];
|
||||
for (ignore_p = false, original = -1, curr_alt = 0;;)
|
||||
{
|
||||
c = *str;
|
||||
if (c == '\0')
|
||||
break;
|
||||
if (c == '#')
|
||||
ignore_p = true;
|
||||
else if (c == ',')
|
||||
{
|
||||
curr_alt++;
|
||||
ignore_p = false;
|
||||
}
|
||||
else if (! ignore_p)
|
||||
switch (c)
|
||||
{
|
||||
case 'X':
|
||||
return -1;
|
||||
|
||||
case 'm':
|
||||
case 'o':
|
||||
/* Accept a register which might be placed in memory. */
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
case '<':
|
||||
case '>':
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
GO_IF_LEGITIMATE_ADDRESS (VOIDmode, op, win_p);
|
||||
break;
|
||||
|
||||
win_p:
|
||||
return -1;
|
||||
|
||||
case 'g':
|
||||
return -1;
|
||||
|
||||
case 'r':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'h': case 'j': case 'k': case 'l':
|
||||
case 'q': case 't': case 'u':
|
||||
case 'v': case 'w': case 'x': case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D':
|
||||
case 'Q': case 'R': case 'S': case 'T': case 'U':
|
||||
case 'W': case 'Y': case 'Z':
|
||||
{
|
||||
enum reg_class cl;
|
||||
|
||||
cl = (c == 'r'
|
||||
? GENERAL_REGS : REG_CLASS_FROM_CONSTRAINT (c, str));
|
||||
if (cl != NO_REGS)
|
||||
return -1;
|
||||
#ifdef EXTRA_CONSTRAINT_STR
|
||||
else if (EXTRA_CONSTRAINT_STR (op, c, str))
|
||||
return -1;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
if (original != -1 && original != c)
|
||||
return -1;
|
||||
original = c;
|
||||
break;
|
||||
}
|
||||
str += CONSTRAINT_LEN (c, str);
|
||||
}
|
||||
if (original == -1)
|
||||
return -1;
|
||||
dup = original - '0';
|
||||
if (use_commut_op_p)
|
||||
{
|
||||
if (commutative_constraint_p (recog_data.constraints[dup]))
|
||||
dup++;
|
||||
else if (dup > 0
|
||||
&& commutative_constraint_p (recog_data.constraints[dup -1]))
|
||||
dup--;
|
||||
else if (! commut_op_used_p)
|
||||
return -1;
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
|
||||
/* Return the operand which should be, in any case, the same as
|
||||
operand with number OP_NUM. If USE_COMMUT_OP_P is TRUE, the
|
||||
function makes temporarily commutative operand exchange before
|
||||
this. */
|
||||
static rtx
|
||||
get_dup (int op_num, bool use_commut_op_p)
|
||||
{
|
||||
int n = get_dup_num (op_num, use_commut_op_p);
|
||||
|
||||
if (n < 0)
|
||||
return NULL_RTX;
|
||||
else
|
||||
return recog_data.operand[n];
|
||||
}
|
||||
|
||||
/* Process registers REG1 and REG2 in move INSN with execution
|
||||
frequency FREQ. The function also processes the registers in a
|
||||
potential move insn (INSN == NULL in this case) with frequency
|
||||
FREQ. The function can modify hard register costs of the
|
||||
corresponding allocnos or create a copy involving the corresponding
|
||||
allocnos. The function does nothing if the both registers are hard
|
||||
registers. When nothing is changed, the function returns
|
||||
FALSE. */
|
||||
static bool
|
||||
process_regs_for_copy (rtx reg1, rtx reg2, rtx insn, int freq)
|
||||
{
|
||||
int hard_regno, cost, index;
|
||||
ira_allocno_t a;
|
||||
enum reg_class rclass, cover_class;
|
||||
enum machine_mode mode;
|
||||
ira_copy_t cp;
|
||||
|
||||
gcc_assert (REG_P (reg1) && REG_P (reg2));
|
||||
if (HARD_REGISTER_P (reg1))
|
||||
{
|
||||
if (HARD_REGISTER_P (reg2))
|
||||
return false;
|
||||
hard_regno = REGNO (reg1);
|
||||
a = ira_curr_regno_allocno_map[REGNO (reg2)];
|
||||
}
|
||||
else if (HARD_REGISTER_P (reg2))
|
||||
{
|
||||
hard_regno = REGNO (reg2);
|
||||
a = ira_curr_regno_allocno_map[REGNO (reg1)];
|
||||
}
|
||||
else if (!CONFLICT_ALLOCNO_P (ira_curr_regno_allocno_map[REGNO (reg1)],
|
||||
ira_curr_regno_allocno_map[REGNO (reg2)]))
|
||||
{
|
||||
cp = ira_add_allocno_copy (ira_curr_regno_allocno_map[REGNO (reg1)],
|
||||
ira_curr_regno_allocno_map[REGNO (reg2)],
|
||||
freq, insn, ira_curr_loop_tree_node);
|
||||
bitmap_set_bit (ira_curr_loop_tree_node->local_copies, cp->num);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
rclass = REGNO_REG_CLASS (hard_regno);
|
||||
mode = ALLOCNO_MODE (a);
|
||||
cover_class = ALLOCNO_COVER_CLASS (a);
|
||||
if (! ira_class_subset_p[rclass][cover_class])
|
||||
return false;
|
||||
if (reg_class_size[rclass] <= (unsigned) CLASS_MAX_NREGS (rclass, mode))
|
||||
/* It is already taken into account in ira-costs.c. */
|
||||
return false;
|
||||
index = ira_class_hard_reg_index[cover_class][hard_regno];
|
||||
if (index < 0)
|
||||
return false;
|
||||
if (HARD_REGISTER_P (reg1))
|
||||
cost = ira_register_move_cost[mode][cover_class][rclass] * freq;
|
||||
else
|
||||
cost = ira_register_move_cost[mode][rclass][cover_class] * freq;
|
||||
ira_allocate_and_set_costs
|
||||
(&ALLOCNO_HARD_REG_COSTS (a), cover_class,
|
||||
ALLOCNO_COVER_CLASS_COST (a));
|
||||
ira_allocate_and_set_costs
|
||||
(&ALLOCNO_CONFLICT_HARD_REG_COSTS (a), cover_class, 0);
|
||||
ALLOCNO_HARD_REG_COSTS (a)[index] -= cost;
|
||||
ALLOCNO_CONFLICT_HARD_REG_COSTS (a)[index] -= cost;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Process all of the output registers of the current insn and
|
||||
the input register REG (its operand number OP_NUM) which dies in the
|
||||
insn as if there were a move insn between them with frequency
|
||||
FREQ. */
|
||||
static void
|
||||
process_reg_shuffles (rtx reg, int op_num, int freq)
|
||||
{
|
||||
int i;
|
||||
rtx another_reg;
|
||||
|
||||
gcc_assert (REG_P (reg));
|
||||
for (i = 0; i < recog_data.n_operands; i++)
|
||||
{
|
||||
another_reg = recog_data.operand[i];
|
||||
|
||||
if (!REG_P (another_reg) || op_num == i
|
||||
|| recog_data.operand_type[i] != OP_OUT)
|
||||
continue;
|
||||
|
||||
process_regs_for_copy (reg, another_reg, NULL_RTX, freq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process INSN and create allocno copies if necessary. For example,
|
||||
it might be because INSN is a pseudo-register move or INSN is two
|
||||
operand insn. */
|
||||
static void
|
||||
add_insn_allocno_copies (rtx insn)
|
||||
{
|
||||
rtx set, operand, dup;
|
||||
const char *str;
|
||||
bool commut_p, bound_p;
|
||||
int i, j, freq;
|
||||
|
||||
freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
|
||||
if (freq == 0)
|
||||
freq = 1;
|
||||
if ((set = single_set (insn)) != NULL_RTX
|
||||
&& REG_P (SET_DEST (set)) && REG_P (SET_SRC (set))
|
||||
&& ! side_effects_p (set)
|
||||
&& find_reg_note (insn, REG_DEAD, SET_SRC (set)) != NULL_RTX)
|
||||
process_regs_for_copy (SET_DEST (set), SET_SRC (set), insn, freq);
|
||||
else
|
||||
{
|
||||
extract_insn (insn);
|
||||
for (i = 0; i < recog_data.n_operands; i++)
|
||||
{
|
||||
operand = recog_data.operand[i];
|
||||
if (REG_P (operand)
|
||||
&& find_reg_note (insn, REG_DEAD, operand) != NULL_RTX)
|
||||
{
|
||||
str = recog_data.constraints[i];
|
||||
while (*str == ' ' && *str == '\t')
|
||||
str++;
|
||||
bound_p = false;
|
||||
for (j = 0, commut_p = false; j < 2; j++, commut_p = true)
|
||||
if ((dup = get_dup (i, commut_p)) != NULL_RTX
|
||||
&& REG_P (dup) && GET_MODE (operand) == GET_MODE (dup)
|
||||
&& process_regs_for_copy (operand, dup, NULL_RTX, freq))
|
||||
bound_p = true;
|
||||
if (bound_p)
|
||||
continue;
|
||||
/* If an operand dies, prefer its hard register for the
|
||||
output operands by decreasing the hard register cost
|
||||
or creating the corresponding allocno copies. The
|
||||
cost will not correspond to a real move insn cost, so
|
||||
make the frequency smaller. */
|
||||
process_reg_shuffles (operand, i, freq < 8 ? 1 : freq / 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add copies originated from BB given by LOOP_TREE_NODE. */
|
||||
static void
|
||||
add_copies (ira_loop_tree_node_t loop_tree_node)
|
||||
{
|
||||
basic_block bb;
|
||||
rtx insn;
|
||||
|
||||
bb = loop_tree_node->bb;
|
||||
if (bb == NULL)
|
||||
return;
|
||||
FOR_BB_INSNS (bb, insn)
|
||||
if (INSN_P (insn))
|
||||
add_insn_allocno_copies (insn);
|
||||
}
|
||||
|
||||
/* Propagate copies the corresponding allocnos on upper loop tree
|
||||
level. */
|
||||
static void
|
||||
propagate_copies (void)
|
||||
{
|
||||
ira_copy_t cp;
|
||||
ira_copy_iterator ci;
|
||||
ira_allocno_t a1, a2, parent_a1, parent_a2;
|
||||
ira_loop_tree_node_t parent;
|
||||
|
||||
FOR_EACH_COPY (cp, ci)
|
||||
{
|
||||
a1 = cp->first;
|
||||
a2 = cp->second;
|
||||
if (ALLOCNO_LOOP_TREE_NODE (a1) == ira_loop_tree_root)
|
||||
continue;
|
||||
ira_assert ((ALLOCNO_LOOP_TREE_NODE (a2) != ira_loop_tree_root));
|
||||
parent = ALLOCNO_LOOP_TREE_NODE (a1)->parent;
|
||||
if ((parent_a1 = ALLOCNO_CAP (a1)) == NULL)
|
||||
parent_a1 = parent->regno_allocno_map[ALLOCNO_REGNO (a1)];
|
||||
if ((parent_a2 = ALLOCNO_CAP (a2)) == NULL)
|
||||
parent_a2 = parent->regno_allocno_map[ALLOCNO_REGNO (a2)];
|
||||
ira_assert (parent_a1 != NULL && parent_a2 != NULL);
|
||||
if (! CONFLICT_ALLOCNO_P (parent_a1, parent_a2))
|
||||
ira_add_allocno_copy (parent_a1, parent_a1, cp->freq,
|
||||
cp->insn, cp->loop_tree_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return TRUE if live ranges of allocnos A1 and A2 intersect. It is
|
||||
used to find a conflict for new allocnos or allocnos with the
|
||||
different cover classes. */
|
||||
bool
|
||||
ira_allocno_live_ranges_intersect_p (ira_allocno_t a1, ira_allocno_t a2)
|
||||
{
|
||||
allocno_live_range_t r1, r2;
|
||||
|
||||
if (a1 == a2)
|
||||
return false;
|
||||
if (ALLOCNO_REG (a1) != NULL && ALLOCNO_REG (a2) != NULL
|
||||
&& (ORIGINAL_REGNO (ALLOCNO_REG (a1))
|
||||
== ORIGINAL_REGNO (ALLOCNO_REG (a2))))
|
||||
return false;
|
||||
/* Remember the ranges are always kept ordered. */
|
||||
for (r1 = ALLOCNO_LIVE_RANGES (a1), r2 = ALLOCNO_LIVE_RANGES (a2);
|
||||
r1 != NULL && r2 != NULL;)
|
||||
{
|
||||
if (r1->start > r2->finish)
|
||||
r1 = r1->next;
|
||||
else if (r2->start > r1->finish)
|
||||
r2 = r2->next;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return TRUE if live ranges of pseudo-registers REGNO1 and REGNO2
|
||||
intersect. This should be used when there is only one region.
|
||||
Currently this is used during reload. */
|
||||
bool
|
||||
ira_pseudo_live_ranges_intersect_p (int regno1, int regno2)
|
||||
{
|
||||
ira_allocno_t a1, a2;
|
||||
|
||||
ira_assert (regno1 >= FIRST_PSEUDO_REGISTER
|
||||
&& regno2 >= FIRST_PSEUDO_REGISTER);
|
||||
/* Reg info caclulated by dataflow infrastructure can be different
|
||||
from one calculated by regclass. */
|
||||
if ((a1 = ira_loop_tree_root->regno_allocno_map[regno1]) == NULL
|
||||
|| (a2 = ira_loop_tree_root->regno_allocno_map[regno2]) == NULL)
|
||||
return false;
|
||||
return ira_allocno_live_ranges_intersect_p (a1, a2);
|
||||
}
|
||||
|
||||
/* Array used to collect all conflict allocnos for given allocno. */
|
||||
static ira_allocno_t *collected_conflict_allocnos;
|
||||
|
||||
/* Build conflict vectors or bit conflict vectors (whatever is more
|
||||
profitable) for allocno A from the conflict table and propagate the
|
||||
conflicts to upper level allocno. */
|
||||
static void
|
||||
build_allocno_conflicts (ira_allocno_t a)
|
||||
{
|
||||
int i, px, parent_num;
|
||||
int conflict_bit_vec_words_num;
|
||||
ira_loop_tree_node_t parent;
|
||||
ira_allocno_t parent_a, another_a, another_parent_a;
|
||||
ira_allocno_t *vec;
|
||||
IRA_INT_TYPE *allocno_conflicts;
|
||||
ira_allocno_set_iterator asi;
|
||||
|
||||
allocno_conflicts = conflicts[ALLOCNO_NUM (a)];
|
||||
px = 0;
|
||||
FOR_EACH_ALLOCNO_IN_SET (allocno_conflicts,
|
||||
ALLOCNO_MIN (a), ALLOCNO_MAX (a), i, asi)
|
||||
{
|
||||
another_a = ira_conflict_id_allocno_map[i];
|
||||
ira_assert (ALLOCNO_COVER_CLASS (a)
|
||||
== ALLOCNO_COVER_CLASS (another_a));
|
||||
collected_conflict_allocnos[px++] = another_a;
|
||||
}
|
||||
if (ira_conflict_vector_profitable_p (a, px))
|
||||
{
|
||||
ira_allocate_allocno_conflict_vec (a, px);
|
||||
vec = (ira_allocno_t*) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a);
|
||||
memcpy (vec, collected_conflict_allocnos, sizeof (ira_allocno_t) * px);
|
||||
vec[px] = NULL;
|
||||
ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = px;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) = conflicts[ALLOCNO_NUM (a)];
|
||||
if (ALLOCNO_MAX (a) < ALLOCNO_MIN (a))
|
||||
conflict_bit_vec_words_num = 0;
|
||||
else
|
||||
conflict_bit_vec_words_num
|
||||
= ((ALLOCNO_MAX (a) - ALLOCNO_MIN (a) + IRA_INT_BITS)
|
||||
/ IRA_INT_BITS);
|
||||
ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a)
|
||||
= conflict_bit_vec_words_num * sizeof (IRA_INT_TYPE);
|
||||
}
|
||||
parent = ALLOCNO_LOOP_TREE_NODE (a)->parent;
|
||||
if ((parent_a = ALLOCNO_CAP (a)) == NULL
|
||||
&& (parent == NULL
|
||||
|| (parent_a = parent->regno_allocno_map[ALLOCNO_REGNO (a)])
|
||||
== NULL))
|
||||
return;
|
||||
ira_assert (parent != NULL);
|
||||
ira_assert (ALLOCNO_COVER_CLASS (a) == ALLOCNO_COVER_CLASS (parent_a));
|
||||
parent_num = ALLOCNO_NUM (parent_a);
|
||||
FOR_EACH_ALLOCNO_IN_SET (allocno_conflicts,
|
||||
ALLOCNO_MIN (a), ALLOCNO_MAX (a), i, asi)
|
||||
{
|
||||
another_a = ira_conflict_id_allocno_map[i];
|
||||
ira_assert (ALLOCNO_COVER_CLASS (a)
|
||||
== ALLOCNO_COVER_CLASS (another_a));
|
||||
if ((another_parent_a = ALLOCNO_CAP (another_a)) == NULL
|
||||
&& (another_parent_a = (parent->regno_allocno_map
|
||||
[ALLOCNO_REGNO (another_a)])) == NULL)
|
||||
continue;
|
||||
ira_assert (ALLOCNO_NUM (another_parent_a) >= 0);
|
||||
ira_assert (ALLOCNO_COVER_CLASS (another_a)
|
||||
== ALLOCNO_COVER_CLASS (another_parent_a));
|
||||
SET_ALLOCNO_SET_BIT (conflicts[parent_num],
|
||||
ALLOCNO_CONFLICT_ID (another_parent_a),
|
||||
ALLOCNO_MIN (parent_a),
|
||||
ALLOCNO_MAX (parent_a));
|
||||
}
|
||||
}
|
||||
|
||||
/* Build conflict vectors or bit conflict vectors (whatever is more
|
||||
profitable) of all allocnos from the conflict table. */
|
||||
static void
|
||||
build_conflicts (void)
|
||||
{
|
||||
int i;
|
||||
ira_allocno_t a, cap;
|
||||
|
||||
collected_conflict_allocnos
|
||||
= (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
|
||||
* ira_allocnos_num);
|
||||
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
|
||||
for (a = ira_regno_allocno_map[i];
|
||||
a != NULL;
|
||||
a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
|
||||
{
|
||||
build_allocno_conflicts (a);
|
||||
for (cap = ALLOCNO_CAP (a); cap != NULL; cap = ALLOCNO_CAP (cap))
|
||||
build_allocno_conflicts (cap);
|
||||
}
|
||||
ira_free (collected_conflict_allocnos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Print hard reg set SET with TITLE to FILE. */
|
||||
static void
|
||||
print_hard_reg_set (FILE *file, const char *title, HARD_REG_SET set)
|
||||
{
|
||||
int i, start;
|
||||
|
||||
fprintf (file, title);
|
||||
for (start = -1, i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
{
|
||||
if (TEST_HARD_REG_BIT (set, i))
|
||||
{
|
||||
if (i == 0 || ! TEST_HARD_REG_BIT (set, i - 1))
|
||||
start = i;
|
||||
}
|
||||
if (start >= 0
|
||||
&& (i == FIRST_PSEUDO_REGISTER - 1 || ! TEST_HARD_REG_BIT (set, i)))
|
||||
{
|
||||
if (start == i - 1)
|
||||
fprintf (file, " %d", start);
|
||||
else if (start == i - 2)
|
||||
fprintf (file, " %d %d", start, start + 1);
|
||||
else
|
||||
fprintf (file, " %d-%d", start, i - 1);
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
|
||||
/* Print information about allocno or only regno (if REG_P) conflicts
|
||||
to FILE. */
|
||||
static void
|
||||
print_conflicts (FILE *file, bool reg_p)
|
||||
{
|
||||
ira_allocno_t a;
|
||||
ira_allocno_iterator ai;
|
||||
HARD_REG_SET conflicting_hard_regs;
|
||||
|
||||
FOR_EACH_ALLOCNO (a, ai)
|
||||
{
|
||||
ira_allocno_t conflict_a;
|
||||
ira_allocno_conflict_iterator aci;
|
||||
basic_block bb;
|
||||
|
||||
if (reg_p)
|
||||
fprintf (file, ";; r%d", ALLOCNO_REGNO (a));
|
||||
else
|
||||
{
|
||||
fprintf (file, ";; a%d(r%d,", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
|
||||
if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
|
||||
fprintf (file, "b%d", bb->index);
|
||||
else
|
||||
fprintf (file, "l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num);
|
||||
fprintf (file, ")");
|
||||
}
|
||||
fprintf (file, " conflicts:");
|
||||
if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) != NULL)
|
||||
FOR_EACH_ALLOCNO_CONFLICT (a, conflict_a, aci)
|
||||
{
|
||||
if (reg_p)
|
||||
fprintf (file, " r%d,", ALLOCNO_REGNO (conflict_a));
|
||||
else
|
||||
{
|
||||
fprintf (file, " a%d(r%d,", ALLOCNO_NUM (conflict_a),
|
||||
ALLOCNO_REGNO (conflict_a));
|
||||
if ((bb = ALLOCNO_LOOP_TREE_NODE (conflict_a)->bb) != NULL)
|
||||
fprintf (file, "b%d)", bb->index);
|
||||
else
|
||||
fprintf (file, "l%d)",
|
||||
ALLOCNO_LOOP_TREE_NODE (conflict_a)->loop->num);
|
||||
}
|
||||
}
|
||||
COPY_HARD_REG_SET (conflicting_hard_regs,
|
||||
ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
|
||||
AND_COMPL_HARD_REG_SET (conflicting_hard_regs, ira_no_alloc_regs);
|
||||
AND_HARD_REG_SET (conflicting_hard_regs,
|
||||
reg_class_contents[ALLOCNO_COVER_CLASS (a)]);
|
||||
print_hard_reg_set (file, "\n;; total conflict hard regs:",
|
||||
conflicting_hard_regs);
|
||||
COPY_HARD_REG_SET (conflicting_hard_regs,
|
||||
ALLOCNO_CONFLICT_HARD_REGS (a));
|
||||
AND_COMPL_HARD_REG_SET (conflicting_hard_regs, ira_no_alloc_regs);
|
||||
AND_HARD_REG_SET (conflicting_hard_regs,
|
||||
reg_class_contents[ALLOCNO_COVER_CLASS (a)]);
|
||||
print_hard_reg_set (file, ";; conflict hard regs:",
|
||||
conflicting_hard_regs);
|
||||
}
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
|
||||
/* Print information about allocno or only regno (if REG_P) conflicts
|
||||
to stderr. */
|
||||
void
|
||||
ira_debug_conflicts (bool reg_p)
|
||||
{
|
||||
print_conflicts (stderr, reg_p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Entry function which builds allocno conflicts and allocno copies
|
||||
and accumulate some allocno info on upper level regions. */
|
||||
void
|
||||
ira_build_conflicts (void)
|
||||
{
|
||||
ira_allocno_t a;
|
||||
ira_allocno_iterator ai;
|
||||
|
||||
if (optimize)
|
||||
{
|
||||
build_conflict_bit_table ();
|
||||
build_conflicts ();
|
||||
ira_traverse_loop_tree (true, ira_loop_tree_root, NULL, add_copies);
|
||||
/* We need finished conflict table for the subsequent call. */
|
||||
if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
|
||||
|| flag_ira_algorithm == IRA_ALGORITHM_MIXED)
|
||||
propagate_copies ();
|
||||
/* Now we can free memory for the conflict table (see function
|
||||
build_allocno_conflicts for details). */
|
||||
FOR_EACH_ALLOCNO (a, ai)
|
||||
{
|
||||
if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) != conflicts[ALLOCNO_NUM (a)])
|
||||
ira_free (conflicts[ALLOCNO_NUM (a)]);
|
||||
}
|
||||
ira_free (conflicts);
|
||||
}
|
||||
FOR_EACH_ALLOCNO (a, ai)
|
||||
{
|
||||
if (ALLOCNO_CALLS_CROSSED_NUM (a) == 0)
|
||||
continue;
|
||||
if (! flag_caller_saves)
|
||||
{
|
||||
IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a),
|
||||
call_used_reg_set);
|
||||
if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0)
|
||||
IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a),
|
||||
call_used_reg_set);
|
||||
}
|
||||
else
|
||||
{
|
||||
IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a),
|
||||
no_caller_save_reg_set);
|
||||
if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0)
|
||||
IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a),
|
||||
no_caller_save_reg_set);
|
||||
}
|
||||
}
|
||||
if (optimize && internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
|
||||
print_conflicts (ira_dump_file, false);
|
||||
}
|
1594
gcc/ira-costs.c
Normal file
1594
gcc/ira-costs.c
Normal file
File diff suppressed because it is too large
Load diff
1019
gcc/ira-emit.c
Normal file
1019
gcc/ira-emit.c
Normal file
File diff suppressed because it is too large
Load diff
1200
gcc/ira-int.h
Normal file
1200
gcc/ira-int.h
Normal file
File diff suppressed because it is too large
Load diff
967
gcc/ira-lives.c
Normal file
967
gcc/ira-lives.c
Normal file
|
@ -0,0 +1,967 @@
|
|||
/* IRA processing allocno lives to build allocno live ranges.
|
||||
Copyright (C) 2006, 2007, 2008
|
||||
Free Software Foundation, Inc.
|
||||
Contributed by Vladimir Makarov <vmakarov@redhat.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 "regs.h"
|
||||
#include "rtl.h"
|
||||
#include "tm_p.h"
|
||||
#include "target.h"
|
||||
#include "flags.h"
|
||||
#include "hard-reg-set.h"
|
||||
#include "basic-block.h"
|
||||
#include "insn-config.h"
|
||||
#include "recog.h"
|
||||
#include "toplev.h"
|
||||
#include "params.h"
|
||||
#include "df.h"
|
||||
#include "sparseset.h"
|
||||
#include "ira-int.h"
|
||||
|
||||
/* The code in this file is similar to one in global but the code
|
||||
works on the allocno basis and creates live ranges instead of
|
||||
pseudo-register conflicts. */
|
||||
|
||||
/* Program points are enumerated by numbers from range
|
||||
0..IRA_MAX_POINT-1. There are approximately two times more program
|
||||
points than insns. Program points are places in the program where
|
||||
liveness info can be changed. In most general case (there are more
|
||||
complicated cases too) some program points correspond to places
|
||||
where input operand dies and other ones correspond to places where
|
||||
output operands are born. */
|
||||
int ira_max_point;
|
||||
|
||||
/* Arrays of size IRA_MAX_POINT mapping a program point to the allocno
|
||||
live ranges with given start/finish point. */
|
||||
allocno_live_range_t *ira_start_point_ranges, *ira_finish_point_ranges;
|
||||
|
||||
/* Number of the current program point. */
|
||||
static int curr_point;
|
||||
|
||||
/* Point where register pressure excess started or -1 if there is no
|
||||
register pressure excess. Excess pressure for a register class at
|
||||
some point means that there are more allocnos of given register
|
||||
class living at the point than number of hard-registers of the
|
||||
class available for the allocation. It is defined only for cover
|
||||
classes. */
|
||||
static int high_pressure_start_point[N_REG_CLASSES];
|
||||
|
||||
/* Allocnos live at current point in the scan. */
|
||||
static sparseset allocnos_live;
|
||||
|
||||
/* Set of hard regs (except eliminable ones) currently live. */
|
||||
static HARD_REG_SET hard_regs_live;
|
||||
|
||||
/* The loop tree node corresponding to the current basic block. */
|
||||
static ira_loop_tree_node_t curr_bb_node;
|
||||
|
||||
/* The function processing birth of register REGNO. It updates living
|
||||
hard regs and conflict hard regs for living allocnos or starts a
|
||||
new live range for the allocno corresponding to REGNO if it is
|
||||
necessary. */
|
||||
static void
|
||||
make_regno_born (int regno)
|
||||
{
|
||||
unsigned int i;
|
||||
ira_allocno_t a;
|
||||
allocno_live_range_t p;
|
||||
|
||||
if (regno < FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
SET_HARD_REG_BIT (hard_regs_live, regno);
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
|
||||
{
|
||||
SET_HARD_REG_BIT (ALLOCNO_CONFLICT_HARD_REGS (ira_allocnos[i]),
|
||||
regno);
|
||||
SET_HARD_REG_BIT (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (ira_allocnos[i]),
|
||||
regno);
|
||||
}
|
||||
return;
|
||||
}
|
||||
a = ira_curr_regno_allocno_map[regno];
|
||||
if (a == NULL)
|
||||
return;
|
||||
if ((p = ALLOCNO_LIVE_RANGES (a)) == NULL
|
||||
|| (p->finish != curr_point && p->finish + 1 != curr_point))
|
||||
ALLOCNO_LIVE_RANGES (a)
|
||||
= ira_create_allocno_live_range (a, curr_point, -1,
|
||||
ALLOCNO_LIVE_RANGES (a));
|
||||
}
|
||||
|
||||
/* Update ALLOCNO_EXCESS_PRESSURE_POINTS_NUM for allocno A. */
|
||||
static void
|
||||
update_allocno_pressure_excess_length (ira_allocno_t a)
|
||||
{
|
||||
int start;
|
||||
enum reg_class cover_class;
|
||||
allocno_live_range_t p;
|
||||
|
||||
cover_class = ALLOCNO_COVER_CLASS (a);
|
||||
if (high_pressure_start_point[cover_class] < 0)
|
||||
return;
|
||||
p = ALLOCNO_LIVE_RANGES (a);
|
||||
ira_assert (p != NULL);
|
||||
start = (high_pressure_start_point[cover_class] > p->start
|
||||
? high_pressure_start_point[cover_class] : p->start);
|
||||
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a) += curr_point - start + 1;
|
||||
}
|
||||
|
||||
/* Process the death of register REGNO. This updates hard_regs_live
|
||||
or finishes the current live range for the allocno corresponding to
|
||||
REGNO. */
|
||||
static void
|
||||
make_regno_dead (int regno)
|
||||
{
|
||||
ira_allocno_t a;
|
||||
allocno_live_range_t p;
|
||||
|
||||
if (regno < FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
CLEAR_HARD_REG_BIT (hard_regs_live, regno);
|
||||
return;
|
||||
}
|
||||
a = ira_curr_regno_allocno_map[regno];
|
||||
if (a == NULL)
|
||||
return;
|
||||
p = ALLOCNO_LIVE_RANGES (a);
|
||||
ira_assert (p != NULL);
|
||||
p->finish = curr_point;
|
||||
update_allocno_pressure_excess_length (a);
|
||||
}
|
||||
|
||||
/* Process the birth and, right after then, death of register
|
||||
REGNO. */
|
||||
static void
|
||||
make_regno_born_and_dead (int regno)
|
||||
{
|
||||
make_regno_born (regno);
|
||||
make_regno_dead (regno);
|
||||
}
|
||||
|
||||
/* The current register pressures for each cover class for the current
|
||||
basic block. */
|
||||
static int curr_reg_pressure[N_REG_CLASSES];
|
||||
|
||||
/* Mark allocno A as currently living and update current register
|
||||
pressure, maximal register pressure for the current BB, start point
|
||||
of the register pressure excess, and conflicting hard registers of
|
||||
A. */
|
||||
static void
|
||||
set_allocno_live (ira_allocno_t a)
|
||||
{
|
||||
int nregs;
|
||||
enum reg_class cover_class;
|
||||
|
||||
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
|
||||
return;
|
||||
sparseset_set_bit (allocnos_live, ALLOCNO_NUM (a));
|
||||
IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), hard_regs_live);
|
||||
IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), hard_regs_live);
|
||||
cover_class = ALLOCNO_COVER_CLASS (a);
|
||||
nregs = ira_reg_class_nregs[cover_class][ALLOCNO_MODE (a)];
|
||||
curr_reg_pressure[cover_class] += nregs;
|
||||
if (high_pressure_start_point[cover_class] < 0
|
||||
&& (curr_reg_pressure[cover_class]
|
||||
> ira_available_class_regs[cover_class]))
|
||||
high_pressure_start_point[cover_class] = curr_point;
|
||||
if (curr_bb_node->reg_pressure[cover_class]
|
||||
< curr_reg_pressure[cover_class])
|
||||
curr_bb_node->reg_pressure[cover_class] = curr_reg_pressure[cover_class];
|
||||
}
|
||||
|
||||
/* Mark allocno A as currently not living and update current register
|
||||
pressure, start point of the register pressure excess, and register
|
||||
pressure excess length for living allocnos. */
|
||||
static void
|
||||
clear_allocno_live (ira_allocno_t a)
|
||||
{
|
||||
unsigned int i;
|
||||
enum reg_class cover_class;
|
||||
|
||||
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
|
||||
{
|
||||
cover_class = ALLOCNO_COVER_CLASS (a);
|
||||
curr_reg_pressure[cover_class]
|
||||
-= ira_reg_class_nregs[cover_class][ALLOCNO_MODE (a)];
|
||||
ira_assert (curr_reg_pressure[cover_class] >= 0);
|
||||
if (high_pressure_start_point[cover_class] >= 0
|
||||
&& (curr_reg_pressure[cover_class]
|
||||
<= ira_available_class_regs[cover_class]))
|
||||
{
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
|
||||
{
|
||||
update_allocno_pressure_excess_length (ira_allocnos[i]);
|
||||
}
|
||||
high_pressure_start_point[cover_class] = -1;
|
||||
}
|
||||
}
|
||||
sparseset_clear_bit (allocnos_live, ALLOCNO_NUM (a));
|
||||
}
|
||||
|
||||
/* Record all regs that are set in any one insn. Communication from
|
||||
mark_reg_{store,clobber}. */
|
||||
static VEC(rtx, heap) *regs_set;
|
||||
|
||||
/* Handle the case where REG is set by the insn being scanned, during
|
||||
the scan to build live ranges and calculate reg pressure info.
|
||||
Store a 1 in hard_regs_live or allocnos_live for this register or
|
||||
the corresponding allocno, record how many consecutive hardware
|
||||
registers it actually needs.
|
||||
|
||||
Note that even if REG does not remain alive after this insn, we
|
||||
must mark it here as live, to ensure a conflict between REG and any
|
||||
other reg allocnos set in this insn that really do live. This is
|
||||
because those other allocnos could be considered after this.
|
||||
|
||||
REG might actually be something other than a register; if so, we do
|
||||
nothing.
|
||||
|
||||
SETTER is 0 if this register was modified by an auto-increment
|
||||
(i.e., a REG_INC note was found for it). */
|
||||
static void
|
||||
mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED,
|
||||
void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int regno;
|
||||
|
||||
if (GET_CODE (reg) == SUBREG)
|
||||
reg = SUBREG_REG (reg);
|
||||
|
||||
if (! REG_P (reg))
|
||||
return;
|
||||
|
||||
VEC_safe_push (rtx, heap, regs_set, reg);
|
||||
|
||||
regno = REGNO (reg);
|
||||
|
||||
if (regno >= FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
ira_allocno_t a = ira_curr_regno_allocno_map[regno];
|
||||
|
||||
if (a != NULL)
|
||||
{
|
||||
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
|
||||
return;
|
||||
set_allocno_live (a);
|
||||
}
|
||||
make_regno_born (regno);
|
||||
}
|
||||
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
|
||||
{
|
||||
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
|
||||
enum reg_class cover_class;
|
||||
|
||||
while (regno < last)
|
||||
{
|
||||
if (! TEST_HARD_REG_BIT (hard_regs_live, regno)
|
||||
&& ! TEST_HARD_REG_BIT (eliminable_regset, regno))
|
||||
{
|
||||
cover_class = ira_class_translate[REGNO_REG_CLASS (regno)];
|
||||
if (cover_class != NO_REGS)
|
||||
{
|
||||
curr_reg_pressure[cover_class]++;
|
||||
if (high_pressure_start_point[cover_class] < 0
|
||||
&& (curr_reg_pressure[cover_class]
|
||||
> ira_available_class_regs[cover_class]))
|
||||
high_pressure_start_point[cover_class] = curr_point;
|
||||
}
|
||||
make_regno_born (regno);
|
||||
if (cover_class != NO_REGS
|
||||
&& (curr_bb_node->reg_pressure[cover_class]
|
||||
< curr_reg_pressure[cover_class]))
|
||||
curr_bb_node->reg_pressure[cover_class]
|
||||
= curr_reg_pressure[cover_class];
|
||||
}
|
||||
regno++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Like mark_reg_store except notice just CLOBBERs; ignore SETs. */
|
||||
static void
|
||||
mark_reg_clobber (rtx reg, const_rtx setter, void *data)
|
||||
{
|
||||
if (GET_CODE (setter) == CLOBBER)
|
||||
mark_reg_store (reg, setter, data);
|
||||
}
|
||||
|
||||
/* Record that hard register REG (if it is a hard register) has
|
||||
conflicts with all the allocno currently live or the corresponding
|
||||
allocno lives at just the current program point. Do not mark REG
|
||||
(or the allocno) itself as live. */
|
||||
static void
|
||||
mark_reg_conflicts (rtx reg)
|
||||
{
|
||||
int regno;
|
||||
|
||||
if (GET_CODE (reg) == SUBREG)
|
||||
reg = SUBREG_REG (reg);
|
||||
|
||||
if (! REG_P (reg))
|
||||
return;
|
||||
|
||||
regno = REGNO (reg);
|
||||
|
||||
if (regno >= FIRST_PSEUDO_REGISTER)
|
||||
make_regno_born_and_dead (regno);
|
||||
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
|
||||
{
|
||||
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
|
||||
|
||||
while (regno < last)
|
||||
{
|
||||
make_regno_born_and_dead (regno);
|
||||
regno++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark REG (or the corresponding allocno) as being dead (following
|
||||
the insn being scanned now). Store a 0 in hard_regs_live or
|
||||
allocnos_live for the register. */
|
||||
static void
|
||||
mark_reg_death (rtx reg)
|
||||
{
|
||||
unsigned int i;
|
||||
int regno = REGNO (reg);
|
||||
|
||||
if (regno >= FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
ira_allocno_t a = ira_curr_regno_allocno_map[regno];
|
||||
|
||||
if (a != NULL)
|
||||
{
|
||||
if (! sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
|
||||
return;
|
||||
clear_allocno_live (a);
|
||||
}
|
||||
make_regno_dead (regno);
|
||||
}
|
||||
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
|
||||
{
|
||||
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
|
||||
enum reg_class cover_class;
|
||||
|
||||
while (regno < last)
|
||||
{
|
||||
if (TEST_HARD_REG_BIT (hard_regs_live, regno))
|
||||
{
|
||||
cover_class = ira_class_translate[REGNO_REG_CLASS (regno)];
|
||||
if (cover_class != NO_REGS)
|
||||
{
|
||||
curr_reg_pressure[cover_class]--;
|
||||
if (high_pressure_start_point[cover_class] >= 0
|
||||
&& (curr_reg_pressure[cover_class]
|
||||
<= ira_available_class_regs[cover_class]))
|
||||
{
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
|
||||
{
|
||||
update_allocno_pressure_excess_length
|
||||
(ira_allocnos[i]);
|
||||
}
|
||||
high_pressure_start_point[cover_class] = -1;
|
||||
}
|
||||
ira_assert (curr_reg_pressure[cover_class] >= 0);
|
||||
}
|
||||
make_regno_dead (regno);
|
||||
}
|
||||
regno++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Checks that CONSTRAINTS permits to use only one hard register. If
|
||||
it is so, the function returns the class of the hard register.
|
||||
Otherwise it returns NO_REGS. */
|
||||
static enum reg_class
|
||||
single_reg_class (const char *constraints, rtx op, rtx equiv_const)
|
||||
{
|
||||
int ignore_p;
|
||||
enum reg_class cl, next_cl;
|
||||
int c;
|
||||
|
||||
cl = NO_REGS;
|
||||
for (ignore_p = false;
|
||||
(c = *constraints);
|
||||
constraints += CONSTRAINT_LEN (c, constraints))
|
||||
if (c == '#')
|
||||
ignore_p = true;
|
||||
else if (c == ',')
|
||||
ignore_p = false;
|
||||
else if (! ignore_p)
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '=':
|
||||
case '+':
|
||||
case '*':
|
||||
case '&':
|
||||
case '%':
|
||||
case '!':
|
||||
case '?':
|
||||
break;
|
||||
case 'i':
|
||||
if (CONSTANT_P (op)
|
||||
|| (equiv_const != NULL_RTX && CONSTANT_P (equiv_const)))
|
||||
return NO_REGS;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if (GET_CODE (op) == CONST_INT
|
||||
|| (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode)
|
||||
|| (equiv_const != NULL_RTX
|
||||
&& (GET_CODE (equiv_const) == CONST_INT
|
||||
|| (GET_CODE (equiv_const) == CONST_DOUBLE
|
||||
&& GET_MODE (equiv_const) == VOIDmode))))
|
||||
return NO_REGS;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if ((CONSTANT_P (op) && GET_CODE (op) != CONST_INT
|
||||
&& (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != VOIDmode))
|
||||
|| (equiv_const != NULL_RTX
|
||||
&& CONSTANT_P (equiv_const)
|
||||
&& GET_CODE (equiv_const) != CONST_INT
|
||||
&& (GET_CODE (equiv_const) != CONST_DOUBLE
|
||||
|| GET_MODE (equiv_const) != VOIDmode)))
|
||||
return NO_REGS;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
if ((GET_CODE (op) == CONST_INT
|
||||
&& CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), c, constraints))
|
||||
|| (equiv_const != NULL_RTX
|
||||
&& GET_CODE (equiv_const) == CONST_INT
|
||||
&& CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const),
|
||||
c, constraints)))
|
||||
return NO_REGS;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'F':
|
||||
if (GET_CODE (op) == CONST_DOUBLE
|
||||
|| (GET_CODE (op) == CONST_VECTOR
|
||||
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_FLOAT)
|
||||
|| (equiv_const != NULL_RTX
|
||||
&& (GET_CODE (equiv_const) == CONST_DOUBLE
|
||||
|| (GET_CODE (equiv_const) == CONST_VECTOR
|
||||
&& (GET_MODE_CLASS (GET_MODE (equiv_const))
|
||||
== MODE_VECTOR_FLOAT)))))
|
||||
return NO_REGS;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
case 'H':
|
||||
if ((GET_CODE (op) == CONST_DOUBLE
|
||||
&& CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, c, constraints))
|
||||
|| (equiv_const != NULL_RTX
|
||||
&& GET_CODE (equiv_const) == CONST_DOUBLE
|
||||
&& CONST_DOUBLE_OK_FOR_CONSTRAINT_P (equiv_const,
|
||||
c, constraints)))
|
||||
return NO_REGS;
|
||||
/* ??? what about memory */
|
||||
case 'r':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'h': case 'j': case 'k': case 'l':
|
||||
case 'q': case 't': case 'u':
|
||||
case 'v': case 'w': case 'x': case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D':
|
||||
case 'Q': case 'R': case 'S': case 'T': case 'U':
|
||||
case 'W': case 'Y': case 'Z':
|
||||
next_cl = (c == 'r'
|
||||
? GENERAL_REGS
|
||||
: REG_CLASS_FROM_CONSTRAINT (c, constraints));
|
||||
if ((cl != NO_REGS && next_cl != cl)
|
||||
|| ira_available_class_regs[next_cl] > 1)
|
||||
return NO_REGS;
|
||||
cl = next_cl;
|
||||
break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
next_cl
|
||||
= single_reg_class (recog_data.constraints[c - '0'],
|
||||
recog_data.operand[c - '0'], NULL_RTX);
|
||||
if ((cl != NO_REGS && next_cl != cl) || next_cl == NO_REGS
|
||||
|| ira_available_class_regs[next_cl] > 1)
|
||||
return NO_REGS;
|
||||
cl = next_cl;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NO_REGS;
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
/* The function checks that operand OP_NUM of the current insn can use
|
||||
only one hard register. If it is so, the function returns the
|
||||
class of the hard register. Otherwise it returns NO_REGS. */
|
||||
static enum reg_class
|
||||
single_reg_operand_class (int op_num)
|
||||
{
|
||||
if (op_num < 0 || recog_data.n_alternatives == 0)
|
||||
return NO_REGS;
|
||||
return single_reg_class (recog_data.constraints[op_num],
|
||||
recog_data.operand[op_num], NULL_RTX);
|
||||
}
|
||||
|
||||
/* Processes input operands, if IN_P, or output operands otherwise of
|
||||
the current insn with FREQ to find allocno which can use only one
|
||||
hard register and makes other currently living allocnos conflicting
|
||||
with the hard register. */
|
||||
static void
|
||||
process_single_reg_class_operands (bool in_p, int freq)
|
||||
{
|
||||
int i, regno, cost;
|
||||
unsigned int px;
|
||||
enum reg_class cl, cover_class;
|
||||
rtx operand;
|
||||
ira_allocno_t operand_a, a;
|
||||
|
||||
for (i = 0; i < recog_data.n_operands; i++)
|
||||
{
|
||||
operand = recog_data.operand[i];
|
||||
if (in_p && recog_data.operand_type[i] != OP_IN
|
||||
&& recog_data.operand_type[i] != OP_INOUT)
|
||||
continue;
|
||||
if (! in_p && recog_data.operand_type[i] != OP_OUT
|
||||
&& recog_data.operand_type[i] != OP_INOUT)
|
||||
continue;
|
||||
cl = single_reg_operand_class (i);
|
||||
if (cl == NO_REGS)
|
||||
continue;
|
||||
|
||||
operand_a = NULL;
|
||||
|
||||
if (GET_CODE (operand) == SUBREG)
|
||||
operand = SUBREG_REG (operand);
|
||||
|
||||
if (REG_P (operand)
|
||||
&& (regno = REGNO (operand)) >= FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
enum machine_mode mode;
|
||||
enum reg_class cover_class;
|
||||
|
||||
operand_a = ira_curr_regno_allocno_map[regno];
|
||||
mode = ALLOCNO_MODE (operand_a);
|
||||
cover_class = ALLOCNO_COVER_CLASS (operand_a);
|
||||
if (ira_class_subset_p[cl][cover_class]
|
||||
&& ira_class_hard_regs_num[cl] != 0
|
||||
&& (ira_class_hard_reg_index[cover_class]
|
||||
[ira_class_hard_regs[cl][0]]) >= 0
|
||||
&& reg_class_size[cl] <= (unsigned) CLASS_MAX_NREGS (cl, mode))
|
||||
{
|
||||
/* ??? FREQ */
|
||||
cost = freq * (in_p
|
||||
? ira_register_move_cost[mode][cover_class][cl]
|
||||
: ira_register_move_cost[mode][cl][cover_class]);
|
||||
ira_allocate_and_set_costs
|
||||
(&ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a), cover_class, 0);
|
||||
ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a)
|
||||
[ira_class_hard_reg_index
|
||||
[cover_class][ira_class_hard_regs[cl][0]]]
|
||||
-= cost;
|
||||
}
|
||||
}
|
||||
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
|
||||
{
|
||||
a = ira_allocnos[px];
|
||||
cover_class = ALLOCNO_COVER_CLASS (a);
|
||||
if (a != operand_a)
|
||||
{
|
||||
/* We could increase costs of A instead of making it
|
||||
conflicting with the hard register. But it works worse
|
||||
because it will be spilled in reload in anyway. */
|
||||
IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a),
|
||||
reg_class_contents[cl]);
|
||||
IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a),
|
||||
reg_class_contents[cl]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Process insns of the basic block given by its LOOP_TREE_NODE to
|
||||
update allocno live ranges, allocno hard register conflicts,
|
||||
intersected calls, and register pressure info for allocnos for the
|
||||
basic block for and regions containing the basic block. */
|
||||
static void
|
||||
process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
|
||||
{
|
||||
int i;
|
||||
unsigned int j;
|
||||
basic_block bb;
|
||||
rtx insn;
|
||||
edge e;
|
||||
edge_iterator ei;
|
||||
bitmap_iterator bi;
|
||||
bitmap reg_live_in;
|
||||
unsigned int px;
|
||||
|
||||
bb = loop_tree_node->bb;
|
||||
if (bb != NULL)
|
||||
{
|
||||
for (i = 0; i < ira_reg_class_cover_size; i++)
|
||||
{
|
||||
curr_reg_pressure[ira_reg_class_cover[i]] = 0;
|
||||
high_pressure_start_point[ira_reg_class_cover[i]] = -1;
|
||||
}
|
||||
curr_bb_node = loop_tree_node;
|
||||
reg_live_in = DF_LR_IN (bb);
|
||||
sparseset_clear (allocnos_live);
|
||||
REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_in);
|
||||
AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset);
|
||||
AND_COMPL_HARD_REG_SET (hard_regs_live, ira_no_alloc_regs);
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (TEST_HARD_REG_BIT (hard_regs_live, i))
|
||||
{
|
||||
enum reg_class cover_class;
|
||||
|
||||
cover_class = REGNO_REG_CLASS (i);
|
||||
if (cover_class == NO_REGS)
|
||||
continue;
|
||||
cover_class = ira_class_translate[cover_class];
|
||||
curr_reg_pressure[cover_class]++;
|
||||
if (curr_bb_node->reg_pressure[cover_class]
|
||||
< curr_reg_pressure[cover_class])
|
||||
curr_bb_node->reg_pressure[cover_class]
|
||||
= curr_reg_pressure[cover_class];
|
||||
ira_assert (curr_reg_pressure[cover_class]
|
||||
<= ira_available_class_regs[cover_class]);
|
||||
}
|
||||
EXECUTE_IF_SET_IN_BITMAP (reg_live_in, FIRST_PSEUDO_REGISTER, j, bi)
|
||||
{
|
||||
ira_allocno_t a = ira_curr_regno_allocno_map[j];
|
||||
|
||||
if (a == NULL)
|
||||
continue;
|
||||
ira_assert (! sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)));
|
||||
set_allocno_live (a);
|
||||
make_regno_born (j);
|
||||
}
|
||||
|
||||
#ifdef EH_RETURN_DATA_REGNO
|
||||
if (bb_has_eh_pred (bb))
|
||||
{
|
||||
for (j = 0; ; ++j)
|
||||
{
|
||||
unsigned int regno = EH_RETURN_DATA_REGNO (j);
|
||||
|
||||
if (regno == INVALID_REGNUM)
|
||||
break;
|
||||
make_regno_born_and_dead (regno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocnos can't go in stack regs at the start of a basic block
|
||||
that is reached by an abnormal edge. Likewise for call
|
||||
clobbered regs, because caller-save, fixup_abnormal_edges and
|
||||
possibly the table driven EH machinery are not quite ready to
|
||||
handle such allocnos live across such edges. */
|
||||
FOR_EACH_EDGE (e, ei, bb->preds)
|
||||
if (e->flags & EDGE_ABNORMAL)
|
||||
break;
|
||||
|
||||
if (e != NULL)
|
||||
{
|
||||
#ifdef STACK_REGS
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
|
||||
{
|
||||
ALLOCNO_NO_STACK_REG_P (ira_allocnos[px]) = true;
|
||||
ALLOCNO_TOTAL_NO_STACK_REG_P (ira_allocnos[px]) = true;
|
||||
}
|
||||
for (px = FIRST_STACK_REG; px <= LAST_STACK_REG; px++)
|
||||
make_regno_born_and_dead (px);
|
||||
#endif
|
||||
/* No need to record conflicts for call clobbered regs if we
|
||||
have nonlocal labels around, as we don't ever try to
|
||||
allocate such regs in this case. */
|
||||
if (!cfun->has_nonlocal_label)
|
||||
for (px = 0; px < FIRST_PSEUDO_REGISTER; px++)
|
||||
if (call_used_regs[px])
|
||||
make_regno_born_and_dead (px);
|
||||
}
|
||||
|
||||
/* Scan the code of this basic block, noting which allocnos and
|
||||
hard regs are born or die. */
|
||||
FOR_BB_INSNS (bb, insn)
|
||||
{
|
||||
rtx link;
|
||||
int freq;
|
||||
|
||||
if (! INSN_P (insn))
|
||||
continue;
|
||||
|
||||
freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
|
||||
if (freq == 0)
|
||||
freq = 1;
|
||||
|
||||
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
|
||||
fprintf (ira_dump_file, " Insn %u(l%d): point = %d\n",
|
||||
INSN_UID (insn), loop_tree_node->parent->loop->num,
|
||||
curr_point);
|
||||
|
||||
/* Check regs_set is an empty set. */
|
||||
gcc_assert (VEC_empty (rtx, regs_set));
|
||||
|
||||
/* Mark any allocnos clobbered by INSN as live, so they
|
||||
conflict with the inputs. */
|
||||
note_stores (PATTERN (insn), mark_reg_clobber, NULL);
|
||||
|
||||
extract_insn (insn);
|
||||
process_single_reg_class_operands (true, freq);
|
||||
|
||||
/* Mark any allocnos dead after INSN as dead now. */
|
||||
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
|
||||
if (REG_NOTE_KIND (link) == REG_DEAD)
|
||||
mark_reg_death (XEXP (link, 0));
|
||||
|
||||
curr_point++;
|
||||
|
||||
if (CALL_P (insn))
|
||||
{
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
|
||||
{
|
||||
ira_allocno_t a = ira_allocnos[i];
|
||||
|
||||
ALLOCNO_CALL_FREQ (a) += freq;
|
||||
ALLOCNO_CALLS_CROSSED_NUM (a)++;
|
||||
/* Don't allocate allocnos that cross calls, if this
|
||||
function receives a nonlocal goto. */
|
||||
if (cfun->has_nonlocal_label)
|
||||
{
|
||||
SET_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a));
|
||||
SET_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark any allocnos set in INSN as live. Clobbers are
|
||||
processed again, so they will conflict with the reg
|
||||
allocnos that are set. */
|
||||
note_stores (PATTERN (insn), mark_reg_store, NULL);
|
||||
|
||||
#ifdef AUTO_INC_DEC
|
||||
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
|
||||
if (REG_NOTE_KIND (link) == REG_INC)
|
||||
mark_reg_store (XEXP (link, 0), NULL_RTX, NULL);
|
||||
#endif
|
||||
|
||||
/* If INSN has multiple outputs, then any allocno that dies
|
||||
here and is used inside of an output must conflict with
|
||||
the other outputs.
|
||||
|
||||
It is unsafe to use !single_set here since it will ignore
|
||||
an unused output. Just because an output is unused does
|
||||
not mean the compiler can assume the side effect will not
|
||||
occur. Consider if ALLOCNO appears in the address of an
|
||||
output and we reload the output. If we allocate ALLOCNO
|
||||
to the same hard register as an unused output we could
|
||||
set the hard register before the output reload insn. */
|
||||
if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn))
|
||||
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
|
||||
if (REG_NOTE_KIND (link) == REG_DEAD)
|
||||
{
|
||||
int i;
|
||||
int used_in_output = 0;
|
||||
rtx reg = XEXP (link, 0);
|
||||
|
||||
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
|
||||
{
|
||||
rtx set = XVECEXP (PATTERN (insn), 0, i);
|
||||
|
||||
if (GET_CODE (set) == SET
|
||||
&& ! REG_P (SET_DEST (set))
|
||||
&& ! rtx_equal_p (reg, SET_DEST (set))
|
||||
&& reg_overlap_mentioned_p (reg, SET_DEST (set)))
|
||||
used_in_output = 1;
|
||||
}
|
||||
if (used_in_output)
|
||||
mark_reg_conflicts (reg);
|
||||
}
|
||||
|
||||
process_single_reg_class_operands (false, freq);
|
||||
|
||||
/* Mark any allocnos set in INSN and then never used. */
|
||||
while (! VEC_empty (rtx, regs_set))
|
||||
{
|
||||
rtx reg = VEC_pop (rtx, regs_set);
|
||||
rtx note = find_regno_note (insn, REG_UNUSED, REGNO (reg));
|
||||
|
||||
if (note)
|
||||
mark_reg_death (XEXP (note, 0));
|
||||
}
|
||||
curr_point++;
|
||||
}
|
||||
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
|
||||
{
|
||||
make_regno_dead (ALLOCNO_REGNO (ira_allocnos[i]));
|
||||
}
|
||||
|
||||
curr_point++;
|
||||
|
||||
}
|
||||
/* Propagate register pressure to upper loop tree nodes: */
|
||||
if (loop_tree_node != ira_loop_tree_root)
|
||||
for (i = 0; i < ira_reg_class_cover_size; i++)
|
||||
{
|
||||
enum reg_class cover_class;
|
||||
|
||||
cover_class = ira_reg_class_cover[i];
|
||||
if (loop_tree_node->reg_pressure[cover_class]
|
||||
> loop_tree_node->parent->reg_pressure[cover_class])
|
||||
loop_tree_node->parent->reg_pressure[cover_class]
|
||||
= loop_tree_node->reg_pressure[cover_class];
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and set up IRA_START_POINT_RANGES and
|
||||
IRA_FINISH_POINT_RANGES. */
|
||||
static void
|
||||
create_start_finish_chains (void)
|
||||
{
|
||||
ira_allocno_t a;
|
||||
ira_allocno_iterator ai;
|
||||
allocno_live_range_t r;
|
||||
|
||||
ira_start_point_ranges
|
||||
= (allocno_live_range_t *) ira_allocate (ira_max_point
|
||||
* sizeof (allocno_live_range_t));
|
||||
memset (ira_start_point_ranges, 0,
|
||||
ira_max_point * sizeof (allocno_live_range_t));
|
||||
ira_finish_point_ranges
|
||||
= (allocno_live_range_t *) ira_allocate (ira_max_point
|
||||
* sizeof (allocno_live_range_t));
|
||||
memset (ira_finish_point_ranges, 0,
|
||||
ira_max_point * sizeof (allocno_live_range_t));
|
||||
FOR_EACH_ALLOCNO (a, ai)
|
||||
{
|
||||
for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
|
||||
{
|
||||
r->start_next = ira_start_point_ranges[r->start];
|
||||
ira_start_point_ranges[r->start] = r;
|
||||
r->finish_next = ira_finish_point_ranges[r->finish];
|
||||
ira_finish_point_ranges[r->finish] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Rebuild IRA_START_POINT_RANGES and IRA_FINISH_POINT_RANGES after
|
||||
new live ranges and program points were added as a result if new
|
||||
insn generation. */
|
||||
void
|
||||
ira_rebuild_start_finish_chains (void)
|
||||
{
|
||||
ira_free (ira_finish_point_ranges);
|
||||
ira_free (ira_start_point_ranges);
|
||||
create_start_finish_chains ();
|
||||
}
|
||||
|
||||
/* Print live ranges R to file F. */
|
||||
void
|
||||
ira_print_live_range_list (FILE *f, allocno_live_range_t r)
|
||||
{
|
||||
for (; r != NULL; r = r->next)
|
||||
fprintf (f, " [%d..%d]", r->start, r->finish);
|
||||
fprintf (f, "\n");
|
||||
}
|
||||
|
||||
/* Print live ranges R to stderr. */
|
||||
void
|
||||
ira_debug_live_range_list (allocno_live_range_t r)
|
||||
{
|
||||
ira_print_live_range_list (stderr, r);
|
||||
}
|
||||
|
||||
/* Print live ranges of allocno A to file F. */
|
||||
static void
|
||||
print_allocno_live_ranges (FILE *f, ira_allocno_t a)
|
||||
{
|
||||
fprintf (f, " a%d(r%d):", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
|
||||
ira_print_live_range_list (f, ALLOCNO_LIVE_RANGES (a));
|
||||
}
|
||||
|
||||
/* Print live ranges of allocno A to stderr. */
|
||||
void
|
||||
ira_debug_allocno_live_ranges (ira_allocno_t a)
|
||||
{
|
||||
print_allocno_live_ranges (stderr, a);
|
||||
}
|
||||
|
||||
/* Print live ranges of all allocnos to file F. */
|
||||
static void
|
||||
print_live_ranges (FILE *f)
|
||||
{
|
||||
ira_allocno_t a;
|
||||
ira_allocno_iterator ai;
|
||||
|
||||
FOR_EACH_ALLOCNO (a, ai)
|
||||
print_allocno_live_ranges (f, a);
|
||||
}
|
||||
|
||||
/* Print live ranges of all allocnos to stderr. */
|
||||
void
|
||||
ira_debug_live_ranges (void)
|
||||
{
|
||||
print_live_ranges (stderr);
|
||||
}
|
||||
|
||||
/* The main entry function creates live ranges, set up
|
||||
CONFLICT_HARD_REGS and TOTAL_CONFLICT_HARD_REGS for allocnos, and
|
||||
calculate register pressure info. */
|
||||
void
|
||||
ira_create_allocno_live_ranges (void)
|
||||
{
|
||||
allocnos_live = sparseset_alloc (ira_allocnos_num);
|
||||
/* Make a vector that mark_reg_{store,clobber} will store in. */
|
||||
if (!regs_set)
|
||||
regs_set = VEC_alloc (rtx, heap, 10);
|
||||
curr_point = 0;
|
||||
ira_traverse_loop_tree (true, ira_loop_tree_root, NULL,
|
||||
process_bb_node_lives);
|
||||
ira_max_point = curr_point;
|
||||
create_start_finish_chains ();
|
||||
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
|
||||
print_live_ranges (ira_dump_file);
|
||||
/* Clean up. */
|
||||
sparseset_free (allocnos_live);
|
||||
}
|
||||
|
||||
/* Free arrays IRA_START_POINT_RANGES and IRA_FINISH_POINT_RANGES. */
|
||||
void
|
||||
ira_finish_allocno_live_ranges (void)
|
||||
{
|
||||
ira_free (ira_finish_point_ranges);
|
||||
ira_free (ira_start_point_ranges);
|
||||
}
|
37
gcc/ira.h
Normal file
37
gcc/ira.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* Communication between the Integrated Register Allocator (IRA) and
|
||||
the rest of the compiler.
|
||||
Copyright (C) 2006, 2007, 2008
|
||||
Free Software Foundation, Inc.
|
||||
Contributed by Vladimir Makarov <vmakarov@redhat.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/>. */
|
||||
|
||||
extern void ira_init_once (void);
|
||||
extern void ira_init (void);
|
||||
extern void ira_finish_once (void);
|
||||
extern rtx ira_eliminate_regs (rtx, enum machine_mode);
|
||||
extern void ira_sort_insn_chain (bool);
|
||||
|
||||
extern void ira_sort_regnos_for_alter_reg (int *, int, unsigned int *);
|
||||
extern void ira_mark_allocation_change (int);
|
||||
extern void ira_mark_memory_move_deletion (int, int);
|
||||
extern bool ira_reassign_pseudos (int *, int, HARD_REG_SET, HARD_REG_SET *,
|
||||
HARD_REG_SET *, bitmap);
|
||||
extern rtx ira_reuse_stack_slot (int, unsigned int, unsigned int);
|
||||
extern void ira_mark_new_stack_slot (rtx, int, unsigned int);
|
||||
extern bool ira_better_spill_reload_regno_p (int *, int *, rtx, rtx, rtx);
|
||||
|
|
@ -298,7 +298,6 @@ static int equiv_init_movable_p (rtx, int);
|
|||
static int contains_replace_regs (rtx);
|
||||
static int memref_referenced_p (rtx, rtx);
|
||||
static int memref_used_between_p (rtx, rtx, rtx);
|
||||
static void update_equiv_regs (void);
|
||||
static void no_equiv (rtx, const_rtx, void *);
|
||||
static void block_alloc (int);
|
||||
static int qty_sugg_compare (int, int);
|
||||
|
@ -795,9 +794,11 @@ memref_used_between_p (rtx memref, rtx start, rtx end)
|
|||
into the using insn. If it succeeds, we can eliminate the register
|
||||
completely.
|
||||
|
||||
Initialize the REG_EQUIV_INIT array of initializing insns. */
|
||||
Initialize the REG_EQUIV_INIT array of initializing insns.
|
||||
|
||||
static void
|
||||
Return non-zero if jump label rebuilding should be done. */
|
||||
|
||||
int
|
||||
update_equiv_regs (void)
|
||||
{
|
||||
rtx insn;
|
||||
|
@ -1183,6 +1184,8 @@ update_equiv_regs (void)
|
|||
new_insn = emit_insn_before (PATTERN (equiv_insn), insn);
|
||||
REG_NOTES (new_insn) = REG_NOTES (equiv_insn);
|
||||
REG_NOTES (equiv_insn) = 0;
|
||||
/* Rescan it to process the notes. */
|
||||
df_insn_rescan (new_insn);
|
||||
|
||||
/* Make sure this insn is recognized before
|
||||
reload begins, otherwise
|
||||
|
@ -1227,6 +1230,7 @@ update_equiv_regs (void)
|
|||
|
||||
end_alias_analysis ();
|
||||
free (reg_equiv);
|
||||
return recorded_label_ref;
|
||||
}
|
||||
|
||||
/* Mark REG as having no known equivalence.
|
||||
|
@ -2442,6 +2446,12 @@ find_stack_regs (void)
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
gate_handle_local_alloc (void)
|
||||
{
|
||||
return ! flag_ira;
|
||||
}
|
||||
|
||||
/* Run old register allocator. Return TRUE if we must exit
|
||||
rest_of_compilation upon return. */
|
||||
static unsigned int
|
||||
|
@ -2517,7 +2527,7 @@ struct rtl_opt_pass pass_local_alloc =
|
|||
{
|
||||
RTL_PASS,
|
||||
"lreg", /* name */
|
||||
NULL, /* gate */
|
||||
gate_handle_local_alloc, /* gate */
|
||||
rest_of_handle_local_alloc, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
|
|
28
gcc/opts.c
28
gcc/opts.c
|
@ -878,6 +878,11 @@ decode_options (unsigned int argc, const char **argv)
|
|||
flag_section_anchors = 0;
|
||||
}
|
||||
|
||||
#ifdef IRA_COVER_CLASSES
|
||||
/* Use IRA if it is implemented for the target. */
|
||||
flag_ira = 1;
|
||||
#endif
|
||||
|
||||
/* Originally we just set the variables if a particular optimization level,
|
||||
but with the advent of being able to change the optimization level for a
|
||||
function, we need to reset optimizations. */
|
||||
|
@ -1119,6 +1124,14 @@ decode_options (unsigned int argc, const char **argv)
|
|||
flag_reorder_blocks = 1;
|
||||
}
|
||||
|
||||
#ifndef IRA_COVER_CLASSES
|
||||
if (flag_ira)
|
||||
{
|
||||
inform ("-fira does not work on this architecture");
|
||||
flag_ira = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Save the current optimization options if this is the first call. */
|
||||
if (first_time_p)
|
||||
{
|
||||
|
@ -1970,6 +1983,21 @@ common_handle_option (size_t scode, const char *arg, int value,
|
|||
warning (0, "unknown tls-model \"%s\"", arg);
|
||||
break;
|
||||
|
||||
case OPT_fira_algorithm_:
|
||||
if (!strcmp (arg, "regional"))
|
||||
flag_ira_algorithm = IRA_ALGORITHM_REGIONAL;
|
||||
else if (!strcmp (arg, "CB"))
|
||||
flag_ira_algorithm = IRA_ALGORITHM_CB;
|
||||
else if (!strcmp (arg, "mixed"))
|
||||
flag_ira_algorithm = IRA_ALGORITHM_MIXED;
|
||||
else
|
||||
warning (0, "unknown ira algorithm \"%s\"", arg);
|
||||
break;
|
||||
|
||||
case OPT_fira_verbose_:
|
||||
flag_ira_verbose = value;
|
||||
break;
|
||||
|
||||
case OPT_ftracer:
|
||||
flag_tracer_set = true;
|
||||
break;
|
||||
|
|
|
@ -714,6 +714,11 @@ DEFPARAM (PARAM_DF_DOUBLE_QUEUE_THRESHOLD_FACTOR,
|
|||
"Multiplier used for determining the double-queueing threshold",
|
||||
2, 0, 0)
|
||||
|
||||
DEFPARAM (PARAM_IRA_MAX_LOOPS_NUM,
|
||||
"ira-max-loops-num",
|
||||
"max loops number for regional RA",
|
||||
50, 0, 0)
|
||||
|
||||
/* Switch initialization conversion will refuse to create arrays that are
|
||||
bigger than this parameter times the number of switch branches. */
|
||||
|
||||
|
|
|
@ -167,6 +167,8 @@ typedef enum compiler_param
|
|||
PARAM_VALUE (PARAM_L2_CACHE_SIZE)
|
||||
#define USE_CANONICAL_TYPES \
|
||||
PARAM_VALUE (PARAM_USE_CANONICAL_TYPES)
|
||||
#define IRA_MAX_LOOPS_NUM \
|
||||
PARAM_VALUE (PARAM_IRA_MAX_LOOPS_NUM)
|
||||
#define SWITCH_CONVERSION_BRANCH_RATIO \
|
||||
PARAM_VALUE (PARAM_SWITCH_CONVERSION_BRANCH_RATIO)
|
||||
#endif /* ! GCC_PARAMS_H */
|
||||
|
|
|
@ -767,6 +767,7 @@ init_optimization_passes (void)
|
|||
NEXT_PASS (pass_subregs_of_mode_init);
|
||||
NEXT_PASS (pass_local_alloc);
|
||||
NEXT_PASS (pass_global_alloc);
|
||||
NEXT_PASS (pass_ira);
|
||||
NEXT_PASS (pass_subregs_of_mode_finish);
|
||||
NEXT_PASS (pass_postreload);
|
||||
{
|
||||
|
|
|
@ -1565,7 +1565,7 @@ move2add_note_store (rtx dst, const_rtx set, void *data ATTRIBUTE_UNUSED)
|
|||
static bool
|
||||
gate_handle_postreload (void)
|
||||
{
|
||||
return (optimize > 0);
|
||||
return (optimize > 0 && reload_completed);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ static enum reg_class reg_class_superclasses[N_REG_CLASSES][N_REG_CLASSES];
|
|||
|
||||
/* For each reg class, table listing all the classes contained in it. */
|
||||
|
||||
static enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
|
||||
enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
|
||||
|
||||
/* For each pair of reg classes,
|
||||
a largest reg class contained in their union. */
|
||||
|
@ -211,24 +211,22 @@ bool have_regs_of_mode [MAX_MACHINE_MODE];
|
|||
|
||||
/* 1 if class does contain register of given mode. */
|
||||
|
||||
static char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
|
||||
|
||||
typedef unsigned short move_table[N_REG_CLASSES];
|
||||
char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
|
||||
|
||||
/* Maximum cost of moving from a register in one class to a register in
|
||||
another class. Based on REGISTER_MOVE_COST. */
|
||||
|
||||
static move_table *move_cost[MAX_MACHINE_MODE];
|
||||
move_table *move_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Similar, but here we don't have to move if the first index is a subset
|
||||
of the second so in that case the cost is zero. */
|
||||
|
||||
static move_table *may_move_in_cost[MAX_MACHINE_MODE];
|
||||
move_table *may_move_in_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Similar, but here we don't have to move if the first index is a superset
|
||||
of the second so in that case the cost is zero. */
|
||||
|
||||
static move_table *may_move_out_cost[MAX_MACHINE_MODE];
|
||||
move_table *may_move_out_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Keep track of the last mode we initialized move costs for. */
|
||||
static int last_mode_for_init_move_cost;
|
||||
|
@ -313,7 +311,7 @@ init_reg_sets (void)
|
|||
|
||||
/* Initialize may_move_cost and friends for mode M. */
|
||||
|
||||
static void
|
||||
void
|
||||
init_move_cost (enum machine_mode m)
|
||||
{
|
||||
static unsigned short last_move_cost[N_REG_CLASSES][N_REG_CLASSES];
|
||||
|
@ -1024,6 +1022,7 @@ reg_preferred_class (int regno)
|
|||
{
|
||||
if (reg_pref == 0)
|
||||
return GENERAL_REGS;
|
||||
|
||||
return (enum reg_class) reg_pref[regno].prefclass;
|
||||
}
|
||||
|
||||
|
@ -2283,6 +2282,32 @@ auto_inc_dec_reg_p (rtx reg, enum machine_mode mode)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Allocate space for reg info. */
|
||||
void
|
||||
allocate_reg_info (void)
|
||||
{
|
||||
int size = max_reg_num ();
|
||||
|
||||
gcc_assert (! reg_pref && ! reg_renumber);
|
||||
reg_renumber = XNEWVEC (short, size);
|
||||
reg_pref = XCNEWVEC (struct reg_pref, size);
|
||||
memset (reg_renumber, -1, size * sizeof (short));
|
||||
}
|
||||
|
||||
|
||||
/* Resize reg info. The new elements will be uninitialized. */
|
||||
void
|
||||
resize_reg_info (void)
|
||||
{
|
||||
int size = max_reg_num ();
|
||||
|
||||
gcc_assert (reg_pref && reg_renumber);
|
||||
reg_renumber = XRESIZEVEC (short, reg_renumber, size);
|
||||
reg_pref = XRESIZEVEC (struct reg_pref, reg_pref, size);
|
||||
}
|
||||
|
||||
|
||||
/* Free up the space allocated by allocate_reg_info. */
|
||||
void
|
||||
free_reg_info (void)
|
||||
|
@ -2300,6 +2325,21 @@ free_reg_info (void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Set up preferred and alternate classes for REGNO as PREFCLASS and
|
||||
ALTCLASS. */
|
||||
void
|
||||
setup_reg_classes (int regno,
|
||||
enum reg_class prefclass, enum reg_class altclass)
|
||||
{
|
||||
if (reg_pref == NULL)
|
||||
return;
|
||||
reg_pref[regno].prefclass = prefclass;
|
||||
reg_pref[regno].altclass = altclass;
|
||||
}
|
||||
|
||||
|
||||
/* This is the `regscan' pass of the compiler, run just before cse and
|
||||
again just before loop. It finds the first and last use of each
|
||||
|
|
|
@ -1117,7 +1117,8 @@ regmove_optimize (rtx f, int nregs)
|
|||
|
||||
for (pass = 0; pass <= 2; pass++)
|
||||
{
|
||||
if (! flag_regmove && pass >= flag_expensive_optimizations)
|
||||
/* We need fewer optimizations for IRA. */
|
||||
if ((! flag_regmove || flag_ira) && pass >= flag_expensive_optimizations)
|
||||
goto done;
|
||||
|
||||
if (dump_file)
|
||||
|
@ -1165,7 +1166,9 @@ regmove_optimize (rtx f, int nregs)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (! flag_regmove)
|
||||
|
||||
/* All optimizations important for IRA have been done. */
|
||||
if (! flag_regmove || flag_ira)
|
||||
continue;
|
||||
|
||||
if (! find_matches (insn, &match))
|
||||
|
|
18
gcc/regs.h
18
gcc/regs.h
|
@ -262,9 +262,27 @@ extern int caller_save_needed;
|
|||
#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) 0
|
||||
#endif
|
||||
|
||||
/* 1 if the corresponding class does contain register of given
|
||||
mode. */
|
||||
extern char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
|
||||
|
||||
typedef unsigned short move_table[N_REG_CLASSES];
|
||||
|
||||
/* Maximum cost of moving from a register in one class to a register
|
||||
in another class. */
|
||||
extern move_table *move_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Specify number of hard registers given machine mode occupy. */
|
||||
extern unsigned char hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
|
||||
|
||||
/* Similar, but here we don't have to move if the first index is a
|
||||
subset of the second so in that case the cost is zero. */
|
||||
extern move_table *may_move_in_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Similar, but here we don't have to move if the first index is a
|
||||
superset of the second so in that case the cost is zero. */
|
||||
extern move_table *may_move_out_cost[MAX_MACHINE_MODE];
|
||||
|
||||
/* Return an exclusive upper bound on the registers occupied by hard
|
||||
register (reg:MODE REGNO). */
|
||||
|
||||
|
|
10
gcc/reload.c
10
gcc/reload.c
|
@ -1549,8 +1549,10 @@ push_reload (rtx in, rtx out, rtx *inloc, rtx *outloc,
|
|||
&& reg_mentioned_p (XEXP (note, 0), in)
|
||||
/* Check that a former pseudo is valid; see find_dummy_reload. */
|
||||
&& (ORIGINAL_REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER
|
||||
|| (!bitmap_bit_p (DF_LIVE_OUT (ENTRY_BLOCK_PTR),
|
||||
ORIGINAL_REGNO (XEXP (note, 0)))
|
||||
|| (! bitmap_bit_p (flag_ira
|
||||
? DF_LR_OUT (ENTRY_BLOCK_PTR)
|
||||
: DF_LIVE_OUT (ENTRY_BLOCK_PTR),
|
||||
ORIGINAL_REGNO (XEXP (note, 0)))
|
||||
&& hard_regno_nregs[regno][GET_MODE (XEXP (note, 0))] == 1))
|
||||
&& ! refers_to_regno_for_reload_p (regno,
|
||||
end_hard_regno (rel_mode,
|
||||
|
@ -2027,7 +2029,9 @@ find_dummy_reload (rtx real_in, rtx real_out, rtx *inloc, rtx *outloc,
|
|||
can ignore the conflict). We must never introduce writes
|
||||
to such hardregs, as they would clobber the other live
|
||||
pseudo. See PR 20973. */
|
||||
|| (!bitmap_bit_p (DF_LIVE_OUT (ENTRY_BLOCK_PTR),
|
||||
|| (!bitmap_bit_p (flag_ira
|
||||
? DF_LR_OUT (ENTRY_BLOCK_PTR)
|
||||
: DF_LIVE_OUT (ENTRY_BLOCK_PTR),
|
||||
ORIGINAL_REGNO (in))
|
||||
/* Similarly, only do this if we can be sure that the death
|
||||
note is still valid. global can assign some hardreg to
|
||||
|
|
11
gcc/reload.h
11
gcc/reload.h
|
@ -1,6 +1,6 @@
|
|||
/* Communication between reload.c and reload1.c.
|
||||
Copyright (C) 1987, 1991, 1992, 1993, 1994, 1995, 1997, 1998,
|
||||
1999, 2000, 2001, 2003, 2004, 2007 Free Software Foundation, Inc.
|
||||
/* Communication between reload.c, reload1.c and the rest of compiler.
|
||||
Copyright (C) 1987, 1991, 1992, 1993, 1994, 1995, 1997, 1998, 1999,
|
||||
2000, 2001, 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
|
@ -209,8 +209,9 @@ struct insn_chain
|
|||
int block;
|
||||
/* The rtx of the insn. */
|
||||
rtx insn;
|
||||
/* Register life information: record all live hard registers, and all
|
||||
live pseudos that have a hard register. */
|
||||
/* Register life information: record all live hard registers, and
|
||||
all live pseudos that have a hard register. This set also
|
||||
contains pseudos spilled by IRA. */
|
||||
regset_head live_throughout;
|
||||
regset_head dead_or_set;
|
||||
|
||||
|
|
430
gcc/reload1.c
430
gcc/reload1.c
|
@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "toplev.h"
|
||||
#include "except.h"
|
||||
#include "tree.h"
|
||||
#include "ira.h"
|
||||
#include "df.h"
|
||||
#include "target.h"
|
||||
#include "dse.h"
|
||||
|
@ -257,6 +258,9 @@ static unsigned int spill_stack_slot_width[FIRST_PSEUDO_REGISTER];
|
|||
/* Record which pseudos needed to be spilled. */
|
||||
static regset_head spilled_pseudos;
|
||||
|
||||
/* Record which pseudos changed their allocation in finish_spills. */
|
||||
static regset_head changed_allocation_pseudos;
|
||||
|
||||
/* Used for communication between order_regs_for_reload and count_pseudo.
|
||||
Used to avoid counting one pseudo twice. */
|
||||
static regset_head pseudos_counted;
|
||||
|
@ -389,7 +393,7 @@ static void delete_caller_save_insns (void);
|
|||
static void spill_failure (rtx, enum reg_class);
|
||||
static void count_spilled_pseudo (int, int, int);
|
||||
static void delete_dead_insn (rtx);
|
||||
static void alter_reg (int, int);
|
||||
static void alter_reg (int, int, bool);
|
||||
static void set_label_offsets (rtx, rtx, int);
|
||||
static void check_eliminable_occurrences (rtx);
|
||||
static void elimination_effects (rtx, enum machine_mode);
|
||||
|
@ -443,6 +447,8 @@ static rtx inc_for_reload (rtx, rtx, rtx, int);
|
|||
static void add_auto_inc_notes (rtx, rtx);
|
||||
#endif
|
||||
static void copy_eh_notes (rtx, rtx);
|
||||
static void substitute (rtx *, const_rtx, rtx);
|
||||
static bool gen_reload_chain_without_interm_reg_p (int, int);
|
||||
static int reloads_conflict (int, int);
|
||||
static rtx gen_reload (rtx, rtx, int, enum reload_type);
|
||||
static rtx emit_insn_if_valid_for_reload (rtx);
|
||||
|
@ -501,6 +507,7 @@ init_reload (void)
|
|||
reload_startobj = XOBNEWVAR (&reload_obstack, char, 0);
|
||||
|
||||
INIT_REG_SET (&spilled_pseudos);
|
||||
INIT_REG_SET (&changed_allocation_pseudos);
|
||||
INIT_REG_SET (&pseudos_counted);
|
||||
}
|
||||
|
||||
|
@ -546,11 +553,11 @@ compute_use_by_pseudos (HARD_REG_SET *to, regset from)
|
|||
|
||||
if (r < 0)
|
||||
{
|
||||
/* reload_combine uses the information from
|
||||
DF_LIVE_IN (BASIC_BLOCK), which might still
|
||||
contain registers that have not actually been allocated
|
||||
since they have an equivalence. */
|
||||
gcc_assert (reload_completed);
|
||||
/* reload_combine uses the information from DF_LIVE_IN,
|
||||
which might still contain registers that have not
|
||||
actually been allocated since they have an
|
||||
equivalence. */
|
||||
gcc_assert ((flag_ira && optimize) || reload_completed);
|
||||
}
|
||||
else
|
||||
add_to_hard_reg_set (to, PSEUDO_REGNO_MODE (regno), r);
|
||||
|
@ -684,6 +691,9 @@ static int something_needs_operands_changed;
|
|||
/* Nonzero means we couldn't get enough spill regs. */
|
||||
static int failure;
|
||||
|
||||
/* Temporary array of pseudo-register number. */
|
||||
static int *temp_pseudo_reg_arr;
|
||||
|
||||
/* Main entry point for the reload pass.
|
||||
|
||||
FIRST is the first insn of the function being compiled.
|
||||
|
@ -700,7 +710,7 @@ static int failure;
|
|||
int
|
||||
reload (rtx first, int global)
|
||||
{
|
||||
int i;
|
||||
int i, n;
|
||||
rtx insn;
|
||||
struct elim_table *ep;
|
||||
basic_block bb;
|
||||
|
@ -883,12 +893,21 @@ reload (rtx first, int global)
|
|||
offsets_known_at = XNEWVEC (char, num_labels);
|
||||
offsets_at = (HOST_WIDE_INT (*)[NUM_ELIMINABLE_REGS]) xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
|
||||
|
||||
/* Alter each pseudo-reg rtx to contain its hard reg number.
|
||||
Assign stack slots to the pseudos that lack hard regs or equivalents.
|
||||
/* Alter each pseudo-reg rtx to contain its hard reg number. Assign
|
||||
stack slots to the pseudos that lack hard regs or equivalents.
|
||||
Do not touch virtual registers. */
|
||||
|
||||
for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
|
||||
alter_reg (i, -1);
|
||||
temp_pseudo_reg_arr = XNEWVEC (int, max_regno - LAST_VIRTUAL_REGISTER - 1);
|
||||
for (n = 0, i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
|
||||
temp_pseudo_reg_arr[n++] = i;
|
||||
|
||||
if (flag_ira && optimize)
|
||||
/* Ask IRA to order pseudo-registers for better stack slot
|
||||
sharing. */
|
||||
ira_sort_regnos_for_alter_reg (temp_pseudo_reg_arr, n, reg_max_ref_width);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
alter_reg (temp_pseudo_reg_arr[i], -1, false);
|
||||
|
||||
/* If we have some registers we think can be eliminated, scan all insns to
|
||||
see if there is an insn that sets one of these registers to something
|
||||
|
@ -1002,7 +1021,7 @@ reload (rtx first, int global)
|
|||
the loop. */
|
||||
reg_equiv_memory_loc[i] = 0;
|
||||
reg_equiv_init[i] = 0;
|
||||
alter_reg (i, -1);
|
||||
alter_reg (i, -1, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1055,12 @@ reload (rtx first, int global)
|
|||
|
||||
calculate_needs_all_insns (global);
|
||||
|
||||
CLEAR_REG_SET (&spilled_pseudos);
|
||||
if (! flag_ira || ! optimize)
|
||||
/* Don't do it for IRA. We need this info because we don't
|
||||
change live_throughout and dead_or_set for chains when IRA
|
||||
is used. */
|
||||
CLEAR_REG_SET (&spilled_pseudos);
|
||||
|
||||
did_spill = 0;
|
||||
|
||||
something_changed = 0;
|
||||
|
@ -1094,6 +1118,11 @@ reload (rtx first, int global)
|
|||
obstack_free (&reload_obstack, reload_firstobj);
|
||||
}
|
||||
|
||||
if (flag_ira && optimize)
|
||||
/* Restore the original insn chain order for correct reload work
|
||||
(e.g. for correct inheritance). */
|
||||
ira_sort_insn_chain (false);
|
||||
|
||||
/* If global-alloc was run, notify it of any register eliminations we have
|
||||
done. */
|
||||
if (global)
|
||||
|
@ -1163,6 +1192,7 @@ reload (rtx first, int global)
|
|||
regs. */
|
||||
failed:
|
||||
|
||||
CLEAR_REG_SET (&changed_allocation_pseudos);
|
||||
CLEAR_REG_SET (&spilled_pseudos);
|
||||
reload_in_progress = 0;
|
||||
|
||||
|
@ -1333,6 +1363,8 @@ reload (rtx first, int global)
|
|||
VEC_free (rtx, gc, reg_equiv_memory_loc_vec);
|
||||
reg_equiv_memory_loc = 0;
|
||||
|
||||
free (temp_pseudo_reg_arr);
|
||||
|
||||
if (offsets_known_at)
|
||||
free (offsets_known_at);
|
||||
if (offsets_at)
|
||||
|
@ -1573,10 +1605,24 @@ calculate_needs_all_insns (int global)
|
|||
{
|
||||
rtx set = single_set (insn);
|
||||
if (set
|
||||
&& SET_SRC (set) == SET_DEST (set)
|
||||
&& REG_P (SET_SRC (set))
|
||||
&& REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER)
|
||||
&&
|
||||
((SET_SRC (set) == SET_DEST (set)
|
||||
&& REG_P (SET_SRC (set))
|
||||
&& REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER)
|
||||
|| (REG_P (SET_SRC (set)) && REG_P (SET_DEST (set))
|
||||
&& reg_renumber[REGNO (SET_SRC (set))] < 0
|
||||
&& reg_renumber[REGNO (SET_DEST (set))] < 0
|
||||
&& reg_equiv_memory_loc[REGNO (SET_SRC (set))] != NULL
|
||||
&& reg_equiv_memory_loc[REGNO (SET_DEST (set))] != NULL
|
||||
&& rtx_equal_p (reg_equiv_memory_loc
|
||||
[REGNO (SET_SRC (set))],
|
||||
reg_equiv_memory_loc
|
||||
[REGNO (SET_DEST (set))]))))
|
||||
{
|
||||
if (flag_ira && optimize)
|
||||
/* Inform IRA about the insn deletion. */
|
||||
ira_mark_memory_move_deletion (REGNO (SET_DEST (set)),
|
||||
REGNO (SET_SRC (set)));
|
||||
delete_insn (insn);
|
||||
/* Delete it from the reload chain. */
|
||||
if (chain->prev)
|
||||
|
@ -1665,6 +1711,10 @@ static int spill_cost[FIRST_PSEUDO_REGISTER];
|
|||
only the first hard reg for a multi-reg pseudo. */
|
||||
static int spill_add_cost[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* Map of hard regno to pseudo regno currently occupying the hard
|
||||
reg. */
|
||||
static int hard_regno_to_pseudo_regno[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* Update the spill cost arrays, considering that pseudo REG is live. */
|
||||
|
||||
static void
|
||||
|
@ -1675,7 +1725,10 @@ count_pseudo (int reg)
|
|||
int nregs;
|
||||
|
||||
if (REGNO_REG_SET_P (&pseudos_counted, reg)
|
||||
|| REGNO_REG_SET_P (&spilled_pseudos, reg))
|
||||
|| REGNO_REG_SET_P (&spilled_pseudos, reg)
|
||||
/* Ignore spilled pseudo-registers which can be here only if IRA
|
||||
is used. */
|
||||
|| (flag_ira && optimize && r < 0))
|
||||
return;
|
||||
|
||||
SET_REGNO_REG_SET (&pseudos_counted, reg);
|
||||
|
@ -1683,10 +1736,12 @@ count_pseudo (int reg)
|
|||
gcc_assert (r >= 0);
|
||||
|
||||
spill_add_cost[r] += freq;
|
||||
|
||||
nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
|
||||
while (nregs-- > 0)
|
||||
spill_cost[r + nregs] += freq;
|
||||
{
|
||||
hard_regno_to_pseudo_regno[r + nregs] = reg;
|
||||
spill_cost[r + nregs] += freq;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the SPILL_COST and SPILL_ADD_COST arrays and determine the
|
||||
|
@ -1704,6 +1759,8 @@ order_regs_for_reload (struct insn_chain *chain)
|
|||
|
||||
memset (spill_cost, 0, sizeof spill_cost);
|
||||
memset (spill_add_cost, 0, sizeof spill_add_cost);
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
hard_regno_to_pseudo_regno[i] = -1;
|
||||
|
||||
/* Count number of uses of each hard reg by pseudo regs allocated to it
|
||||
and then order them by decreasing use. First exclude hard registers
|
||||
|
@ -1746,18 +1803,25 @@ static HARD_REG_SET used_spill_regs_local;
|
|||
static void
|
||||
count_spilled_pseudo (int spilled, int spilled_nregs, int reg)
|
||||
{
|
||||
int freq = REG_FREQ (reg);
|
||||
int r = reg_renumber[reg];
|
||||
int nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
|
||||
|
||||
if (REGNO_REG_SET_P (&spilled_pseudos, reg)
|
||||
/* Ignore spilled pseudo-registers which can be here only if IRA is
|
||||
used. */
|
||||
if ((flag_ira && optimize && r < 0)
|
||||
|| REGNO_REG_SET_P (&spilled_pseudos, reg)
|
||||
|| spilled + spilled_nregs <= r || r + nregs <= spilled)
|
||||
return;
|
||||
|
||||
SET_REGNO_REG_SET (&spilled_pseudos, reg);
|
||||
|
||||
spill_add_cost[r] -= REG_FREQ (reg);
|
||||
spill_add_cost[r] -= freq;
|
||||
while (nregs-- > 0)
|
||||
spill_cost[r + nregs] -= REG_FREQ (reg);
|
||||
{
|
||||
hard_regno_to_pseudo_regno[r + nregs] = -1;
|
||||
spill_cost[r + nregs] -= freq;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reload register to use for reload number ORDER. */
|
||||
|
@ -1769,11 +1833,13 @@ find_reg (struct insn_chain *chain, int order)
|
|||
struct reload *rl = rld + rnum;
|
||||
int best_cost = INT_MAX;
|
||||
int best_reg = -1;
|
||||
unsigned int i, j;
|
||||
unsigned int i, j, n;
|
||||
int k;
|
||||
HARD_REG_SET not_usable;
|
||||
HARD_REG_SET used_by_other_reload;
|
||||
reg_set_iterator rsi;
|
||||
static int regno_pseudo_regs[FIRST_PSEUDO_REGISTER];
|
||||
static int best_regno_pseudo_regs[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
COPY_HARD_REG_SET (not_usable, bad_spill_regs);
|
||||
IOR_HARD_REG_SET (not_usable, bad_spill_regs_global);
|
||||
|
@ -1791,7 +1857,11 @@ find_reg (struct insn_chain *chain, int order)
|
|||
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
{
|
||||
#ifdef REG_ALLOC_ORDER
|
||||
unsigned int regno = reg_alloc_order[i];
|
||||
#else
|
||||
unsigned int regno = i;
|
||||
#endif
|
||||
|
||||
if (! TEST_HARD_REG_BIT (not_usable, regno)
|
||||
&& ! TEST_HARD_REG_BIT (used_by_other_reload, regno)
|
||||
|
@ -1810,6 +1880,38 @@ find_reg (struct insn_chain *chain, int order)
|
|||
}
|
||||
if (! ok)
|
||||
continue;
|
||||
|
||||
if (flag_ira && optimize)
|
||||
{
|
||||
/* Ask IRA to find a better pseudo-register for
|
||||
spilling. */
|
||||
for (n = j = 0; j < this_nregs; j++)
|
||||
{
|
||||
int r = hard_regno_to_pseudo_regno[regno + j];
|
||||
|
||||
if (r < 0)
|
||||
continue;
|
||||
if (n == 0 || regno_pseudo_regs[n - 1] != r)
|
||||
regno_pseudo_regs[n++] = r;
|
||||
}
|
||||
regno_pseudo_regs[n++] = -1;
|
||||
if (best_reg < 0
|
||||
|| ira_better_spill_reload_regno_p (regno_pseudo_regs,
|
||||
best_regno_pseudo_regs,
|
||||
rl->in, rl->out,
|
||||
chain->insn))
|
||||
{
|
||||
best_reg = regno;
|
||||
for (j = 0;; j++)
|
||||
{
|
||||
best_regno_pseudo_regs[j] = regno_pseudo_regs[j];
|
||||
if (regno_pseudo_regs[j] < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rl->in && REG_P (rl->in) && REGNO (rl->in) == regno)
|
||||
this_cost--;
|
||||
if (rl->out && REG_P (rl->out) && REGNO (rl->out) == regno)
|
||||
|
@ -1857,6 +1959,7 @@ find_reg (struct insn_chain *chain, int order)
|
|||
{
|
||||
gcc_assert (spill_cost[best_reg + i] == 0);
|
||||
gcc_assert (spill_add_cost[best_reg + i] == 0);
|
||||
gcc_assert (hard_regno_to_pseudo_regno[best_reg + i] == -1);
|
||||
SET_HARD_REG_BIT (used_spill_regs_local, best_reg + i);
|
||||
}
|
||||
return 1;
|
||||
|
@ -2026,7 +2129,7 @@ delete_dead_insn (rtx insn)
|
|||
can share one stack slot. */
|
||||
|
||||
static void
|
||||
alter_reg (int i, int from_reg)
|
||||
alter_reg (int i, int from_reg, bool dont_share_p)
|
||||
{
|
||||
/* When outputting an inline function, this can happen
|
||||
for a reg that isn't actually used. */
|
||||
|
@ -2059,7 +2162,15 @@ alter_reg (int i, int from_reg)
|
|||
unsigned int total_size = MAX (inherent_size, reg_max_ref_width[i]);
|
||||
unsigned int min_align = reg_max_ref_width[i] * BITS_PER_UNIT;
|
||||
int adjust = 0;
|
||||
bool shared_p = false;
|
||||
|
||||
if (flag_ira && optimize)
|
||||
/* Mark the spill for IRA. */
|
||||
SET_REGNO_REG_SET (&spilled_pseudos, i);
|
||||
x = (dont_share_p || ! flag_ira || ! optimize
|
||||
? NULL_RTX : ira_reuse_stack_slot (i, inherent_size, total_size));
|
||||
if (x)
|
||||
shared_p = true;
|
||||
/* Each pseudo reg has an inherent size which comes from its own mode,
|
||||
and a total size which provides room for paradoxical subregs
|
||||
which refer to the pseudo reg in wider modes.
|
||||
|
@ -2068,7 +2179,7 @@ alter_reg (int i, int from_reg)
|
|||
enough inherent space and enough total space.
|
||||
Otherwise, we allocate a new slot, making sure that it has no less
|
||||
inherent space, and no less total space, then the previous slot. */
|
||||
if (from_reg == -1)
|
||||
else if (from_reg == -1 || (! dont_share_p && flag_ira && optimize))
|
||||
{
|
||||
alias_set_type alias_set = new_alias_set ();
|
||||
|
||||
|
@ -2086,6 +2197,10 @@ alter_reg (int i, int from_reg)
|
|||
/* Nothing can alias this slot except this pseudo. */
|
||||
set_mem_alias_set (x, alias_set);
|
||||
dse_record_singleton_alias_set (alias_set, mode);
|
||||
|
||||
if (! dont_share_p && flag_ira && optimize)
|
||||
/* Inform IRA about allocation a new stack slot. */
|
||||
ira_mark_new_stack_slot (x, i, total_size);
|
||||
}
|
||||
|
||||
/* Reuse a stack slot if possible. */
|
||||
|
@ -2164,8 +2279,13 @@ alter_reg (int i, int from_reg)
|
|||
|
||||
/* If we have a decl for the original register, set it for the
|
||||
memory. If this is a shared MEM, make a copy. */
|
||||
if (REG_EXPR (regno_reg_rtx[i])
|
||||
&& DECL_P (REG_EXPR (regno_reg_rtx[i])))
|
||||
if (shared_p)
|
||||
{
|
||||
x = copy_rtx (x);
|
||||
set_mem_attrs_from_reg (x, regno_reg_rtx[i]);
|
||||
}
|
||||
else if (REG_EXPR (regno_reg_rtx[i])
|
||||
&& DECL_P (REG_EXPR (regno_reg_rtx[i])))
|
||||
{
|
||||
rtx decl = DECL_RTL_IF_SET (REG_EXPR (regno_reg_rtx[i]));
|
||||
|
||||
|
@ -2441,7 +2561,7 @@ eliminate_regs_1 (rtx x, enum machine_mode mem_mode, rtx insn,
|
|||
/* There exists at least one use of REGNO that cannot be
|
||||
eliminated. Prevent the defining insn from being deleted. */
|
||||
reg_equiv_init[regno] = NULL_RTX;
|
||||
alter_reg (regno, -1);
|
||||
alter_reg (regno, -1, true);
|
||||
}
|
||||
return x;
|
||||
|
||||
|
@ -3817,18 +3937,22 @@ finish_spills (int global)
|
|||
spill_reg_order[i] = -1;
|
||||
|
||||
EXECUTE_IF_SET_IN_REG_SET (&spilled_pseudos, FIRST_PSEUDO_REGISTER, i, rsi)
|
||||
{
|
||||
/* Record the current hard register the pseudo is allocated to in
|
||||
pseudo_previous_regs so we avoid reallocating it to the same
|
||||
hard reg in a later pass. */
|
||||
gcc_assert (reg_renumber[i] >= 0);
|
||||
|
||||
SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
|
||||
/* Mark it as no longer having a hard register home. */
|
||||
reg_renumber[i] = -1;
|
||||
/* We will need to scan everything again. */
|
||||
something_changed = 1;
|
||||
}
|
||||
if (! flag_ira || ! optimize || reg_renumber[i] >= 0)
|
||||
{
|
||||
/* Record the current hard register the pseudo is allocated to
|
||||
in pseudo_previous_regs so we avoid reallocating it to the
|
||||
same hard reg in a later pass. */
|
||||
gcc_assert (reg_renumber[i] >= 0);
|
||||
|
||||
SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
|
||||
/* Mark it as no longer having a hard register home. */
|
||||
reg_renumber[i] = -1;
|
||||
if (flag_ira && optimize)
|
||||
/* Inform IRA about the change. */
|
||||
ira_mark_allocation_change (i);
|
||||
/* We will need to scan everything again. */
|
||||
something_changed = 1;
|
||||
}
|
||||
|
||||
/* Retry global register allocation if possible. */
|
||||
if (global)
|
||||
|
@ -3853,24 +3977,50 @@ finish_spills (int global)
|
|||
}
|
||||
}
|
||||
|
||||
/* Retry allocating the spilled pseudos. For each reg, merge the
|
||||
various reg sets that indicate which hard regs can't be used,
|
||||
and call retry_global_alloc.
|
||||
We change spill_pseudos here to only contain pseudos that did not
|
||||
get a new hard register. */
|
||||
for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
|
||||
if (reg_old_renumber[i] != reg_renumber[i])
|
||||
{
|
||||
HARD_REG_SET forbidden;
|
||||
COPY_HARD_REG_SET (forbidden, bad_spill_regs_global);
|
||||
IOR_HARD_REG_SET (forbidden, pseudo_forbidden_regs[i]);
|
||||
IOR_HARD_REG_SET (forbidden, pseudo_previous_regs[i]);
|
||||
retry_global_alloc (i, forbidden);
|
||||
if (reg_renumber[i] >= 0)
|
||||
CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
|
||||
}
|
||||
}
|
||||
if (! flag_ira || ! optimize)
|
||||
{
|
||||
/* Retry allocating the spilled pseudos. For each reg,
|
||||
merge the various reg sets that indicate which hard regs
|
||||
can't be used, and call retry_global_alloc. We change
|
||||
spill_pseudos here to only contain pseudos that did not
|
||||
get a new hard register. */
|
||||
for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
|
||||
if (reg_old_renumber[i] != reg_renumber[i])
|
||||
{
|
||||
HARD_REG_SET forbidden;
|
||||
|
||||
COPY_HARD_REG_SET (forbidden, bad_spill_regs_global);
|
||||
IOR_HARD_REG_SET (forbidden, pseudo_forbidden_regs[i]);
|
||||
IOR_HARD_REG_SET (forbidden, pseudo_previous_regs[i]);
|
||||
retry_global_alloc (i, forbidden);
|
||||
if (reg_renumber[i] >= 0)
|
||||
CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Retry allocating the pseudos spilled in IRA and the
|
||||
reload. For each reg, merge the various reg sets that
|
||||
indicate which hard regs can't be used, and call
|
||||
ira_reassign_pseudos. */
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0, i = FIRST_PSEUDO_REGISTER; i < (unsigned) max_regno; i++)
|
||||
if (reg_old_renumber[i] != reg_renumber[i])
|
||||
{
|
||||
if (reg_renumber[i] < 0)
|
||||
temp_pseudo_reg_arr[n++] = i;
|
||||
else
|
||||
CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
|
||||
}
|
||||
if (ira_reassign_pseudos (temp_pseudo_reg_arr, n,
|
||||
bad_spill_regs_global,
|
||||
pseudo_forbidden_regs, pseudo_previous_regs,
|
||||
&spilled_pseudos))
|
||||
something_changed = 1;
|
||||
|
||||
}
|
||||
}
|
||||
/* Fix up the register information in the insn chain.
|
||||
This involves deleting those of the spilled pseudos which did not get
|
||||
a new hard register home from the live_{before,after} sets. */
|
||||
|
@ -3879,9 +4029,14 @@ finish_spills (int global)
|
|||
HARD_REG_SET used_by_pseudos;
|
||||
HARD_REG_SET used_by_pseudos2;
|
||||
|
||||
AND_COMPL_REG_SET (&chain->live_throughout, &spilled_pseudos);
|
||||
AND_COMPL_REG_SET (&chain->dead_or_set, &spilled_pseudos);
|
||||
|
||||
if (! flag_ira || ! optimize)
|
||||
{
|
||||
/* Don't do it for IRA because IRA and the reload still can
|
||||
assign hard registers to the spilled pseudos on next
|
||||
reload iterations. */
|
||||
AND_COMPL_REG_SET (&chain->live_throughout, &spilled_pseudos);
|
||||
AND_COMPL_REG_SET (&chain->dead_or_set, &spilled_pseudos);
|
||||
}
|
||||
/* Mark any unallocated hard regs as available for spills. That
|
||||
makes inheritance work somewhat better. */
|
||||
if (chain->need_reload)
|
||||
|
@ -3890,20 +4045,18 @@ finish_spills (int global)
|
|||
REG_SET_TO_HARD_REG_SET (used_by_pseudos2, &chain->dead_or_set);
|
||||
IOR_HARD_REG_SET (used_by_pseudos, used_by_pseudos2);
|
||||
|
||||
/* Save the old value for the sanity test below. */
|
||||
COPY_HARD_REG_SET (used_by_pseudos2, chain->used_spill_regs);
|
||||
|
||||
compute_use_by_pseudos (&used_by_pseudos, &chain->live_throughout);
|
||||
compute_use_by_pseudos (&used_by_pseudos, &chain->dead_or_set);
|
||||
/* Value of chain->used_spill_regs from previous iteration
|
||||
may be not included in the value calculated here because
|
||||
of possible removing caller-saves insns (see function
|
||||
delete_caller_save_insns. */
|
||||
COMPL_HARD_REG_SET (chain->used_spill_regs, used_by_pseudos);
|
||||
AND_HARD_REG_SET (chain->used_spill_regs, used_spill_regs);
|
||||
|
||||
/* Make sure we only enlarge the set. */
|
||||
gcc_assert (hard_reg_set_subset_p (used_by_pseudos2,
|
||||
chain->used_spill_regs));
|
||||
}
|
||||
}
|
||||
|
||||
CLEAR_REG_SET (&changed_allocation_pseudos);
|
||||
/* Let alter_reg modify the reg rtx's for the modified pseudos. */
|
||||
for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
|
||||
{
|
||||
|
@ -3911,7 +4064,9 @@ finish_spills (int global)
|
|||
if (reg_old_renumber[i] == regno)
|
||||
continue;
|
||||
|
||||
alter_reg (i, reg_old_renumber[i]);
|
||||
SET_REGNO_REG_SET (&changed_allocation_pseudos, i);
|
||||
|
||||
alter_reg (i, reg_old_renumber[i], false);
|
||||
reg_old_renumber[i] = regno;
|
||||
if (dump_file)
|
||||
{
|
||||
|
@ -4295,8 +4450,8 @@ reload_as_needed (int live_known)
|
|||
be partially clobbered by the call. */
|
||||
else if (CALL_P (insn))
|
||||
{
|
||||
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, call_used_reg_set);
|
||||
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, reg_reloaded_call_part_clobbered);
|
||||
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, call_used_reg_set);
|
||||
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, reg_reloaded_call_part_clobbered);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4967,6 +5122,126 @@ reloads_unique_chain_p (int r1, int r2)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* The recursive function change all occurrences of WHAT in *WHERE
|
||||
onto REPL. */
|
||||
static void
|
||||
substitute (rtx *where, const_rtx what, rtx repl)
|
||||
{
|
||||
const char *fmt;
|
||||
int i;
|
||||
enum rtx_code code;
|
||||
|
||||
if (*where == 0)
|
||||
return;
|
||||
|
||||
if (*where == what || rtx_equal_p (*where, what))
|
||||
{
|
||||
*where = repl;
|
||||
return;
|
||||
}
|
||||
|
||||
code = GET_CODE (*where);
|
||||
fmt = GET_RTX_FORMAT (code);
|
||||
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
||||
{
|
||||
if (fmt[i] == 'E')
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = XVECLEN (*where, i) - 1; j >= 0; j--)
|
||||
substitute (&XVECEXP (*where, i, j), what, repl);
|
||||
}
|
||||
else if (fmt[i] == 'e')
|
||||
substitute (&XEXP (*where, i), what, repl);
|
||||
}
|
||||
}
|
||||
|
||||
/* The function returns TRUE if chain of reload R1 and R2 (in any
|
||||
order) can be evaluated without usage of intermediate register for
|
||||
the reload containing another reload. It is important to see
|
||||
gen_reload to understand what the function is trying to do. As an
|
||||
example, let us have reload chain
|
||||
|
||||
r2: const
|
||||
r1: <something> + const
|
||||
|
||||
and reload R2 got reload reg HR. The function returns true if
|
||||
there is a correct insn HR = HR + <something>. Otherwise,
|
||||
gen_reload will use intermediate register (and this is the reload
|
||||
reg for R1) to reload <something>.
|
||||
|
||||
We need this function to find a conflict for chain reloads. In our
|
||||
example, if HR = HR + <something> is incorrect insn, then we cannot
|
||||
use HR as a reload register for R2. If we do use it then we get a
|
||||
wrong code:
|
||||
|
||||
HR = const
|
||||
HR = <something>
|
||||
HR = HR + HR
|
||||
|
||||
*/
|
||||
static bool
|
||||
gen_reload_chain_without_interm_reg_p (int r1, int r2)
|
||||
{
|
||||
bool result;
|
||||
int regno, n, code;
|
||||
rtx out, in, tem, insn;
|
||||
rtx last = get_last_insn ();
|
||||
|
||||
/* Make r2 a component of r1. */
|
||||
if (reg_mentioned_p (rld[r1].in, rld[r2].in))
|
||||
{
|
||||
n = r1;
|
||||
r1 = r2;
|
||||
r2 = n;
|
||||
}
|
||||
gcc_assert (reg_mentioned_p (rld[r2].in, rld[r1].in));
|
||||
regno = rld[r1].regno >= 0 ? rld[r1].regno : rld[r2].regno;
|
||||
gcc_assert (regno >= 0);
|
||||
out = gen_rtx_REG (rld[r1].mode, regno);
|
||||
in = copy_rtx (rld[r1].in);
|
||||
substitute (&in, rld[r2].in, gen_rtx_REG (rld[r2].mode, regno));
|
||||
|
||||
/* If IN is a paradoxical SUBREG, remove it and try to put the
|
||||
opposite SUBREG on OUT. Likewise for a paradoxical SUBREG on OUT. */
|
||||
if (GET_CODE (in) == SUBREG
|
||||
&& (GET_MODE_SIZE (GET_MODE (in))
|
||||
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))
|
||||
&& (tem = gen_lowpart_common (GET_MODE (SUBREG_REG (in)), out)) != 0)
|
||||
in = SUBREG_REG (in), out = tem;
|
||||
|
||||
if (GET_CODE (in) == PLUS
|
||||
&& (REG_P (XEXP (in, 0))
|
||||
|| GET_CODE (XEXP (in, 0)) == SUBREG
|
||||
|| MEM_P (XEXP (in, 0)))
|
||||
&& (REG_P (XEXP (in, 1))
|
||||
|| GET_CODE (XEXP (in, 1)) == SUBREG
|
||||
|| CONSTANT_P (XEXP (in, 1))
|
||||
|| MEM_P (XEXP (in, 1))))
|
||||
{
|
||||
insn = emit_insn (gen_rtx_SET (VOIDmode, out, in));
|
||||
code = recog_memoized (insn);
|
||||
result = false;
|
||||
|
||||
if (code >= 0)
|
||||
{
|
||||
extract_insn (insn);
|
||||
/* We want constrain operands to treat this insn strictly in
|
||||
its validity determination, i.e., the way it would after
|
||||
reload has completed. */
|
||||
result = constrain_operands (1);
|
||||
}
|
||||
|
||||
delete_insns_since (last);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* It looks like other cases in gen_reload are not possible for
|
||||
chain reloads or do need an intermediate hard registers. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
|
||||
Return 0 otherwise.
|
||||
|
||||
|
@ -5016,7 +5291,8 @@ reloads_conflict (int r1, int r2)
|
|||
case RELOAD_FOR_OPERAND_ADDRESS:
|
||||
return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
|
||||
|| (r2_type == RELOAD_FOR_OPERAND_ADDRESS
|
||||
&& !reloads_unique_chain_p (r1, r2)));
|
||||
&& (!reloads_unique_chain_p (r1, r2)
|
||||
|| !gen_reload_chain_without_interm_reg_p (r1, r2))));
|
||||
|
||||
case RELOAD_FOR_OPADDR_ADDR:
|
||||
return (r2_type == RELOAD_FOR_INPUT
|
||||
|
@ -6724,7 +7000,10 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
|
|||
&& REG_N_SETS (REGNO (old)) == 1)
|
||||
{
|
||||
reg_renumber[REGNO (old)] = REGNO (reloadreg);
|
||||
alter_reg (REGNO (old), -1);
|
||||
if (flag_ira && optimize)
|
||||
/* Inform IRA about the change. */
|
||||
ira_mark_allocation_change (REGNO (old));
|
||||
alter_reg (REGNO (old), -1, false);
|
||||
}
|
||||
special = 1;
|
||||
}
|
||||
|
@ -8161,7 +8440,7 @@ delete_output_reload (rtx insn, int j, int last_reload_reg, rtx new_reload_reg)
|
|||
n_occurrences += count_occurrences (PATTERN (insn),
|
||||
eliminate_regs (substed, 0,
|
||||
NULL_RTX), 0);
|
||||
for (i1 = reg_equiv_alt_mem_list [REGNO (reg)]; i1; i1 = XEXP (i1, 1))
|
||||
for (i1 = reg_equiv_alt_mem_list[REGNO (reg)]; i1; i1 = XEXP (i1, 1))
|
||||
{
|
||||
gcc_assert (!rtx_equal_p (XEXP (i1, 0), substed));
|
||||
n_occurrences += count_occurrences (PATTERN (insn), XEXP (i1, 0), 0);
|
||||
|
@ -8262,7 +8541,10 @@ delete_output_reload (rtx insn, int j, int last_reload_reg, rtx new_reload_reg)
|
|||
|
||||
/* For the debugging info, say the pseudo lives in this reload reg. */
|
||||
reg_renumber[REGNO (reg)] = REGNO (new_reload_reg);
|
||||
alter_reg (REGNO (reg), -1);
|
||||
if (flag_ira && optimize)
|
||||
/* Inform IRA about the change. */
|
||||
ira_mark_allocation_change (REGNO (reg));
|
||||
alter_reg (REGNO (reg), -1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
12
gcc/rtl.h
12
gcc/rtl.h
|
@ -1805,6 +1805,12 @@ rtx remove_list_elem (rtx, rtx *);
|
|||
|
||||
/* regclass.c */
|
||||
|
||||
/* Initialize may_move_cost and friends for mode M. */
|
||||
extern void init_move_cost (enum machine_mode);
|
||||
/* Allocate register info memory. */
|
||||
extern void allocate_reg_info (void);
|
||||
/* Resize reg info. */
|
||||
extern void resize_reg_info (void);
|
||||
/* Free up register info memory. */
|
||||
extern void free_reg_info (void);
|
||||
|
||||
|
@ -1815,6 +1821,7 @@ extern const char *decode_asm_operands (rtx, rtx *, rtx **, const char **,
|
|||
|
||||
extern enum reg_class reg_preferred_class (int);
|
||||
extern enum reg_class reg_alternate_class (int);
|
||||
extern void setup_reg_classes (int, enum reg_class, enum reg_class);
|
||||
|
||||
extern void split_all_insns (void);
|
||||
extern unsigned int split_all_insns_noflow (void);
|
||||
|
@ -2183,12 +2190,16 @@ extern bool can_copy_p (enum machine_mode);
|
|||
extern rtx fis_get_condition (rtx);
|
||||
|
||||
/* In global.c */
|
||||
#ifdef HARD_CONST
|
||||
extern HARD_REG_SET eliminable_regset;
|
||||
#endif
|
||||
extern void mark_elimination (int, int);
|
||||
extern void dump_global_regs (FILE *);
|
||||
#ifdef HARD_CONST
|
||||
/* Yes, this ifdef is silly, but HARD_REG_SET is not always defined. */
|
||||
extern void retry_global_alloc (int, HARD_REG_SET);
|
||||
#endif
|
||||
extern void build_insn_chain (void);
|
||||
|
||||
/* In regclass.c */
|
||||
extern int reg_classes_intersect_p (enum reg_class, enum reg_class);
|
||||
|
@ -2214,6 +2225,7 @@ extern void dbr_schedule (rtx);
|
|||
|
||||
/* In local-alloc.c */
|
||||
extern void dump_local_alloc (FILE *);
|
||||
extern int update_equiv_regs (void);
|
||||
|
||||
/* In reload1.c */
|
||||
extern int function_invariant_p (const_rtx);
|
||||
|
|
28
gcc/testsuite/gcc.dg/20080410-1.c
Normal file
28
gcc/testsuite/gcc.dg/20080410-1.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* { dg-do compile { target "sh-*-*" } } */
|
||||
/* { dg-options "-O0 -m4 -ml -fira" } */
|
||||
/* { dg-final { scan-assembler-not "add\tr0,r0" } } */
|
||||
|
||||
/* This test checks that chain reloads conflict. I they don't
|
||||
conflict, the same hard register R0 is used for the both reloads
|
||||
but in this case the second reload needs an intermediate register
|
||||
(which is the reload register). As the result we have the
|
||||
following code
|
||||
|
||||
mov #4,r0 -- first reload
|
||||
mov r14,r0 -- second reload
|
||||
add r0,r0 -- second reload
|
||||
|
||||
The right code should be
|
||||
|
||||
mov #4,r0 -- first reload
|
||||
mov r14,r1 -- second reload
|
||||
add r0,r1 -- second reload
|
||||
|
||||
*/
|
||||
|
||||
_Complex float foo_float ();
|
||||
|
||||
void bar_float ()
|
||||
{
|
||||
__real foo_float ();
|
||||
}
|
|
@ -179,6 +179,8 @@ DEFTIMEVAR (TV_SMS , "sms modulo scheduling")
|
|||
DEFTIMEVAR (TV_SCHED , "scheduling")
|
||||
DEFTIMEVAR (TV_LOCAL_ALLOC , "local alloc")
|
||||
DEFTIMEVAR (TV_GLOBAL_ALLOC , "global alloc")
|
||||
DEFTIMEVAR (TV_IRA , "integrated RA")
|
||||
DEFTIMEVAR (TV_RELOAD , "reload")
|
||||
DEFTIMEVAR (TV_RELOAD_CSE_REGS , "reload CSE regs")
|
||||
DEFTIMEVAR (TV_SEQABSTR , "sequence abstraction")
|
||||
DEFTIMEVAR (TV_GCSE_AFTER_RELOAD , "load CSE after reload")
|
||||
|
|
17
gcc/toplev.c
17
gcc/toplev.c
|
@ -66,6 +66,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "diagnostic.h"
|
||||
#include "params.h"
|
||||
#include "reload.h"
|
||||
#include "ira.h"
|
||||
#include "dwarf2asm.h"
|
||||
#include "integrate.h"
|
||||
#include "real.h"
|
||||
|
@ -270,6 +271,14 @@ int flag_next_runtime = 0;
|
|||
|
||||
enum tls_model flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC;
|
||||
|
||||
/* Set the default algorithm for the integrated register allocator. */
|
||||
|
||||
enum ira_algorithm flag_ira_algorithm = IRA_ALGORITHM_MIXED;
|
||||
|
||||
/* Set the default value for -fira-verbose. */
|
||||
|
||||
unsigned int flag_ira_verbose = 5;
|
||||
|
||||
/* Nonzero means change certain warnings into errors.
|
||||
Usually these are warnings about failure to conform to some standard. */
|
||||
|
||||
|
@ -2009,6 +2018,7 @@ backend_init (void)
|
|||
save_register_info ();
|
||||
|
||||
/* Initialize the target-specific back end pieces. */
|
||||
ira_init_once ();
|
||||
backend_init_target ();
|
||||
}
|
||||
|
||||
|
@ -2029,9 +2039,10 @@ lang_dependent_init_target (void)
|
|||
/* Do the target-specific parts of expr initialization. */
|
||||
init_expr_target ();
|
||||
|
||||
/* Although the actions of init_set_costs are language-independent,
|
||||
it uses optabs, so we cannot call it from backend_init. */
|
||||
/* Although the actions of these functions are language-independent,
|
||||
they use optabs, so we cannot call them from backend_init. */
|
||||
init_set_costs ();
|
||||
ira_init ();
|
||||
|
||||
expand_dummy_function_end ();
|
||||
}
|
||||
|
@ -2132,6 +2143,8 @@ finalize (void)
|
|||
statistics_fini ();
|
||||
finish_optimization_passes ();
|
||||
|
||||
ira_finish_once ();
|
||||
|
||||
if (mem_report)
|
||||
dump_memory_report (true);
|
||||
|
||||
|
|
|
@ -139,6 +139,11 @@ extern int flag_unroll_all_loops;
|
|||
extern int flag_unswitch_loops;
|
||||
extern int flag_cprop_registers;
|
||||
extern int time_report;
|
||||
extern int flag_ira;
|
||||
extern int flag_ira_coalesce;
|
||||
extern int flag_ira_move_spills;
|
||||
extern int flag_ira_share_save_slots;
|
||||
extern int flag_ira_share_spill_slots;
|
||||
|
||||
/* Things to do with target switches. */
|
||||
extern void print_version (FILE *, const char *);
|
||||
|
|
|
@ -469,6 +469,7 @@ extern struct rtl_opt_pass pass_sms;
|
|||
extern struct rtl_opt_pass pass_sched;
|
||||
extern struct rtl_opt_pass pass_local_alloc;
|
||||
extern struct rtl_opt_pass pass_global_alloc;
|
||||
extern struct rtl_opt_pass pass_ira;
|
||||
extern struct rtl_opt_pass pass_postreload;
|
||||
extern struct rtl_opt_pass pass_clean_state;
|
||||
extern struct rtl_opt_pass pass_branch_prob;
|
||||
|
|
Loading…
Add table
Reference in a new issue