aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorVladimir Makarov <vmakarov@redhat.com>2008-08-26 12:39:58 +0000
committerVladimir Makarov <vmakarov@gcc.gnu.org>2008-08-26 12:39:58 +0000
commit058e97ecf33ad0dfd926b3876a4bcf59ac9556ff (patch)
tree6a33af204d23b09732010003bb7079bf0835f4df /gcc
parent8ff27c248ca53aa53d6f2a19d2ee1ce6220013c1 (diff)
downloadgcc-058e97ecf33ad0dfd926b3876a4bcf59ac9556ff.zip
gcc-058e97ecf33ad0dfd926b3876a4bcf59ac9556ff.tar.gz
gcc-058e97ecf33ad0dfd926b3876a4bcf59ac9556ff.tar.bz2
[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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog217
-rw-r--r--gcc/Makefile.in47
-rw-r--r--gcc/alias.c46
-rw-r--r--gcc/caller-save.c473
-rw-r--r--gcc/cfgloopanal.c25
-rw-r--r--gcc/common.opt24
-rw-r--r--gcc/config/alpha/alpha.h13
-rw-r--r--gcc/config/arm/arm.h14
-rw-r--r--gcc/config/avr/avr.h13
-rw-r--r--gcc/config/bfin/bfin.h13
-rw-r--r--gcc/config/i386/i386.h13
-rw-r--r--gcc/config/ia64/ia64.h13
-rw-r--r--gcc/config/mn10300/mn10300.h13
-rw-r--r--gcc/config/rs6000/rs6000.h16
-rw-r--r--gcc/config/s390/s390.h24
-rw-r--r--gcc/config/sh/sh.h14
-rw-r--r--gcc/config/sh/sh.md7
-rw-r--r--gcc/config/sparc/sparc.h13
-rw-r--r--gcc/config/spu/spu.h3
-rw-r--r--gcc/doc/invoke.texi58
-rw-r--r--gcc/doc/passes.texi22
-rw-r--r--gcc/doc/tm.texi25
-rw-r--r--gcc/flags.h13
-rw-r--r--gcc/global.c41
-rw-r--r--gcc/hard-reg-set.h9
-rw-r--r--gcc/ira-build.c2449
-rw-r--r--gcc/ira-color.c2955
-rw-r--r--gcc/ira-conflicts.c777
-rw-r--r--gcc/ira-costs.c1594
-rw-r--r--gcc/ira-emit.c1019
-rw-r--r--gcc/ira-int.h1200
-rw-r--r--gcc/ira-lives.c967
-rw-r--r--gcc/ira.c2064
-rw-r--r--gcc/ira.h37
-rw-r--r--gcc/local-alloc.c18
-rw-r--r--gcc/opts.c28
-rw-r--r--gcc/params.def5
-rw-r--r--gcc/params.h2
-rw-r--r--gcc/passes.c1
-rw-r--r--gcc/postreload.c2
-rw-r--r--gcc/regclass.c56
-rw-r--r--gcc/regmove.c7
-rw-r--r--gcc/regs.h18
-rw-r--r--gcc/reload.c10
-rw-r--r--gcc/reload.h11
-rw-r--r--gcc/reload1.c432
-rw-r--r--gcc/rtl.h12
-rw-r--r--gcc/testsuite/gcc.dg/20080410-1.c28
-rw-r--r--gcc/timevar.def2
-rw-r--r--gcc/toplev.c17
-rw-r--r--gcc/toplev.h5
-rw-r--r--gcc/tree-pass.h1
52 files changed, 14707 insertions, 179 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7aae9b16..2c24ea1 100644
--- a/gcc/ChangeLog
+++ b/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>
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e50c2d5..8edcd94 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -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) \
diff --git a/gcc/alias.c b/gcc/alias.c
index 684205c..56660ec 100644
--- a/gcc/alias.c
+++ b/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;
diff --git a/gcc/caller-save.c b/gcc/caller-save.c
index e3d76c6..ee8a0dc 100644
--- a/gcc/caller-save.c
+++ b/gcc/caller-save.c
@@ -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. */
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- for (j = MOVE_MAX_WORDS; j > 0; j--)
- {
- int do_save = 1;
-
- /* If no mode exists for this size, try another. Also break out
- if we have already saved this hard register. */
- if (regno_save_mode[i][j] == VOIDmode || regno_save_mem[i][1] != 0)
- continue;
-
- /* See if any register in this group has been saved. */
- for (k = 0; k < j; k++)
- if (regno_save_mem[i + k][1])
+ if (flag_ira && 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);
+
+ /* 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);
+
+ 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);
+
+ /* 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)
+ {
+ int r = reg_renumber[regno];
+ int bound;
+
+ if (r < 0)
+ continue;
- for (k = 0; k < j; k++)
- if (! TEST_HARD_REG_BIT (hard_regs_used, i + k))
+ 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];
+ }
+ for (i = 0; i < call_saved_regs_num; i++)
{
- do_save = 0;
- break;
+ 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;
+ }
}
- 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);
- }
+ }
+ /* 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),
diff --git a/gcc/cfgloopanal.c b/gcc/cfgloopanal.c
index c00d1c5..db5bd2a 100644
--- a/gcc/cfgloopanal.c
+++ b/gcc/cfgloopanal.c
@@ -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. */
diff --git a/gcc/common.opt b/gcc/common.opt
index 523f712..21d1ae3 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -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
diff --git a/gcc/config/alpha/alpha.h b/gcc/config/alpha/alpha.h
index b37a19d..4336e6c 100644
--- a/gcc/config/alpha/alpha.h
+++ b/gcc/config/alpha/alpha.h
@@ -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
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index d99f77d..fd5067a 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -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. */
diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h
index 6a27c3b..b5132e2 100644
--- a/gcc/config/avr/avr.h
+++ b/gcc/config/avr/avr.h
@@ -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
diff --git a/gcc/config/bfin/bfin.h b/gcc/config/bfin/bfin.h
index 6f2d16c..826e60b 100644
--- a/gcc/config/bfin/bfin.h
+++ b/gcc/config/bfin/bfin.h
@@ -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. */
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 2387156..69c7472 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -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
diff --git a/gcc/config/ia64/ia64.h b/gcc/config/ia64/ia64.h
index 60934f2..6fca690 100644
--- a/gcc/config/ia64/ia64.h
+++ b/gcc/config/ia64/ia64.h
@@ -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. */
diff --git a/gcc/config/mn10300/mn10300.h b/gcc/config/mn10300/mn10300.h
index 1d2339b..07035fb 100644
--- a/gcc/config/mn10300/mn10300.h
+++ b/gcc/config/mn10300/mn10300.h
@@ -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
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index 1f6d07b..048d163 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -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
diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
index a8cb477..b96f100 100644
--- a/gcc/config/s390/s390.h
+++ b/gcc/config/s390/s390.h
@@ -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])
diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h
index 2305872..8af2f43 100644
--- a/gcc/config/sh/sh.h
+++ b/gcc/config/sh/sh.h
@@ -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. */
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index 6300054..b6e56aa 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -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;
diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h
index 4d180da..4289470 100644
--- a/gcc/config/sparc/sparc.h
+++ b/gcc/config/sparc/sparc.h
@@ -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.
diff --git a/gcc/config/spu/spu.h b/gcc/config/spu/spu.h
index 0bd69d3..86042aa 100644
--- a/gcc/config/spu/spu.h
+++ b/gcc/config/spu/spu.h
@@ -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 \
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3789be6..0c4e6b4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/doc/passes.texi b/gcc/doc/passes.texi
index daeaf95..9004dd7 100644
--- a/gcc/doc/passes.texi
+++ b/gcc/doc/passes.texi
@@ -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
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 9b4a921..3087694 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -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
diff --git a/gcc/flags.h b/gcc/flags.h
index 1de81f8..1d645d9 100644
--- a/gcc/flags.h
+++ b/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. */
diff --git a/gcc/global.c b/gcc/global.c
index 690a80c..e0783d5 100644
--- a/gcc/global.c
+++ b/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 */
diff --git a/gcc/hard-reg-set.h b/gcc/hard-reg-set.h
index 21030fd..c4f74a2 100644
--- a/gcc/hard-reg-set.h
+++ b/gcc/hard-reg-set.h
@@ -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. */
diff --git a/gcc/ira-build.c b/gcc/ira-build.c
new file mode 100644
index 0000000..979a3c8
--- /dev/null
+++ b/gcc/ira-build.c
@@ -0,0 +1,2449 @@
+/* Building internal representation for IRA.
+ 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 "rtl.h"
+#include "tm_p.h"
+#include "target.h"
+#include "regs.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 "output.h"
+#include "reload.h"
+#include "sparseset.h"
+#include "ira-int.h"
+
+static ira_copy_t find_allocno_copy (ira_allocno_t, ira_allocno_t, rtx,
+ ira_loop_tree_node_t);
+
+/* The root of the loop tree corresponding to the all function. */
+ira_loop_tree_node_t ira_loop_tree_root;
+
+/* Height of the loop tree. */
+int ira_loop_tree_height;
+
+/* All nodes representing basic blocks are referred through the
+ following array. We can not use basic block member `aux' for this
+ because it is used for insertion of insns on edges. */
+ira_loop_tree_node_t ira_bb_nodes;
+
+/* All nodes representing loops are referred through the following
+ array. */
+ira_loop_tree_node_t ira_loop_nodes;
+
+/* Map regno -> allocnos with given regno (see comments for
+ allocno member `next_regno_allocno'). */
+ira_allocno_t *ira_regno_allocno_map;
+
+/* Array of references to all allocnos. The order number of the
+ allocno corresponds to the index in the array. Removed allocnos
+ have NULL element value. */
+ira_allocno_t *ira_allocnos;
+
+/* Sizes of the previous array. */
+int ira_allocnos_num;
+
+/* Map conflict id -> allocno with given conflict id (see comments for
+ allocno member `conflict_id'). */
+ira_allocno_t *ira_conflict_id_allocno_map;
+
+/* Array of references to all copies. The order number of the copy
+ corresponds to the index in the array. Removed copies have NULL
+ element value. */
+ira_copy_t *ira_copies;
+
+/* Size of the previous array. */
+int ira_copies_num;
+
+
+
+/* LAST_BASIC_BLOCK before generating additional insns because of live
+ range splitting. Emitting insns on a critical edge creates a new
+ basic block. */
+static int last_basic_block_before_change;
+
+/* The following function allocates the loop tree nodes. If LOOPS_P
+ is FALSE, the nodes corresponding to the loops (except the root
+ which corresponds the all function) will be not allocated but nodes
+ will still be allocated for basic blocks. */
+static void
+create_loop_tree_nodes (bool loops_p)
+{
+ unsigned int i, j;
+ int max_regno;
+ bool skip_p;
+ edge_iterator ei;
+ edge e;
+ VEC (edge, heap) *edges;
+ loop_p loop;
+
+ ira_bb_nodes
+ = ((struct ira_loop_tree_node *)
+ ira_allocate (sizeof (struct ira_loop_tree_node) * last_basic_block));
+ last_basic_block_before_change = last_basic_block;
+ for (i = 0; i < (unsigned int) last_basic_block; i++)
+ {
+ ira_bb_nodes[i].regno_allocno_map = NULL;
+ memset (ira_bb_nodes[i].reg_pressure, 0,
+ sizeof (ira_bb_nodes[i].reg_pressure));
+ ira_bb_nodes[i].mentioned_allocnos = NULL;
+ ira_bb_nodes[i].modified_regnos = NULL;
+ ira_bb_nodes[i].border_allocnos = NULL;
+ ira_bb_nodes[i].local_copies = NULL;
+ }
+ ira_loop_nodes = ((struct ira_loop_tree_node *)
+ ira_allocate (sizeof (struct ira_loop_tree_node)
+ * VEC_length (loop_p, ira_loops.larray)));
+ max_regno = max_reg_num ();
+ for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++)
+ {
+ if (loop != ira_loops.tree_root)
+ {
+ ira_loop_nodes[i].regno_allocno_map = NULL;
+ if (! loops_p)
+ continue;
+ skip_p = false;
+ FOR_EACH_EDGE (e, ei, loop->header->preds)
+ if (e->src != loop->latch
+ && (e->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (e))
+ {
+ skip_p = true;
+ break;
+ }
+ if (skip_p)
+ continue;
+ edges = get_loop_exit_edges (loop);
+ for (j = 0; VEC_iterate (edge, edges, j, e); j++)
+ if ((e->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (e))
+ {
+ skip_p = true;
+ break;
+ }
+ VEC_free (edge, heap, edges);
+ if (skip_p)
+ continue;
+ }
+ ira_loop_nodes[i].regno_allocno_map
+ = (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t) * max_regno);
+ memset (ira_loop_nodes[i].regno_allocno_map, 0,
+ sizeof (ira_allocno_t) * max_regno);
+ memset (ira_loop_nodes[i].reg_pressure, 0,
+ sizeof (ira_loop_nodes[i].reg_pressure));
+ ira_loop_nodes[i].mentioned_allocnos = ira_allocate_bitmap ();
+ ira_loop_nodes[i].modified_regnos = ira_allocate_bitmap ();
+ ira_loop_nodes[i].border_allocnos = ira_allocate_bitmap ();
+ ira_loop_nodes[i].local_copies = ira_allocate_bitmap ();
+ }
+}
+
+/* The function returns TRUE if there are more one allocation
+ region. */
+static bool
+more_one_region_p (void)
+{
+ unsigned int i;
+ loop_p loop;
+
+ for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++)
+ if (ira_loop_nodes[i].regno_allocno_map != NULL
+ && ira_loop_tree_root != &ira_loop_nodes[i])
+ return true;
+ return false;
+}
+
+/* Free the loop tree node of a loop. */
+static void
+finish_loop_tree_node (ira_loop_tree_node_t loop)
+{
+ if (loop->regno_allocno_map != NULL)
+ {
+ ira_assert (loop->bb == NULL);
+ ira_free_bitmap (loop->local_copies);
+ ira_free_bitmap (loop->border_allocnos);
+ ira_free_bitmap (loop->modified_regnos);
+ ira_free_bitmap (loop->mentioned_allocnos);
+ ira_free (loop->regno_allocno_map);
+ loop->regno_allocno_map = NULL;
+ }
+}
+
+/* Free the loop tree nodes. */
+static void
+finish_loop_tree_nodes (void)
+{
+ unsigned int i;
+ loop_p loop;
+
+ for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++)
+ finish_loop_tree_node (&ira_loop_nodes[i]);
+ ira_free (ira_loop_nodes);
+ for (i = 0; i < (unsigned int) last_basic_block_before_change; i++)
+ {
+ if (ira_bb_nodes[i].local_copies != NULL)
+ ira_free_bitmap (ira_bb_nodes[i].local_copies);
+ if (ira_bb_nodes[i].border_allocnos != NULL)
+ ira_free_bitmap (ira_bb_nodes[i].border_allocnos);
+ if (ira_bb_nodes[i].modified_regnos != NULL)
+ ira_free_bitmap (ira_bb_nodes[i].modified_regnos);
+ if (ira_bb_nodes[i].mentioned_allocnos != NULL)
+ ira_free_bitmap (ira_bb_nodes[i].mentioned_allocnos);
+ if (ira_bb_nodes[i].regno_allocno_map != NULL)
+ ira_free (ira_bb_nodes[i].regno_allocno_map);
+ }
+ ira_free (ira_bb_nodes);
+}
+
+
+
+/* The following recursive function adds LOOP to the loop tree
+ hierarchy. LOOP is added only once. */
+static void
+add_loop_to_tree (struct loop *loop)
+{
+ struct loop *parent;
+ ira_loop_tree_node_t loop_node, parent_node;
+
+ /* We can not use loop node access macros here because of potential
+ checking and because the nodes are not initialized enough
+ yet. */
+ if (loop_outer (loop) != NULL)
+ add_loop_to_tree (loop_outer (loop));
+ if (ira_loop_nodes[loop->num].regno_allocno_map != NULL
+ && ira_loop_nodes[loop->num].children == NULL)
+ {
+ /* We have not added loop node to the tree yet. */
+ loop_node = &ira_loop_nodes[loop->num];
+ loop_node->loop = loop;
+ loop_node->bb = NULL;
+ for (parent = loop_outer (loop);
+ parent != NULL;
+ parent = loop_outer (parent))
+ if (ira_loop_nodes[parent->num].regno_allocno_map != NULL)
+ break;
+ if (parent == NULL)
+ {
+ loop_node->next = NULL;
+ loop_node->subloop_next = NULL;
+ loop_node->parent = NULL;
+ }
+ else
+ {
+ parent_node = &ira_loop_nodes[parent->num];
+ loop_node->next = parent_node->children;
+ parent_node->children = loop_node;
+ loop_node->subloop_next = parent_node->subloops;
+ parent_node->subloops = loop_node;
+ loop_node->parent = parent_node;
+ }
+ }
+}
+
+/* The following recursive function sets up levels of nodes of the
+ tree given its root LOOP_NODE. The enumeration starts with LEVEL.
+ The function returns maximal value of level in the tree + 1. */
+static int
+setup_loop_tree_level (ira_loop_tree_node_t loop_node, int level)
+{
+ int height, max_height;
+ ira_loop_tree_node_t subloop_node;
+
+ ira_assert (loop_node->bb == NULL);
+ loop_node->level = level;
+ max_height = level + 1;
+ for (subloop_node = loop_node->subloops;
+ subloop_node != NULL;
+ subloop_node = subloop_node->subloop_next)
+ {
+ ira_assert (subloop_node->bb == NULL);
+ height = setup_loop_tree_level (subloop_node, level + 1);
+ if (height > max_height)
+ max_height = height;
+ }
+ return max_height;
+}
+
+/* Create the loop tree. The algorithm is designed to provide correct
+ order of loops (they are ordered by their last loop BB) and basic
+ blocks in the chain formed by member next. */
+static void
+form_loop_tree (void)
+{
+ unsigned int i;
+ basic_block bb;
+ struct loop *parent;
+ ira_loop_tree_node_t bb_node, loop_node;
+ loop_p loop;
+
+ /* We can not use loop/bb node access macros because of potential
+ checking and because the nodes are not initialized enough
+ yet. */
+ for (i = 0; VEC_iterate (loop_p, ira_loops.larray, i, loop); i++)
+ if (ira_loop_nodes[i].regno_allocno_map != NULL)
+ {
+ ira_loop_nodes[i].children = NULL;
+ ira_loop_nodes[i].subloops = NULL;
+ }
+ FOR_EACH_BB_REVERSE (bb)
+ {
+ bb_node = &ira_bb_nodes[bb->index];
+ bb_node->bb = bb;
+ bb_node->loop = NULL;
+ bb_node->subloops = NULL;
+ bb_node->children = NULL;
+ bb_node->subloop_next = NULL;
+ bb_node->next = NULL;
+ for (parent = bb->loop_father;
+ parent != NULL;
+ parent = loop_outer (parent))
+ if (ira_loop_nodes[parent->num].regno_allocno_map != NULL)
+ break;
+ add_loop_to_tree (parent);
+ loop_node = &ira_loop_nodes[parent->num];
+ bb_node->next = loop_node->children;
+ bb_node->parent = loop_node;
+ loop_node->children = bb_node;
+ }
+ ira_loop_tree_root = IRA_LOOP_NODE_BY_INDEX (ira_loops.tree_root->num);
+ ira_loop_tree_height = setup_loop_tree_level (ira_loop_tree_root, 0);
+ ira_assert (ira_loop_tree_root->regno_allocno_map != NULL);
+}
+
+
+
+/* Rebuild IRA_REGNO_ALLOCNO_MAP and REGNO_ALLOCNO_MAPs of the loop
+ tree nodes. */
+static void
+rebuild_regno_allocno_maps (void)
+{
+ unsigned int l;
+ int max_regno, regno;
+ ira_allocno_t a;
+ ira_loop_tree_node_t loop_tree_node;
+ loop_p loop;
+ ira_allocno_iterator ai;
+
+ max_regno = max_reg_num ();
+ for (l = 0; VEC_iterate (loop_p, ira_loops.larray, l, loop); l++)
+ if (ira_loop_nodes[l].regno_allocno_map != NULL)
+ {
+ ira_free (ira_loop_nodes[l].regno_allocno_map);
+ ira_loop_nodes[l].regno_allocno_map
+ = (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
+ * max_regno);
+ memset (ira_loop_nodes[l].regno_allocno_map, 0,
+ sizeof (ira_allocno_t) * max_regno);
+ }
+ ira_free (ira_regno_allocno_map);
+ ira_regno_allocno_map
+ = (ira_allocno_t *) ira_allocate (max_regno * sizeof (ira_allocno_t));
+ memset (ira_regno_allocno_map, 0, max_regno * sizeof (ira_allocno_t));
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (ALLOCNO_CAP_MEMBER (a) != NULL)
+ /* Caps are not in the regno allocno maps. */
+ continue;
+ regno = ALLOCNO_REGNO (a);
+ loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
+ ALLOCNO_NEXT_REGNO_ALLOCNO (a) = ira_regno_allocno_map[regno];
+ ira_regno_allocno_map[regno] = a;
+ if (loop_tree_node->regno_allocno_map[regno] == NULL)
+ /* Remember that we can create temporary allocnos to break
+ cycles in register shuffle. */
+ loop_tree_node->regno_allocno_map[regno] = a;
+ }
+}
+
+
+
+/* Pools for allocnos and allocno live ranges. */
+static alloc_pool allocno_pool, allocno_live_range_pool;
+
+/* Vec containing references to all created allocnos. It is a
+ container of array allocnos. */
+static VEC(ira_allocno_t,heap) *allocno_vec;
+
+/* Vec containing references to all created allocnos. It is a
+ container of ira_conflict_id_allocno_map. */
+static VEC(ira_allocno_t,heap) *ira_conflict_id_allocno_map_vec;
+
+/* Initialize data concerning allocnos. */
+static void
+initiate_allocnos (void)
+{
+ allocno_live_range_pool
+ = create_alloc_pool ("allocno live ranges",
+ sizeof (struct ira_allocno_live_range), 100);
+ allocno_pool
+ = create_alloc_pool ("allocnos", sizeof (struct ira_allocno), 100);
+ allocno_vec = VEC_alloc (ira_allocno_t, heap, max_reg_num () * 2);
+ ira_allocnos = NULL;
+ ira_allocnos_num = 0;
+ ira_conflict_id_allocno_map_vec
+ = VEC_alloc (ira_allocno_t, heap, max_reg_num () * 2);
+ ira_conflict_id_allocno_map = NULL;
+ ira_regno_allocno_map
+ = (ira_allocno_t *) ira_allocate (max_reg_num () * sizeof (ira_allocno_t));
+ memset (ira_regno_allocno_map, 0, max_reg_num () * sizeof (ira_allocno_t));
+}
+
+/* Create and return the allocno corresponding to REGNO in
+ LOOP_TREE_NODE. Add the allocno to the list of allocnos with the
+ same regno if CAP_P is FALSE. */
+ira_allocno_t
+ira_create_allocno (int regno, bool cap_p, ira_loop_tree_node_t loop_tree_node)
+{
+ ira_allocno_t a;
+
+ a = (ira_allocno_t) pool_alloc (allocno_pool);
+ ALLOCNO_REGNO (a) = regno;
+ ALLOCNO_LOOP_TREE_NODE (a) = loop_tree_node;
+ if (! cap_p)
+ {
+ ALLOCNO_NEXT_REGNO_ALLOCNO (a) = ira_regno_allocno_map[regno];
+ ira_regno_allocno_map[regno] = a;
+ if (loop_tree_node->regno_allocno_map[regno] == NULL)
+ /* Remember that we can create temporary allocnos to break
+ cycles in register shuffle on region borders (see
+ ira-emit.c). */
+ loop_tree_node->regno_allocno_map[regno] = a;
+ }
+ ALLOCNO_CAP (a) = NULL;
+ ALLOCNO_CAP_MEMBER (a) = NULL;
+ ALLOCNO_NUM (a) = ira_allocnos_num;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) = NULL;
+ ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = 0;
+ COPY_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), ira_no_alloc_regs);
+ COPY_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), ira_no_alloc_regs);
+ ALLOCNO_NREFS (a) = 0;
+ ALLOCNO_FREQ (a) = 1;
+ ALLOCNO_HARD_REGNO (a) = -1;
+ ALLOCNO_CALL_FREQ (a) = 0;
+ ALLOCNO_CALLS_CROSSED_NUM (a) = 0;
+#ifdef STACK_REGS
+ ALLOCNO_NO_STACK_REG_P (a) = false;
+ ALLOCNO_TOTAL_NO_STACK_REG_P (a) = false;
+#endif
+ ALLOCNO_MEM_OPTIMIZED_DEST (a) = NULL;
+ ALLOCNO_MEM_OPTIMIZED_DEST_P (a) = false;
+ ALLOCNO_SOMEWHERE_RENAMED_P (a) = false;
+ ALLOCNO_CHILD_RENAMED_P (a) = false;
+ ALLOCNO_DONT_REASSIGN_P (a) = false;
+ ALLOCNO_IN_GRAPH_P (a) = false;
+ ALLOCNO_ASSIGNED_P (a) = false;
+ ALLOCNO_MAY_BE_SPILLED_P (a) = false;
+ ALLOCNO_SPLAY_REMOVED_P (a) = false;
+ ALLOCNO_CONFLICT_VEC_P (a) = false;
+ ALLOCNO_MODE (a) = (regno < 0 ? VOIDmode : PSEUDO_REGNO_MODE (regno));
+ ALLOCNO_COPIES (a) = NULL;
+ ALLOCNO_HARD_REG_COSTS (a) = NULL;
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (a) = NULL;
+ ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL;
+ ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL;
+ ALLOCNO_LEFT_CONFLICTS_NUM (a) = -1;
+ ALLOCNO_COVER_CLASS (a) = NO_REGS;
+ ALLOCNO_COVER_CLASS_COST (a) = 0;
+ ALLOCNO_MEMORY_COST (a) = 0;
+ ALLOCNO_UPDATED_MEMORY_COST (a) = 0;
+ ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a) = 0;
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (a) = NULL;
+ ALLOCNO_PREV_BUCKET_ALLOCNO (a) = NULL;
+ ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a;
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a;
+ ALLOCNO_LIVE_RANGES (a) = NULL;
+ ALLOCNO_MIN (a) = INT_MAX;
+ ALLOCNO_MAX (a) = -1;
+ ALLOCNO_CONFLICT_ID (a) = ira_allocnos_num;
+ VEC_safe_push (ira_allocno_t, heap, allocno_vec, a);
+ ira_allocnos = VEC_address (ira_allocno_t, allocno_vec);
+ ira_allocnos_num = VEC_length (ira_allocno_t, allocno_vec);
+ VEC_safe_push (ira_allocno_t, heap, ira_conflict_id_allocno_map_vec, a);
+ ira_conflict_id_allocno_map
+ = VEC_address (ira_allocno_t, ira_conflict_id_allocno_map_vec);
+ return a;
+}
+
+/* Set up cover class for A and update its conflict hard registers. */
+void
+ira_set_allocno_cover_class (ira_allocno_t a, enum reg_class cover_class)
+{
+ ALLOCNO_COVER_CLASS (a) = cover_class;
+ IOR_COMPL_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a),
+ reg_class_contents[cover_class]);
+ IOR_COMPL_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a),
+ reg_class_contents[cover_class]);
+}
+
+/* Return TRUE if the conflict vector with NUM elements is more
+ profitable than conflict bit vector for A. */
+bool
+ira_conflict_vector_profitable_p (ira_allocno_t a, int num)
+{
+ int nw;
+
+ if (ALLOCNO_MAX (a) < ALLOCNO_MIN (a))
+ /* We prefer bit vector in such case because it does not result in
+ allocation. */
+ return false;
+
+ nw = (ALLOCNO_MAX (a) - ALLOCNO_MIN (a) + IRA_INT_BITS) / IRA_INT_BITS;
+ return (2 * sizeof (ira_allocno_t) * (num + 1)
+ < 3 * nw * sizeof (IRA_INT_TYPE));
+}
+
+/* Allocates and initialize the conflict vector of A for NUM
+ conflicting allocnos. */
+void
+ira_allocate_allocno_conflict_vec (ira_allocno_t a, int num)
+{
+ int size;
+ ira_allocno_t *vec;
+
+ ira_assert (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) == NULL);
+ num++; /* for NULL end marker */
+ size = sizeof (ira_allocno_t) * num;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) = ira_allocate (size);
+ vec = (ira_allocno_t *) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a);
+ vec[0] = NULL;
+ ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = 0;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a) = size;
+ ALLOCNO_CONFLICT_VEC_P (a) = true;
+}
+
+/* Allocate and initialize the conflict bit vector of A. */
+static void
+allocate_allocno_conflict_bit_vec (ira_allocno_t a)
+{
+ unsigned int size;
+
+ ira_assert (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) == NULL);
+ size = ((ALLOCNO_MAX (a) - ALLOCNO_MIN (a) + IRA_INT_BITS)
+ / IRA_INT_BITS * sizeof (IRA_INT_TYPE));
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) = ira_allocate (size);
+ memset (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a), 0, size);
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a) = size;
+ ALLOCNO_CONFLICT_VEC_P (a) = false;
+}
+
+/* Allocate and initialize the conflict vector or conflict bit vector
+ of A for NUM conflicting allocnos whatever is more profitable. */
+void
+ira_allocate_allocno_conflicts (ira_allocno_t a, int num)
+{
+ if (ira_conflict_vector_profitable_p (a, num))
+ ira_allocate_allocno_conflict_vec (a, num);
+ else
+ allocate_allocno_conflict_bit_vec (a);
+}
+
+/* Add A2 to the conflicts of A1. */
+static void
+add_to_allocno_conflicts (ira_allocno_t a1, ira_allocno_t a2)
+{
+ int num;
+ unsigned int size;
+
+ if (ALLOCNO_CONFLICT_VEC_P (a1))
+ {
+ ira_allocno_t *vec;
+
+ num = ALLOCNO_CONFLICT_ALLOCNOS_NUM (a1) + 2;
+ if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1)
+ >= num * sizeof (ira_allocno_t))
+ vec = (ira_allocno_t *) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1);
+ else
+ {
+ size = (3 * num / 2 + 1) * sizeof (ira_allocno_t);
+ vec = (ira_allocno_t *) ira_allocate (size);
+ memcpy (vec, ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1),
+ sizeof (ira_allocno_t) * ALLOCNO_CONFLICT_ALLOCNOS_NUM (a1));
+ ira_free (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1));
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1) = vec;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1) = size;
+ }
+ vec[num - 2] = a2;
+ vec[num - 1] = NULL;
+ ALLOCNO_CONFLICT_ALLOCNOS_NUM (a1)++;
+ }
+ else
+ {
+ int nw, added_head_nw, id;
+ IRA_INT_TYPE *vec;
+
+ id = ALLOCNO_CONFLICT_ID (a2);
+ vec = (IRA_INT_TYPE *) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1);
+ if (ALLOCNO_MIN (a1) > id)
+ {
+ /* Expand head of the bit vector. */
+ added_head_nw = (ALLOCNO_MIN (a1) - id - 1) / IRA_INT_BITS + 1;
+ nw = (ALLOCNO_MAX (a1) - ALLOCNO_MIN (a1)) / IRA_INT_BITS + 1;
+ size = (nw + added_head_nw) * sizeof (IRA_INT_TYPE);
+ if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1) >= size)
+ {
+ memmove ((char *) vec + added_head_nw * sizeof (IRA_INT_TYPE),
+ vec, nw * sizeof (IRA_INT_TYPE));
+ memset (vec, 0, added_head_nw * sizeof (IRA_INT_TYPE));
+ }
+ else
+ {
+ size
+ = (3 * (nw + added_head_nw) / 2 + 1) * sizeof (IRA_INT_TYPE);
+ vec = (IRA_INT_TYPE *) ira_allocate (size);
+ memcpy ((char *) vec + added_head_nw * sizeof (IRA_INT_TYPE),
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1),
+ nw * sizeof (IRA_INT_TYPE));
+ memset (vec, 0, added_head_nw * sizeof (IRA_INT_TYPE));
+ memset ((char *) vec
+ + (nw + added_head_nw) * sizeof (IRA_INT_TYPE),
+ 0, size - (nw + added_head_nw) * sizeof (IRA_INT_TYPE));
+ ira_free (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1));
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1) = vec;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1) = size;
+ }
+ ALLOCNO_MIN (a1) -= added_head_nw * IRA_INT_BITS;
+ }
+ else if (ALLOCNO_MAX (a1) < id)
+ {
+ nw = (id - ALLOCNO_MIN (a1)) / IRA_INT_BITS + 1;
+ size = nw * sizeof (IRA_INT_TYPE);
+ if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1) < size)
+ {
+ /* Expand tail of the bit vector. */
+ size = (3 * nw / 2 + 1) * sizeof (IRA_INT_TYPE);
+ vec = (IRA_INT_TYPE *) ira_allocate (size);
+ memcpy (vec, ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1),
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1));
+ memset ((char *) vec + ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1),
+ 0, size - ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1));
+ ira_free (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1));
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a1) = vec;
+ ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a1) = size;
+ }
+ ALLOCNO_MAX (a1) = id;
+ }
+ SET_ALLOCNO_SET_BIT (vec, id, ALLOCNO_MIN (a1), ALLOCNO_MAX (a1));
+ }
+}
+
+/* Add A1 to the conflicts of A2 and vise versa. */
+void
+ira_add_allocno_conflict (ira_allocno_t a1, ira_allocno_t a2)
+{
+ add_to_allocno_conflicts (a1, a2);
+ add_to_allocno_conflicts (a2, a1);
+}
+
+/* Clear all conflicts of allocno A. */
+static void
+clear_allocno_conflicts (ira_allocno_t a)
+{
+ if (ALLOCNO_CONFLICT_VEC_P (a))
+ {
+ ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = 0;
+ ((ira_allocno_t *) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a))[0] = NULL;
+ }
+ else if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE (a) != 0)
+ {
+ int nw;
+
+ nw = (ALLOCNO_MAX (a) - ALLOCNO_MIN (a)) / IRA_INT_BITS + 1;
+ memset (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a), 0,
+ nw * sizeof (IRA_INT_TYPE));
+ }
+}
+
+/* The array used to find duplications in conflict vectors of
+ allocnos. */
+static int *allocno_conflict_check;
+
+/* The value used to mark allocation presence in conflict vector of
+ the current allocno. */
+static int curr_allocno_conflict_check_tick;
+
+/* Remove duplications in conflict vector of A. */
+static void
+compress_allocno_conflict_vec (ira_allocno_t a)
+{
+ ira_allocno_t *vec, conflict_a;
+ int i, j;
+
+ ira_assert (ALLOCNO_CONFLICT_VEC_P (a));
+ vec = (ira_allocno_t *) ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a);
+ curr_allocno_conflict_check_tick++;
+ for (i = j = 0; (conflict_a = vec[i]) != NULL; i++)
+ {
+ if (allocno_conflict_check[ALLOCNO_NUM (conflict_a)]
+ != curr_allocno_conflict_check_tick)
+ {
+ allocno_conflict_check[ALLOCNO_NUM (conflict_a)]
+ = curr_allocno_conflict_check_tick;
+ vec[j++] = conflict_a;
+ }
+ }
+ ALLOCNO_CONFLICT_ALLOCNOS_NUM (a) = j;
+ vec[j] = NULL;
+}
+
+/* Remove duplications in conflict vectors of all allocnos. */
+static void
+compress_conflict_vecs (void)
+{
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ allocno_conflict_check
+ = (int *) ira_allocate (sizeof (int) * ira_allocnos_num);
+ memset (allocno_conflict_check, 0, sizeof (int) * ira_allocnos_num);
+ curr_allocno_conflict_check_tick = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ if (ALLOCNO_CONFLICT_VEC_P (a))
+ compress_allocno_conflict_vec (a);
+ ira_free (allocno_conflict_check);
+}
+
+/* This recursive function outputs allocno A and if it is a cap the
+ function outputs its members. */
+void
+ira_print_expanded_allocno (ira_allocno_t a)
+{
+ basic_block bb;
+
+ fprintf (ira_dump_file, " a%d(r%d", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
+ if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
+ fprintf (ira_dump_file, ",b%d", bb->index);
+ else
+ fprintf (ira_dump_file, ",l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num);
+ if (ALLOCNO_CAP_MEMBER (a) != NULL)
+ {
+ fprintf (ira_dump_file, ":");
+ ira_print_expanded_allocno (ALLOCNO_CAP_MEMBER (a));
+ }
+ fprintf (ira_dump_file, ")");
+}
+
+/* Create and return the cap representing allocno A in the
+ parent loop. */
+static ira_allocno_t
+create_cap_allocno (ira_allocno_t a)
+{
+ ira_allocno_t cap;
+ ira_loop_tree_node_t parent;
+ enum reg_class cover_class;
+
+ ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (a) == a
+ && ALLOCNO_NEXT_COALESCED_ALLOCNO (a) == a);
+ parent = ALLOCNO_LOOP_TREE_NODE (a)->parent;
+ cap = ira_create_allocno (ALLOCNO_REGNO (a), true, parent);
+ ALLOCNO_MODE (cap) = ALLOCNO_MODE (a);
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ ira_set_allocno_cover_class (cap, cover_class);
+ ALLOCNO_AVAILABLE_REGS_NUM (cap) = ALLOCNO_AVAILABLE_REGS_NUM (a);
+ ALLOCNO_CAP_MEMBER (cap) = a;
+ bitmap_set_bit (parent->mentioned_allocnos, ALLOCNO_NUM (cap));
+ ALLOCNO_CAP (a) = cap;
+ ALLOCNO_COVER_CLASS_COST (cap) = ALLOCNO_COVER_CLASS_COST (a);
+ ALLOCNO_MEMORY_COST (cap) = ALLOCNO_MEMORY_COST (a);
+ ALLOCNO_UPDATED_MEMORY_COST (cap) = ALLOCNO_UPDATED_MEMORY_COST (a);
+ ira_allocate_and_copy_costs
+ (&ALLOCNO_HARD_REG_COSTS (cap), cover_class, ALLOCNO_HARD_REG_COSTS (a));
+ ira_allocate_and_copy_costs
+ (&ALLOCNO_CONFLICT_HARD_REG_COSTS (cap), cover_class,
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
+ ALLOCNO_NREFS (cap) = ALLOCNO_NREFS (a);
+ ALLOCNO_FREQ (cap) = ALLOCNO_FREQ (a);
+ ALLOCNO_CALL_FREQ (cap) = ALLOCNO_CALL_FREQ (a);
+ IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (cap),
+ ALLOCNO_CONFLICT_HARD_REGS (a));
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (cap),
+ ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ ALLOCNO_CALLS_CROSSED_NUM (cap) = ALLOCNO_CALLS_CROSSED_NUM (a);
+#ifdef STACK_REGS
+ ALLOCNO_NO_STACK_REG_P (cap) = ALLOCNO_NO_STACK_REG_P (a);
+ ALLOCNO_TOTAL_NO_STACK_REG_P (cap) = ALLOCNO_TOTAL_NO_STACK_REG_P (a);
+#endif
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Creating cap ");
+ ira_print_expanded_allocno (cap);
+ fprintf (ira_dump_file, "\n");
+ }
+ return cap;
+}
+
+/* Create and return allocno live range with given attributes. */
+allocno_live_range_t
+ira_create_allocno_live_range (ira_allocno_t a, int start, int finish,
+ allocno_live_range_t next)
+{
+ allocno_live_range_t p;
+
+ p = (allocno_live_range_t) pool_alloc (allocno_live_range_pool);
+ p->allocno = a;
+ p->start = start;
+ p->finish = finish;
+ p->next = next;
+ return p;
+}
+
+/* Copy allocno live range R and return the result. */
+static allocno_live_range_t
+copy_allocno_live_range (allocno_live_range_t r)
+{
+ allocno_live_range_t p;
+
+ p = (allocno_live_range_t) pool_alloc (allocno_live_range_pool);
+ *p = *r;
+ return p;
+}
+
+/* Copy allocno live range list given by its head R and return the
+ result. */
+static allocno_live_range_t
+copy_allocno_live_range_list (allocno_live_range_t r)
+{
+ allocno_live_range_t p, first, last;
+
+ if (r == NULL)
+ return NULL;
+ for (first = last = NULL; r != NULL; r = r->next)
+ {
+ p = copy_allocno_live_range (r);
+ if (first == NULL)
+ first = p;
+ else
+ last->next = p;
+ last = p;
+ }
+ return first;
+}
+
+/* Free allocno live range R. */
+void
+ira_finish_allocno_live_range (allocno_live_range_t r)
+{
+ pool_free (allocno_live_range_pool, r);
+}
+
+/* Free updated register costs of allocno A. */
+void
+ira_free_allocno_updated_costs (ira_allocno_t a)
+{
+ enum reg_class cover_class;
+
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ if (ALLOCNO_UPDATED_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_UPDATED_HARD_REG_COSTS (a), cover_class);
+ ALLOCNO_UPDATED_HARD_REG_COSTS (a) = NULL;
+ if (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a),
+ cover_class);
+ ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) = NULL;
+}
+
+/* Free the memory allocated for allocno A. */
+static void
+finish_allocno (ira_allocno_t a)
+{
+ allocno_live_range_t r, next_r;
+ enum reg_class cover_class = ALLOCNO_COVER_CLASS (a);
+
+ ira_allocnos[ALLOCNO_NUM (a)] = NULL;
+ ira_conflict_id_allocno_map[ALLOCNO_CONFLICT_ID (a)] = NULL;
+ if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a) != NULL)
+ ira_free (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (a));
+ if (ALLOCNO_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_HARD_REG_COSTS (a), cover_class);
+ if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_CONFLICT_HARD_REG_COSTS (a), cover_class);
+ if (ALLOCNO_UPDATED_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_UPDATED_HARD_REG_COSTS (a), cover_class);
+ if (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) != NULL)
+ ira_free_cost_vector (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a),
+ cover_class);
+ for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = next_r)
+ {
+ next_r = r->next;
+ ira_finish_allocno_live_range (r);
+ }
+ pool_free (allocno_pool, a);
+}
+
+/* Free the memory allocated for all allocnos. */
+static void
+finish_allocnos (void)
+{
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ finish_allocno (a);
+ ira_free (ira_regno_allocno_map);
+ VEC_free (ira_allocno_t, heap, ira_conflict_id_allocno_map_vec);
+ VEC_free (ira_allocno_t, heap, allocno_vec);
+ free_alloc_pool (allocno_pool);
+ free_alloc_pool (allocno_live_range_pool);
+}
+
+
+
+/* Pools for copies. */
+static alloc_pool copy_pool;
+
+/* Vec containing references to all created copies. It is a
+ container of array ira_copies. */
+static VEC(ira_copy_t,heap) *copy_vec;
+
+/* The function initializes data concerning allocno copies. */
+static void
+initiate_copies (void)
+{
+ copy_pool
+ = create_alloc_pool ("copies", sizeof (struct ira_allocno_copy), 100);
+ copy_vec = VEC_alloc (ira_copy_t, heap, get_max_uid ());
+ ira_copies = NULL;
+ ira_copies_num = 0;
+}
+
+/* Return copy connecting A1 and A2 and originated from INSN of
+ LOOP_TREE_NODE if any. */
+static ira_copy_t
+find_allocno_copy (ira_allocno_t a1, ira_allocno_t a2, rtx insn,
+ ira_loop_tree_node_t loop_tree_node)
+{
+ ira_copy_t cp, next_cp;
+ ira_allocno_t another_a;
+
+ for (cp = ALLOCNO_COPIES (a1); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == a1)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_a = cp->second;
+ }
+ else if (cp->second == a1)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_a = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ if (another_a == a2 && cp->insn == insn
+ && cp->loop_tree_node == loop_tree_node)
+ return cp;
+ }
+ return NULL;
+}
+
+/* Create and return copy with given attributes LOOP_TREE_NODE, FIRST,
+ SECOND, FREQ, and INSN. */
+ira_copy_t
+ira_create_copy (ira_allocno_t first, ira_allocno_t second, int freq, rtx insn,
+ ira_loop_tree_node_t loop_tree_node)
+{
+ ira_copy_t cp;
+
+ cp = (ira_copy_t) pool_alloc (copy_pool);
+ cp->num = ira_copies_num;
+ cp->first = first;
+ cp->second = second;
+ cp->freq = freq;
+ cp->insn = insn;
+ cp->loop_tree_node = loop_tree_node;
+ VEC_safe_push (ira_copy_t, heap, copy_vec, cp);
+ ira_copies = VEC_address (ira_copy_t, copy_vec);
+ ira_copies_num = VEC_length (ira_copy_t, copy_vec);
+ return cp;
+}
+
+/* Attach a copy CP to allocnos involved into the copy. */
+void
+ira_add_allocno_copy_to_list (ira_copy_t cp)
+{
+ ira_allocno_t first = cp->first, second = cp->second;
+
+ cp->prev_first_allocno_copy = NULL;
+ cp->prev_second_allocno_copy = NULL;
+ cp->next_first_allocno_copy = ALLOCNO_COPIES (first);
+ if (cp->next_first_allocno_copy != NULL)
+ {
+ if (cp->next_first_allocno_copy->first == first)
+ cp->next_first_allocno_copy->prev_first_allocno_copy = cp;
+ else
+ cp->next_first_allocno_copy->prev_second_allocno_copy = cp;
+ }
+ cp->next_second_allocno_copy = ALLOCNO_COPIES (second);
+ if (cp->next_second_allocno_copy != NULL)
+ {
+ if (cp->next_second_allocno_copy->second == second)
+ cp->next_second_allocno_copy->prev_second_allocno_copy = cp;
+ else
+ cp->next_second_allocno_copy->prev_first_allocno_copy = cp;
+ }
+ ALLOCNO_COPIES (first) = cp;
+ ALLOCNO_COPIES (second) = cp;
+}
+
+/* Detach a copy CP from allocnos involved into the copy. */
+void
+ira_remove_allocno_copy_from_list (ira_copy_t cp)
+{
+ ira_allocno_t first = cp->first, second = cp->second;
+ ira_copy_t prev, next;
+
+ next = cp->next_first_allocno_copy;
+ prev = cp->prev_first_allocno_copy;
+ if (prev == NULL)
+ ALLOCNO_COPIES (first) = next;
+ else if (prev->first == first)
+ prev->next_first_allocno_copy = next;
+ else
+ prev->next_second_allocno_copy = next;
+ if (next != NULL)
+ {
+ if (next->first == first)
+ next->prev_first_allocno_copy = prev;
+ else
+ next->prev_second_allocno_copy = prev;
+ }
+ cp->prev_first_allocno_copy = cp->next_first_allocno_copy = NULL;
+
+ next = cp->next_second_allocno_copy;
+ prev = cp->prev_second_allocno_copy;
+ if (prev == NULL)
+ ALLOCNO_COPIES (second) = next;
+ else if (prev->second == second)
+ prev->next_second_allocno_copy = next;
+ else
+ prev->next_first_allocno_copy = next;
+ if (next != NULL)
+ {
+ if (next->second == second)
+ next->prev_second_allocno_copy = prev;
+ else
+ next->prev_first_allocno_copy = prev;
+ }
+ cp->prev_second_allocno_copy = cp->next_second_allocno_copy = NULL;
+}
+
+/* Make a copy CP a canonical copy where number of the
+ first allocno is less than the second one. */
+void
+ira_swap_allocno_copy_ends_if_necessary (ira_copy_t cp)
+{
+ ira_allocno_t temp;
+ ira_copy_t temp_cp;
+
+ if (ALLOCNO_NUM (cp->first) <= ALLOCNO_NUM (cp->second))
+ return;
+
+ temp = cp->first;
+ cp->first = cp->second;
+ cp->second = temp;
+
+ temp_cp = cp->prev_first_allocno_copy;
+ cp->prev_first_allocno_copy = cp->prev_second_allocno_copy;
+ cp->prev_second_allocno_copy = temp_cp;
+
+ temp_cp = cp->next_first_allocno_copy;
+ cp->next_first_allocno_copy = cp->next_second_allocno_copy;
+ cp->next_second_allocno_copy = temp_cp;
+}
+
+/* Create (or update frequency if the copy already exists) and return
+ the copy of allocnos FIRST and SECOND with frequency FREQ
+ corresponding to move insn INSN (if any) and originated from
+ LOOP_TREE_NODE. */
+ira_copy_t
+ira_add_allocno_copy (ira_allocno_t first, ira_allocno_t second, int freq,
+ rtx insn, ira_loop_tree_node_t loop_tree_node)
+{
+ ira_copy_t cp;
+
+ if ((cp = find_allocno_copy (first, second, insn, loop_tree_node)) != NULL)
+ {
+ cp->freq += freq;
+ return cp;
+ }
+ cp = ira_create_copy (first, second, freq, insn, loop_tree_node);
+ ira_assert (first != NULL && second != NULL);
+ ira_add_allocno_copy_to_list (cp);
+ ira_swap_allocno_copy_ends_if_necessary (cp);
+ return cp;
+}
+
+/* Print info about copies involving allocno A into file F. */
+static void
+print_allocno_copies (FILE *f, ira_allocno_t a)
+{
+ ira_allocno_t another_a;
+ ira_copy_t cp, next_cp;
+
+ fprintf (f, " a%d(r%d):", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
+ for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == a)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_a = cp->second;
+ }
+ else if (cp->second == a)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_a = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ fprintf (f, " cp%d:a%d(r%d)@%d", cp->num,
+ ALLOCNO_NUM (another_a), ALLOCNO_REGNO (another_a), cp->freq);
+ }
+ fprintf (f, "\n");
+}
+
+/* Print info about copies involving allocno A into stderr. */
+void
+ira_debug_allocno_copies (ira_allocno_t a)
+{
+ print_allocno_copies (stderr, a);
+}
+
+/* The function frees memory allocated for copy CP. */
+static void
+finish_copy (ira_copy_t cp)
+{
+ pool_free (copy_pool, cp);
+}
+
+
+/* Free memory allocated for all copies. */
+static void
+finish_copies (void)
+{
+ ira_copy_t cp;
+ ira_copy_iterator ci;
+
+ FOR_EACH_COPY (cp, ci)
+ finish_copy (cp);
+ VEC_free (ira_copy_t, heap, copy_vec);
+ free_alloc_pool (copy_pool);
+}
+
+
+
+/* Pools for cost vectors. It is defined only for cover classes. */
+static alloc_pool cost_vector_pool[N_REG_CLASSES];
+
+/* The function initiates work with hard register cost vectors. It
+ creates allocation pool for each cover class. */
+static void
+initiate_cost_vectors (void)
+{
+ int i;
+ enum reg_class cover_class;
+
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ cost_vector_pool[cover_class]
+ = create_alloc_pool ("cost vectors",
+ sizeof (int)
+ * ira_class_hard_regs_num[cover_class],
+ 100);
+ }
+}
+
+/* Allocate and return a cost vector VEC for COVER_CLASS. */
+int *
+ira_allocate_cost_vector (enum reg_class cover_class)
+{
+ return (int *) pool_alloc (cost_vector_pool[cover_class]);
+}
+
+/* Free a cost vector VEC for COVER_CLASS. */
+void
+ira_free_cost_vector (int *vec, enum reg_class cover_class)
+{
+ ira_assert (vec != NULL);
+ pool_free (cost_vector_pool[cover_class], vec);
+}
+
+/* Finish work with hard register cost vectors. Release allocation
+ pool for each cover class. */
+static void
+finish_cost_vectors (void)
+{
+ int i;
+ enum reg_class cover_class;
+
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ free_alloc_pool (cost_vector_pool[cover_class]);
+ }
+}
+
+
+
+/* The current loop tree node and its regno allocno map. */
+ira_loop_tree_node_t ira_curr_loop_tree_node;
+ira_allocno_t *ira_curr_regno_allocno_map;
+
+/* This recursive function traverses loop tree with root LOOP_NODE
+ calling non-null functions PREORDER_FUNC and POSTORDER_FUNC
+ correspondingly in preorder and postorder. The function sets up
+ IRA_CURR_LOOP_TREE_NODE and IRA_CURR_REGNO_ALLOCNO_MAP. If BB_P,
+ basic block nodes of LOOP_NODE is also processed (before its
+ subloop nodes). */
+void
+ira_traverse_loop_tree (bool bb_p, ira_loop_tree_node_t loop_node,
+ void (*preorder_func) (ira_loop_tree_node_t),
+ void (*postorder_func) (ira_loop_tree_node_t))
+{
+ ira_loop_tree_node_t subloop_node;
+
+ ira_assert (loop_node->bb == NULL);
+ ira_curr_loop_tree_node = loop_node;
+ ira_curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map;
+
+ if (preorder_func != NULL)
+ (*preorder_func) (loop_node);
+
+ if (bb_p)
+ for (subloop_node = loop_node->children;
+ subloop_node != NULL;
+ subloop_node = subloop_node->next)
+ if (subloop_node->bb != NULL)
+ {
+ if (preorder_func != NULL)
+ (*preorder_func) (subloop_node);
+
+ if (postorder_func != NULL)
+ (*postorder_func) (subloop_node);
+ }
+
+ for (subloop_node = loop_node->subloops;
+ subloop_node != NULL;
+ subloop_node = subloop_node->subloop_next)
+ {
+ ira_assert (subloop_node->bb == NULL);
+ ira_traverse_loop_tree (bb_p, subloop_node,
+ preorder_func, postorder_func);
+ }
+
+ ira_curr_loop_tree_node = loop_node;
+ ira_curr_regno_allocno_map = ira_curr_loop_tree_node->regno_allocno_map;
+
+ if (postorder_func != NULL)
+ (*postorder_func) (loop_node);
+}
+
+
+
+/* The basic block currently being processed. */
+static basic_block curr_bb;
+
+/* This recursive function creates allocnos corresponding to
+ pseudo-registers containing in X. True OUTPUT_P means that X is
+ a lvalue. */
+static void
+create_insn_allocnos (rtx x, bool output_p)
+{
+ int i, j;
+ const char *fmt;
+ enum rtx_code code = GET_CODE (x);
+
+ if (code == REG)
+ {
+ int regno;
+
+ if ((regno = REGNO (x)) >= FIRST_PSEUDO_REGISTER)
+ {
+ ira_allocno_t a;
+
+ if ((a = ira_curr_regno_allocno_map[regno]) == NULL)
+ a = ira_create_allocno (regno, false, ira_curr_loop_tree_node);
+
+ ALLOCNO_NREFS (a)++;
+ ALLOCNO_FREQ (a) += REG_FREQ_FROM_BB (curr_bb);
+ bitmap_set_bit (ira_curr_loop_tree_node->mentioned_allocnos,
+ ALLOCNO_NUM (a));
+ if (output_p)
+ bitmap_set_bit (ira_curr_loop_tree_node->modified_regnos, regno);
+ }
+ return;
+ }
+ else if (code == SET)
+ {
+ create_insn_allocnos (SET_DEST (x), true);
+ create_insn_allocnos (SET_SRC (x), false);
+ return;
+ }
+ else if (code == CLOBBER)
+ {
+ create_insn_allocnos (XEXP (x, 0), true);
+ return;
+ }
+ else if (code == MEM)
+ {
+ create_insn_allocnos (XEXP (x, 0), false);
+ return;
+ }
+ else if (code == PRE_DEC || code == POST_DEC || code == PRE_INC ||
+ code == POST_INC || code == POST_MODIFY || code == PRE_MODIFY)
+ {
+ create_insn_allocnos (XEXP (x, 0), true);
+ create_insn_allocnos (XEXP (x, 0), false);
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ create_insn_allocnos (XEXP (x, i), output_p);
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ create_insn_allocnos (XVECEXP (x, i, j), output_p);
+ }
+}
+
+/* Create allocnos corresponding to pseudo-registers living in the
+ basic block represented by the corresponding loop tree node
+ BB_NODE. */
+static void
+create_bb_allocnos (ira_loop_tree_node_t bb_node)
+{
+ basic_block bb;
+ rtx insn;
+ unsigned int i;
+ bitmap_iterator bi;
+
+ curr_bb = bb = bb_node->bb;
+ ira_assert (bb != NULL);
+ FOR_BB_INSNS (bb, insn)
+ if (INSN_P (insn))
+ create_insn_allocnos (PATTERN (insn), false);
+ /* It might be a allocno living through from one subloop to
+ another. */
+ EXECUTE_IF_SET_IN_REG_SET (DF_LR_IN (bb), FIRST_PSEUDO_REGISTER, i, bi)
+ if (ira_curr_regno_allocno_map[i] == NULL)
+ ira_create_allocno (i, false, ira_curr_loop_tree_node);
+}
+
+/* Create allocnos corresponding to pseudo-registers living on edge E
+ (a loop entry or exit). Also mark the allocnos as living on the
+ loop border. */
+static void
+create_loop_allocnos (edge e)
+{
+ unsigned int i;
+ bitmap live_in_regs, border_allocnos;
+ bitmap_iterator bi;
+ ira_loop_tree_node_t parent;
+
+ live_in_regs = DF_LR_IN (e->dest);
+ border_allocnos = ira_curr_loop_tree_node->border_allocnos;
+ EXECUTE_IF_SET_IN_REG_SET (DF_LR_OUT (e->src),
+ FIRST_PSEUDO_REGISTER, i, bi)
+ if (bitmap_bit_p (live_in_regs, i))
+ {
+ if (ira_curr_regno_allocno_map[i] == NULL)
+ {
+ /* The order of creations is important for right
+ ira_regno_allocno_map. */
+ if ((parent = ira_curr_loop_tree_node->parent) != NULL
+ && parent->regno_allocno_map[i] == NULL)
+ ira_create_allocno (i, false, parent);
+ ira_create_allocno (i, false, ira_curr_loop_tree_node);
+ }
+ bitmap_set_bit (border_allocnos,
+ ALLOCNO_NUM (ira_curr_regno_allocno_map[i]));
+ }
+}
+
+/* Create allocnos corresponding to pseudo-registers living in loop
+ represented by the corresponding loop tree node LOOP_NODE. This
+ function is called by ira_traverse_loop_tree. */
+static void
+create_loop_tree_node_allocnos (ira_loop_tree_node_t loop_node)
+{
+ if (loop_node->bb != NULL)
+ create_bb_allocnos (loop_node);
+ else if (loop_node != ira_loop_tree_root)
+ {
+ int i;
+ edge_iterator ei;
+ edge e;
+ VEC (edge, heap) *edges;
+
+ FOR_EACH_EDGE (e, ei, loop_node->loop->header->preds)
+ if (e->src != loop_node->loop->latch)
+ create_loop_allocnos (e);
+
+ edges = get_loop_exit_edges (loop_node->loop);
+ for (i = 0; VEC_iterate (edge, edges, i, e); i++)
+ create_loop_allocnos (e);
+ VEC_free (edge, heap, edges);
+ }
+}
+
+/* Propagate information about allocnos modified inside the loop given
+ by its LOOP_TREE_NODE to its parent. */
+static void
+propagate_modified_regnos (ira_loop_tree_node_t loop_tree_node)
+{
+ if (loop_tree_node == ira_loop_tree_root)
+ return;
+ ira_assert (loop_tree_node->bb == NULL);
+ bitmap_ior_into (loop_tree_node->parent->modified_regnos,
+ loop_tree_node->modified_regnos);
+}
+
+/* Propagate new info about allocno A (see comments about accumulated
+ info in allocno definition) to the corresponding allocno on upper
+ loop tree level. So allocnos on upper levels accumulate
+ information about the corresponding allocnos in nested regions.
+ The new info means allocno info finally calculated in this
+ file. */
+static void
+propagate_allocno_info (void)
+{
+ int i;
+ ira_allocno_t a, parent_a;
+ ira_loop_tree_node_t parent;
+ enum reg_class cover_class;
+
+ if (flag_ira_algorithm != IRA_ALGORITHM_REGIONAL
+ && flag_ira_algorithm != IRA_ALGORITHM_MIXED)
+ return;
+ 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))
+ if ((parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) != NULL
+ && (parent_a = parent->regno_allocno_map[i]) != NULL
+ /* There are no caps yet at this point. So use
+ border_allocnos to find allocnos for the propagation. */
+ && bitmap_bit_p (ALLOCNO_LOOP_TREE_NODE (a)->border_allocnos,
+ ALLOCNO_NUM (a)))
+ {
+ ALLOCNO_NREFS (parent_a) += ALLOCNO_NREFS (a);
+ ALLOCNO_FREQ (parent_a) += ALLOCNO_FREQ (a);
+ ALLOCNO_CALL_FREQ (parent_a) += ALLOCNO_CALL_FREQ (a);
+#ifdef STACK_REGS
+ if (ALLOCNO_TOTAL_NO_STACK_REG_P (a))
+ ALLOCNO_TOTAL_NO_STACK_REG_P (parent_a) = true;
+#endif
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (parent_a),
+ ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ ALLOCNO_CALLS_CROSSED_NUM (parent_a)
+ += ALLOCNO_CALLS_CROSSED_NUM (a);
+ ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
+ += ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ ira_assert (cover_class == ALLOCNO_COVER_CLASS (parent_a));
+ ira_allocate_and_accumulate_costs
+ (&ALLOCNO_HARD_REG_COSTS (parent_a), cover_class,
+ ALLOCNO_HARD_REG_COSTS (a));
+ ira_allocate_and_accumulate_costs
+ (&ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a),
+ cover_class,
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
+ ALLOCNO_COVER_CLASS_COST (parent_a)
+ += ALLOCNO_COVER_CLASS_COST (a);
+ ALLOCNO_MEMORY_COST (parent_a) += ALLOCNO_MEMORY_COST (a);
+ ALLOCNO_UPDATED_MEMORY_COST (parent_a)
+ += ALLOCNO_UPDATED_MEMORY_COST (a);
+ }
+}
+
+/* Create allocnos corresponding to pseudo-registers in the current
+ function. Traverse the loop tree for this. */
+static void
+create_allocnos (void)
+{
+ /* We need to process BB first to correctly link allocnos by member
+ next_regno_allocno. */
+ ira_traverse_loop_tree (true, ira_loop_tree_root,
+ create_loop_tree_node_allocnos, NULL);
+ if (optimize)
+ ira_traverse_loop_tree (false, ira_loop_tree_root, NULL,
+ propagate_modified_regnos);
+}
+
+
+
+/* The page contains function to remove some regions from a separate
+ register allocation. We remove regions whose separate allocation
+ will hardly improve the result. As a result we speed up regional
+ register allocation. */
+
+/* Merge ranges R1 and R2 and returns the result. The function
+ maintains the order of ranges and tries to minimize number of the
+ result ranges. */
+static allocno_live_range_t
+merge_ranges (allocno_live_range_t r1, allocno_live_range_t r2)
+{
+ allocno_live_range_t first, last, temp;
+
+ if (r1 == NULL)
+ return r2;
+ if (r2 == NULL)
+ return r1;
+ for (first = last = NULL; r1 != NULL && r2 != NULL;)
+ {
+ if (r1->start < r2->start)
+ {
+ temp = r1;
+ r1 = r2;
+ r2 = temp;
+ }
+ if (r1->start <= r2->finish + 1)
+ {
+ /* Intersected ranges: merge r1 and r2 into r1. */
+ r1->start = r2->start;
+ if (r1->finish < r2->finish)
+ r1->finish = r2->finish;
+ temp = r2;
+ r2 = r2->next;
+ ira_finish_allocno_live_range (temp);
+ if (r2 == NULL)
+ {
+ /* To try to merge with subsequent ranges in r1. */
+ r2 = r1->next;
+ r1->next = NULL;
+ }
+ }
+ else
+ {
+ /* Add r1 to the result. */
+ if (first == NULL)
+ first = last = r1;
+ else
+ {
+ last->next = r1;
+ last = r1;
+ }
+ r1 = r1->next;
+ if (r1 == NULL)
+ {
+ /* To try to merge with subsequent ranges in r2. */
+ r1 = r2->next;
+ r2->next = NULL;
+ }
+ }
+ }
+ if (r1 != NULL)
+ {
+ if (first == NULL)
+ first = r1;
+ else
+ last->next = r1;
+ ira_assert (r1->next == NULL);
+ }
+ else if (r2 != NULL)
+ {
+ if (first == NULL)
+ first = r2;
+ else
+ last->next = r2;
+ ira_assert (r2->next == NULL);
+ }
+ else
+ {
+ ira_assert (last->next == NULL);
+ }
+ return first;
+}
+
+/* The function changes allocno in range list given by R onto A. */
+static void
+change_allocno_in_range_list (allocno_live_range_t r, ira_allocno_t a)
+{
+ for (; r != NULL; r = r->next)
+ r->allocno = a;
+}
+
+/* Return TRUE if NODE represents a loop with low register
+ pressure. */
+static bool
+low_pressure_loop_node_p (ira_loop_tree_node_t node)
+{
+ int i;
+ enum reg_class cover_class;
+
+ if (node->bb != NULL)
+ return false;
+
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ if (node->reg_pressure[cover_class]
+ > ira_available_class_regs[cover_class])
+ return false;
+ }
+ return true;
+}
+
+/* Return TRUE if NODE represents a loop with should be removed from
+ regional allocation. We remove a loop with low register pressure
+ inside another loop with register pressure. In this case a
+ separate allocation of the loop hardly helps (for irregular
+ register file architecture it could help by choosing a better hard
+ register in the loop but we prefer faster allocation even in this
+ case). */
+static bool
+loop_node_to_be_removed_p (ira_loop_tree_node_t node)
+{
+ return (node->parent != NULL && low_pressure_loop_node_p (node->parent)
+ && low_pressure_loop_node_p (node));
+}
+
+/* Definition of vector of loop tree nodes. */
+DEF_VEC_P(ira_loop_tree_node_t);
+DEF_VEC_ALLOC_P(ira_loop_tree_node_t, heap);
+
+/* Vec containing references to all removed loop tree nodes. */
+static VEC(ira_loop_tree_node_t,heap) *removed_loop_vec;
+
+/* Vec containing references to all children of loop tree nodes. */
+static VEC(ira_loop_tree_node_t,heap) *children_vec;
+
+/* Remove subregions of NODE if their separate allocation will not
+ improve the result. */
+static void
+remove_uneccesary_loop_nodes_from_loop_tree (ira_loop_tree_node_t node)
+{
+ unsigned int start;
+ bool remove_p;
+ ira_loop_tree_node_t subnode;
+
+ remove_p = loop_node_to_be_removed_p (node);
+ if (! remove_p)
+ VEC_safe_push (ira_loop_tree_node_t, heap, children_vec, node);
+ start = VEC_length (ira_loop_tree_node_t, children_vec);
+ for (subnode = node->children; subnode != NULL; subnode = subnode->next)
+ if (subnode->bb == NULL)
+ remove_uneccesary_loop_nodes_from_loop_tree (subnode);
+ else
+ VEC_safe_push (ira_loop_tree_node_t, heap, children_vec, subnode);
+ node->children = node->subloops = NULL;
+ if (remove_p)
+ {
+ VEC_safe_push (ira_loop_tree_node_t, heap, removed_loop_vec, node);
+ return;
+ }
+ while (VEC_length (ira_loop_tree_node_t, children_vec) > start)
+ {
+ subnode = VEC_pop (ira_loop_tree_node_t, children_vec);
+ subnode->parent = node;
+ subnode->next = node->children;
+ node->children = subnode;
+ if (subnode->bb == NULL)
+ {
+ subnode->subloop_next = node->subloops;
+ node->subloops = subnode;
+ }
+ }
+}
+
+/* Remove allocnos from loops removed from the allocation
+ consideration. */
+static void
+remove_unnecessary_allocnos (void)
+{
+ int regno;
+ bool merged_p;
+ enum reg_class cover_class;
+ ira_allocno_t a, prev_a, next_a, parent_a;
+ ira_loop_tree_node_t a_node, parent;
+ allocno_live_range_t r;
+
+ merged_p = false;
+ for (regno = max_reg_num () - 1; regno >= FIRST_PSEUDO_REGISTER; regno--)
+ for (prev_a = NULL, a = ira_regno_allocno_map[regno];
+ a != NULL;
+ a = next_a)
+ {
+ next_a = ALLOCNO_NEXT_REGNO_ALLOCNO (a);
+ a_node = ALLOCNO_LOOP_TREE_NODE (a);
+ if (! loop_node_to_be_removed_p (a_node))
+ prev_a = a;
+ else
+ {
+ for (parent = a_node->parent;
+ (parent_a = parent->regno_allocno_map[regno]) == NULL
+ && loop_node_to_be_removed_p (parent);
+ parent = parent->parent)
+ ;
+ if (parent_a == NULL)
+ {
+ /* There are no allocnos with the same regno in upper
+ region -- just move the allocno to the upper
+ region. */
+ prev_a = a;
+ ALLOCNO_LOOP_TREE_NODE (a) = parent;
+ parent->regno_allocno_map[regno] = a;
+ bitmap_set_bit (parent->mentioned_allocnos, ALLOCNO_NUM (a));
+ }
+ else
+ {
+ /* Remove the allocno and update info of allocno in
+ the upper region. */
+ if (prev_a == NULL)
+ ira_regno_allocno_map[regno] = next_a;
+ else
+ ALLOCNO_NEXT_REGNO_ALLOCNO (prev_a) = next_a;
+ r = ALLOCNO_LIVE_RANGES (a);
+ change_allocno_in_range_list (r, parent_a);
+ ALLOCNO_LIVE_RANGES (parent_a)
+ = merge_ranges (r, ALLOCNO_LIVE_RANGES (parent_a));
+ merged_p = true;
+ ALLOCNO_LIVE_RANGES (a) = NULL;
+ IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (parent_a),
+ ALLOCNO_CONFLICT_HARD_REGS (a));
+#ifdef STACK_REGS
+ if (ALLOCNO_NO_STACK_REG_P (a))
+ ALLOCNO_NO_STACK_REG_P (parent_a) = true;
+#endif
+ ALLOCNO_NREFS (parent_a) += ALLOCNO_NREFS (a);
+ ALLOCNO_FREQ (parent_a) += ALLOCNO_FREQ (a);
+ ALLOCNO_CALL_FREQ (parent_a) += ALLOCNO_CALL_FREQ (a);
+ IOR_HARD_REG_SET
+ (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (parent_a),
+ ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ ALLOCNO_CALLS_CROSSED_NUM (parent_a)
+ += ALLOCNO_CALLS_CROSSED_NUM (a);
+ ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
+ += ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
+#ifdef STACK_REGS
+ if (ALLOCNO_TOTAL_NO_STACK_REG_P (a))
+ ALLOCNO_TOTAL_NO_STACK_REG_P (parent_a) = true;
+#endif
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ ira_assert (cover_class == ALLOCNO_COVER_CLASS (parent_a));
+ ira_allocate_and_accumulate_costs
+ (&ALLOCNO_HARD_REG_COSTS (parent_a), cover_class,
+ ALLOCNO_HARD_REG_COSTS (a));
+ ira_allocate_and_accumulate_costs
+ (&ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a),
+ cover_class,
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
+ ALLOCNO_COVER_CLASS_COST (parent_a)
+ += ALLOCNO_COVER_CLASS_COST (a);
+ ALLOCNO_MEMORY_COST (parent_a) += ALLOCNO_MEMORY_COST (a);
+ ALLOCNO_UPDATED_MEMORY_COST (parent_a)
+ += ALLOCNO_UPDATED_MEMORY_COST (a);
+ finish_allocno (a);
+ }
+ }
+ }
+ if (merged_p)
+ ira_rebuild_start_finish_chains ();
+}
+
+/* Remove loops from consideration. We remove loops for which a
+ separate allocation will not improve the result. We have to do
+ this after allocno creation and their costs and cover class
+ evaluation because only after that the register pressure can be
+ known and is calculated. */
+static void
+remove_unnecessary_regions (void)
+{
+ children_vec
+ = VEC_alloc (ira_loop_tree_node_t, heap,
+ last_basic_block + VEC_length (loop_p, ira_loops.larray));
+ removed_loop_vec
+ = VEC_alloc (ira_loop_tree_node_t, heap,
+ last_basic_block + VEC_length (loop_p, ira_loops.larray));
+ remove_uneccesary_loop_nodes_from_loop_tree (ira_loop_tree_root) ;
+ VEC_free (ira_loop_tree_node_t, heap, children_vec);
+ remove_unnecessary_allocnos ();
+ while (VEC_length (ira_loop_tree_node_t, removed_loop_vec) > 0)
+ finish_loop_tree_node (VEC_pop (ira_loop_tree_node_t, removed_loop_vec));
+ VEC_free (ira_loop_tree_node_t, heap, removed_loop_vec);
+}
+
+
+
+/* Set up minimal and maximal live range points for allocnos. */
+static void
+setup_min_max_allocno_live_range_point (void)
+{
+ int i;
+ ira_allocno_t a, parent_a, cap;
+ ira_allocno_iterator ai;
+ allocno_live_range_t r;
+ ira_loop_tree_node_t parent;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ r = ALLOCNO_LIVE_RANGES (a);
+ if (r == NULL)
+ continue;
+ ALLOCNO_MAX (a) = r->finish;
+ for (; r->next != NULL; r = r->next)
+ ;
+ ALLOCNO_MIN (a) = r->start;
+ }
+ 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))
+ {
+ if (ALLOCNO_MAX (a) < 0)
+ continue;
+ ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
+ /* Accumulation of range info. */
+ if (ALLOCNO_CAP (a) != NULL)
+ {
+ for (cap = ALLOCNO_CAP (a); cap != NULL; cap = ALLOCNO_CAP (cap))
+ {
+ if (ALLOCNO_MAX (cap) < ALLOCNO_MAX (a))
+ ALLOCNO_MAX (cap) = ALLOCNO_MAX (a);
+ if (ALLOCNO_MIN (cap) > ALLOCNO_MIN (a))
+ ALLOCNO_MIN (cap) = ALLOCNO_MIN (a);
+ }
+ continue;
+ }
+ if ((parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) == NULL)
+ continue;
+ parent_a = parent->regno_allocno_map[i];
+ if (ALLOCNO_MAX (parent_a) < ALLOCNO_MAX (a))
+ ALLOCNO_MAX (parent_a) = ALLOCNO_MAX (a);
+ if (ALLOCNO_MIN (parent_a) > ALLOCNO_MIN (a))
+ ALLOCNO_MIN (parent_a) = ALLOCNO_MIN (a);
+ }
+#ifdef ENABLE_IRA_CHECKING
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if ((0 <= ALLOCNO_MIN (a) && ALLOCNO_MIN (a) <= ira_max_point)
+ && (0 <= ALLOCNO_MAX (a) && ALLOCNO_MAX (a) <= ira_max_point))
+ continue;
+ gcc_unreachable ();
+ }
+#endif
+}
+
+/* Sort allocnos according to their live ranges. Allocnos with
+ smaller cover class are put first. Allocnos with the same cove
+ class are ordered according their start (min). Allocnos with the
+ same start are ordered according their finish (max). */
+static int
+allocno_range_compare_func (const void *v1p, const void *v2p)
+{
+ int diff;
+ ira_allocno_t a1 = *(const ira_allocno_t *) v1p;
+ ira_allocno_t a2 = *(const ira_allocno_t *) v2p;
+
+ if ((diff = ALLOCNO_COVER_CLASS (a1) - ALLOCNO_COVER_CLASS (a2)) != 0)
+ return diff;
+ if ((diff = ALLOCNO_MIN (a1) - ALLOCNO_MIN (a2)) != 0)
+ return diff;
+ if ((diff = ALLOCNO_MAX (a1) - ALLOCNO_MAX (a2)) != 0)
+ return diff;
+ return ALLOCNO_NUM (a1) - ALLOCNO_NUM (a2);
+}
+
+/* Sort ira_conflict_id_allocno_map and set up conflict id of
+ allocnos. */
+static void
+sort_conflict_id_allocno_map (void)
+{
+ int i, num;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ num = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ ira_conflict_id_allocno_map[num++] = a;
+ qsort (ira_conflict_id_allocno_map, num, sizeof (ira_allocno_t),
+ allocno_range_compare_func);
+ for (i = 0; i < num; i++)
+ if ((a = ira_conflict_id_allocno_map[i]) != NULL)
+ ALLOCNO_CONFLICT_ID (a) = i;
+ for (i = num; i < ira_allocnos_num; i++)
+ ira_conflict_id_allocno_map[i] = NULL;
+}
+
+/* Set up minimal and maximal conflict ids of allocnos with which
+ given allocno can conflict. */
+static void
+setup_min_max_conflict_allocno_ids (void)
+{
+ enum reg_class cover_class;
+ int i, j, min, max, start, finish, first_not_finished, filled_area_start;
+ int *live_range_min, *last_lived;
+ ira_allocno_t a;
+
+ live_range_min = (int *) ira_allocate (sizeof (int) * ira_allocnos_num);
+ cover_class = -1;
+ first_not_finished = -1;
+ for (i = 0; i < ira_allocnos_num; i++)
+ {
+ a = ira_conflict_id_allocno_map[i];
+ if (a == NULL)
+ continue;
+ if (cover_class != ALLOCNO_COVER_CLASS (a))
+ {
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ min = i;
+ first_not_finished = i;
+ }
+ else
+ {
+ start = ALLOCNO_MIN (a);
+ /* If we skip an allocno, the allocno with smaller ids will
+ be also skipped because of the secondary sorting the
+ range finishes (see function
+ allocno_range_compare_func). */
+ while (first_not_finished < i
+ && start > ALLOCNO_MAX (ira_conflict_id_allocno_map
+ [first_not_finished]))
+ first_not_finished++;
+ min = first_not_finished;
+ }
+ if (min == i)
+ /* We could increase min further in this case but it is good
+ enough. */
+ min++;
+ live_range_min[i] = ALLOCNO_MIN (a);
+ ALLOCNO_MIN (a) = min;
+ }
+ last_lived = (int *) ira_allocate (sizeof (int) * ira_max_point);
+ cover_class = -1;
+ filled_area_start = -1;
+ for (i = ira_allocnos_num - 1; i >= 0; i--)
+ {
+ a = ira_conflict_id_allocno_map[i];
+ if (a == NULL)
+ continue;
+ if (cover_class != ALLOCNO_COVER_CLASS (a))
+ {
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ for (j = 0; j < ira_max_point; j++)
+ last_lived[j] = -1;
+ filled_area_start = ira_max_point;
+ }
+ min = live_range_min[i];
+ finish = ALLOCNO_MAX (a);
+ max = last_lived[finish];
+ if (max < 0)
+ /* We could decrease max further in this case but it is good
+ enough. */
+ max = ALLOCNO_CONFLICT_ID (a) - 1;
+ ALLOCNO_MAX (a) = max;
+ /* In filling, we can go further A range finish to recognize
+ intersection quickly because if the finish of subsequently
+ processed allocno (it has smaller conflict id) range is
+ further A range finish than they are definitely intersected
+ (the reason for this is the allocnos with bigger conflict id
+ have their range starts not smaller than allocnos with
+ smaller ids. */
+ for (j = min; j < filled_area_start; j++)
+ last_lived[j] = i;
+ filled_area_start = min;
+ }
+ ira_free (last_lived);
+ ira_free (live_range_min);
+}
+
+
+
+static void
+create_caps (void)
+{
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+ ira_loop_tree_node_t loop_tree_node;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (ALLOCNO_LOOP_TREE_NODE (a) == ira_loop_tree_root)
+ continue;
+ if (ALLOCNO_CAP_MEMBER (a) != NULL)
+ create_cap_allocno (a);
+ else if (ALLOCNO_CAP (a) == NULL)
+ {
+ loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
+ if (!bitmap_bit_p (loop_tree_node->border_allocnos, ALLOCNO_NUM (a)))
+ create_cap_allocno (a);
+ }
+ }
+}
+
+
+
+/* The page contains code transforming more one region internal
+ representation (IR) to one region IR which is necessary for reload.
+ This transformation is called IR flattening. We might just rebuild
+ the IR for one region but we don't do it because it takes a lot of
+ time. */
+
+/* This recursive function returns immediate common dominator of two
+ loop tree nodes N1 and N2. */
+static ira_loop_tree_node_t
+common_loop_tree_node_dominator (ira_loop_tree_node_t n1,
+ ira_loop_tree_node_t n2)
+{
+ ira_assert (n1 != NULL && n2 != NULL);
+ if (n1 == n2)
+ return n1;
+ if (n1->level < n2->level)
+ return common_loop_tree_node_dominator (n1, n2->parent);
+ else if (n1->level > n2->level)
+ return common_loop_tree_node_dominator (n1->parent, n2);
+ else
+ return common_loop_tree_node_dominator (n1->parent, n2->parent);
+}
+
+/* Flatten the IR. In other words, this function transforms IR as if
+ it were built with one region (without loops). We could make it
+ much simpler by rebuilding IR with one region, but unfortunately it
+ takes a lot of time. MAX_REGNO_BEFORE_EMIT and
+ IRA_MAX_POINT_BEFORE_EMIT are correspondingly MAX_REG_NUM () and
+ IRA_MAX_POINT before emitting insns on the loop borders. */
+void
+ira_flattening (int max_regno_before_emit, int ira_max_point_before_emit)
+{
+ int i, j, num;
+ bool propagate_p, stop_p, keep_p;
+ int hard_regs_num;
+ bool new_pseudos_p, merged_p;
+ unsigned int n;
+ enum reg_class cover_class;
+ ira_allocno_t a, parent_a, first, second, node_first, node_second;
+ ira_allocno_t dominator_a;
+ ira_copy_t cp;
+ ira_loop_tree_node_t parent, node, dominator;
+ allocno_live_range_t r;
+ ira_allocno_iterator ai;
+ ira_copy_iterator ci;
+ sparseset allocnos_live;
+ /* Map: regno -> allocnos which will finally represent the regno for
+ IR with one region. */
+ ira_allocno_t *regno_top_level_allocno_map;
+ bool *allocno_propagated_p;
+
+ regno_top_level_allocno_map
+ = (ira_allocno_t *) ira_allocate (max_reg_num () * sizeof (ira_allocno_t));
+ memset (regno_top_level_allocno_map, 0,
+ max_reg_num () * sizeof (ira_allocno_t));
+ allocno_propagated_p
+ = (bool *) ira_allocate (ira_allocnos_num * sizeof (bool));
+ memset (allocno_propagated_p, 0, ira_allocnos_num * sizeof (bool));
+ new_pseudos_p = merged_p = false;
+ /* Fix final allocno attributes. */
+ for (i = max_regno_before_emit - 1; i >= FIRST_PSEUDO_REGISTER; i--)
+ {
+ propagate_p = false;
+ for (a = ira_regno_allocno_map[i];
+ a != NULL;
+ a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
+ {
+ ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
+ if (ALLOCNO_SOMEWHERE_RENAMED_P (a))
+ new_pseudos_p = true;
+ if (ALLOCNO_CAP (a) != NULL
+ || (parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) == NULL
+ || ((parent_a = parent->regno_allocno_map[ALLOCNO_REGNO (a)])
+ == NULL))
+ {
+ ALLOCNO_COPIES (a) = NULL;
+ regno_top_level_allocno_map[REGNO (ALLOCNO_REG (a))] = a;
+ continue;
+ }
+ ira_assert (ALLOCNO_CAP_MEMBER (parent_a) == NULL);
+ if (propagate_p)
+ {
+ if (!allocno_propagated_p [ALLOCNO_NUM (parent_a)])
+ COPY_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (parent_a),
+ ALLOCNO_CONFLICT_HARD_REGS (parent_a));
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (parent_a),
+ ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+#ifdef STACK_REGS
+ if (!allocno_propagated_p [ALLOCNO_NUM (parent_a)])
+ ALLOCNO_TOTAL_NO_STACK_REG_P (parent_a)
+ = ALLOCNO_NO_STACK_REG_P (parent_a);
+ ALLOCNO_TOTAL_NO_STACK_REG_P (parent_a)
+ = (ALLOCNO_TOTAL_NO_STACK_REG_P (parent_a)
+ || ALLOCNO_TOTAL_NO_STACK_REG_P (a));
+#endif
+ allocno_propagated_p [ALLOCNO_NUM (parent_a)] = true;
+ }
+ if (REGNO (ALLOCNO_REG (a)) == REGNO (ALLOCNO_REG (parent_a)))
+ {
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file,
+ " Moving ranges of a%dr%d to a%dr%d: ",
+ ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)),
+ ALLOCNO_NUM (parent_a),
+ REGNO (ALLOCNO_REG (parent_a)));
+ ira_print_live_range_list (ira_dump_file,
+ ALLOCNO_LIVE_RANGES (a));
+ }
+ change_allocno_in_range_list (ALLOCNO_LIVE_RANGES (a), parent_a);
+ ALLOCNO_LIVE_RANGES (parent_a)
+ = merge_ranges (ALLOCNO_LIVE_RANGES (a),
+ ALLOCNO_LIVE_RANGES (parent_a));
+ merged_p = true;
+ ALLOCNO_LIVE_RANGES (a) = NULL;
+ ALLOCNO_MEM_OPTIMIZED_DEST_P (parent_a)
+ = (ALLOCNO_MEM_OPTIMIZED_DEST_P (parent_a)
+ || ALLOCNO_MEM_OPTIMIZED_DEST_P (a));
+ continue;
+ }
+ new_pseudos_p = true;
+ propagate_p = true;
+ first = ALLOCNO_MEM_OPTIMIZED_DEST (a) == NULL ? NULL : a;
+ stop_p = false;
+ for (;;)
+ {
+ if (first == NULL
+ && ALLOCNO_MEM_OPTIMIZED_DEST (parent_a) != NULL)
+ first = parent_a;
+ ALLOCNO_NREFS (parent_a) -= ALLOCNO_NREFS (a);
+ ALLOCNO_FREQ (parent_a) -= ALLOCNO_FREQ (a);
+ if (first != NULL
+ && ALLOCNO_MEM_OPTIMIZED_DEST (first) == parent_a)
+ stop_p = true;
+ else if (!stop_p)
+ {
+ ALLOCNO_CALL_FREQ (parent_a) -= ALLOCNO_CALL_FREQ (a);
+ ALLOCNO_CALLS_CROSSED_NUM (parent_a)
+ -= ALLOCNO_CALLS_CROSSED_NUM (a);
+ ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (parent_a)
+ -= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
+ }
+ ira_assert (ALLOCNO_CALLS_CROSSED_NUM (parent_a) >= 0
+ && ALLOCNO_NREFS (parent_a) >= 0
+ && ALLOCNO_FREQ (parent_a) >= 0);
+ cover_class = ALLOCNO_COVER_CLASS (parent_a);
+ hard_regs_num = ira_class_hard_regs_num[cover_class];
+ if (ALLOCNO_HARD_REG_COSTS (a) != NULL
+ && ALLOCNO_HARD_REG_COSTS (parent_a) != NULL)
+ for (j = 0; j < hard_regs_num; j++)
+ ALLOCNO_HARD_REG_COSTS (parent_a)[j]
+ -= ALLOCNO_HARD_REG_COSTS (a)[j];
+ if (ALLOCNO_CONFLICT_HARD_REG_COSTS (a) != NULL
+ && ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a) != NULL)
+ for (j = 0; j < hard_regs_num; j++)
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (parent_a)[j]
+ -= ALLOCNO_CONFLICT_HARD_REG_COSTS (a)[j];
+ ALLOCNO_COVER_CLASS_COST (parent_a)
+ -= ALLOCNO_COVER_CLASS_COST (a);
+ ALLOCNO_MEMORY_COST (parent_a) -= ALLOCNO_MEMORY_COST (a);
+ if (ALLOCNO_CAP (parent_a) != NULL
+ || (parent
+ = ALLOCNO_LOOP_TREE_NODE (parent_a)->parent) == NULL
+ || (parent_a = (parent->regno_allocno_map
+ [ALLOCNO_REGNO (parent_a)])) == NULL)
+ break;
+ }
+ if (first != NULL)
+ {
+ parent_a = ALLOCNO_MEM_OPTIMIZED_DEST (first);
+ dominator = common_loop_tree_node_dominator
+ (ALLOCNO_LOOP_TREE_NODE (parent_a),
+ ALLOCNO_LOOP_TREE_NODE (first));
+ dominator_a = dominator->regno_allocno_map[ALLOCNO_REGNO (a)];
+ ira_assert (parent_a != NULL);
+ stop_p = first != a;
+ /* Remember that exit can be to a grandparent (not only
+ to a parent) or a child of the grandparent. */
+ for (first = a;;)
+ {
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
+ {
+ fprintf
+ (ira_dump_file,
+ " Coping ranges of a%dr%d to a%dr%d: ",
+ ALLOCNO_NUM (first), REGNO (ALLOCNO_REG (first)),
+ ALLOCNO_NUM (parent_a),
+ REGNO (ALLOCNO_REG (parent_a)));
+ ira_print_live_range_list (ira_dump_file,
+ ALLOCNO_LIVE_RANGES (first));
+ }
+ r = copy_allocno_live_range_list (ALLOCNO_LIVE_RANGES
+ (first));
+ change_allocno_in_range_list (r, parent_a);
+ ALLOCNO_LIVE_RANGES (parent_a)
+ = merge_ranges (r, ALLOCNO_LIVE_RANGES (parent_a));
+ merged_p = true;
+ if (stop_p)
+ break;
+ parent = ALLOCNO_LOOP_TREE_NODE (first)->parent;
+ ira_assert (parent != NULL);
+ first = parent->regno_allocno_map[ALLOCNO_REGNO (a)];
+ ira_assert (first != NULL);
+ if (first == dominator_a)
+ break;
+ }
+ }
+ ALLOCNO_COPIES (a) = NULL;
+ regno_top_level_allocno_map[REGNO (ALLOCNO_REG (a))] = a;
+ }
+ }
+ ira_free (allocno_propagated_p);
+ ira_assert (new_pseudos_p || ira_max_point_before_emit == ira_max_point);
+ if (merged_p || ira_max_point_before_emit != ira_max_point)
+ ira_rebuild_start_finish_chains ();
+ if (new_pseudos_p)
+ {
+ /* Rebuild conflicts. */
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (a != regno_top_level_allocno_map[REGNO (ALLOCNO_REG (a))]
+ || ALLOCNO_CAP_MEMBER (a) != NULL)
+ continue;
+ for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
+ ira_assert (r->allocno == a);
+ clear_allocno_conflicts (a);
+ }
+ allocnos_live = sparseset_alloc (ira_allocnos_num);
+ for (i = 0; i < ira_max_point; i++)
+ {
+ for (r = ira_start_point_ranges[i]; r != NULL; r = r->start_next)
+ {
+ a = r->allocno;
+ if (a != regno_top_level_allocno_map[REGNO (ALLOCNO_REG (a))]
+ || ALLOCNO_CAP_MEMBER (a) != NULL)
+ continue;
+ num = ALLOCNO_NUM (a);
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ sparseset_set_bit (allocnos_live, num);
+ EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, n)
+ {
+ ira_allocno_t live_a = ira_allocnos[n];
+
+ if (cover_class == ALLOCNO_COVER_CLASS (live_a)
+ /* Don't set up conflict for the allocno with itself. */
+ && num != (int) n)
+ ira_add_allocno_conflict (a, 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);
+ compress_conflict_vecs ();
+ }
+ /* Mark some copies for removing and change allocnos in the rest
+ copies. */
+ FOR_EACH_COPY (cp, ci)
+ {
+ if (ALLOCNO_CAP_MEMBER (cp->first) != NULL
+ || ALLOCNO_CAP_MEMBER (cp->second) != NULL)
+ {
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
+ fprintf
+ (ira_dump_file, " Remove cp%d:%c%dr%d-%c%dr%d\n",
+ cp->num, ALLOCNO_CAP_MEMBER (cp->first) != NULL ? 'c' : 'a',
+ ALLOCNO_NUM (cp->first), REGNO (ALLOCNO_REG (cp->first)),
+ ALLOCNO_CAP_MEMBER (cp->second) != NULL ? 'c' : 'a',
+ ALLOCNO_NUM (cp->second), REGNO (ALLOCNO_REG (cp->second)));
+ cp->loop_tree_node = NULL;
+ continue;
+ }
+ first = regno_top_level_allocno_map[REGNO (ALLOCNO_REG (cp->first))];
+ second = regno_top_level_allocno_map[REGNO (ALLOCNO_REG (cp->second))];
+ node = cp->loop_tree_node;
+ if (node == NULL)
+ keep_p = true; /* It copy generated in ira-emit.c. */
+ else
+ {
+ /* Check that the copy was not propagated from level on
+ which we will have different pseudos. */
+ node_first = node->regno_allocno_map[ALLOCNO_REGNO (cp->first)];
+ node_second = node->regno_allocno_map[ALLOCNO_REGNO (cp->second)];
+ keep_p = ((REGNO (ALLOCNO_REG (first))
+ == REGNO (ALLOCNO_REG (node_first)))
+ && (REGNO (ALLOCNO_REG (second))
+ == REGNO (ALLOCNO_REG (node_second))));
+ }
+ if (keep_p)
+ {
+ cp->loop_tree_node = ira_loop_tree_root;
+ cp->first = first;
+ cp->second = second;
+ }
+ else
+ {
+ cp->loop_tree_node = NULL;
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Remove cp%d:a%dr%d-a%dr%d\n",
+ cp->num, ALLOCNO_NUM (cp->first),
+ REGNO (ALLOCNO_REG (cp->first)), ALLOCNO_NUM (cp->second),
+ REGNO (ALLOCNO_REG (cp->second)));
+ }
+ }
+ /* Remove unnecessary allocnos on lower levels of the loop tree. */
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (a != regno_top_level_allocno_map[REGNO (ALLOCNO_REG (a))]
+ || ALLOCNO_CAP_MEMBER (a) != NULL)
+ {
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Remove a%dr%d\n",
+ ALLOCNO_NUM (a), REGNO (ALLOCNO_REG (a)));
+ finish_allocno (a);
+ continue;
+ }
+ ALLOCNO_LOOP_TREE_NODE (a) = ira_loop_tree_root;
+ ALLOCNO_REGNO (a) = REGNO (ALLOCNO_REG (a));
+ ALLOCNO_CAP (a) = NULL;
+ ALLOCNO_UPDATED_MEMORY_COST (a) = ALLOCNO_MEMORY_COST (a);
+ if (! ALLOCNO_ASSIGNED_P (a))
+ ira_free_allocno_updated_costs (a);
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
+ ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
+ }
+ /* Remove unnecessary copies. */
+ FOR_EACH_COPY (cp, ci)
+ {
+ if (cp->loop_tree_node == NULL)
+ {
+ ira_copies[cp->num] = NULL;
+ finish_copy (cp);
+ continue;
+ }
+ ira_assert
+ (ALLOCNO_LOOP_TREE_NODE (cp->first) == ira_loop_tree_root
+ && ALLOCNO_LOOP_TREE_NODE (cp->second) == ira_loop_tree_root);
+ ira_add_allocno_copy_to_list (cp);
+ ira_swap_allocno_copy_ends_if_necessary (cp);
+ }
+ rebuild_regno_allocno_maps ();
+ ira_free (regno_top_level_allocno_map);
+}
+
+
+
+#ifdef ENABLE_IRA_CHECKING
+/* Check creation of all allocnos. Allocnos on lower levels should
+ have allocnos or caps on all upper levels. */
+static void
+check_allocno_creation (void)
+{
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+ ira_loop_tree_node_t loop_tree_node;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (ALLOCNO_LOOP_TREE_NODE (a) == ira_loop_tree_root)
+ continue;
+ if (ALLOCNO_CAP_MEMBER (a) != NULL)
+ {
+ ira_assert (ALLOCNO_CAP (a) != NULL);
+ }
+ else if (ALLOCNO_CAP (a) == NULL)
+ {
+ loop_tree_node = ALLOCNO_LOOP_TREE_NODE (a);
+ ira_assert (loop_tree_node->parent
+ ->regno_allocno_map[ALLOCNO_REGNO (a)] != NULL
+ && bitmap_bit_p (loop_tree_node->border_allocnos,
+ ALLOCNO_NUM (a)));
+ }
+ }
+}
+#endif
+
+/* Create a internal representation (IR) for IRA (allocnos, copies,
+ loop tree nodes). If LOOPS_P is FALSE the nodes corresponding to
+ the loops (except the root which corresponds the all function) and
+ correspondingly allocnos for the loops will be not created. Such
+ parameter value is used for Chaitin-Briggs coloring. The function
+ returns TRUE if we generate loop structure (besides nodes
+ representing all function and the basic blocks) for regional
+ allocation. A true return means that we really need to flatten IR
+ before the reload. */
+bool
+ira_build (bool loops_p)
+{
+ df_analyze ();
+
+ initiate_cost_vectors ();
+ initiate_allocnos ();
+ initiate_copies ();
+ create_loop_tree_nodes (loops_p);
+ form_loop_tree ();
+ create_allocnos ();
+ ira_costs ();
+ ira_create_allocno_live_ranges ();
+ remove_unnecessary_regions ();
+ loops_p = more_one_region_p ();
+ if (loops_p)
+ {
+ propagate_allocno_info ();
+ create_caps ();
+ }
+ ira_tune_allocno_costs_and_cover_classes ();
+#ifdef ENABLE_IRA_CHECKING
+ check_allocno_creation ();
+#endif
+ setup_min_max_allocno_live_range_point ();
+ sort_conflict_id_allocno_map ();
+ setup_min_max_conflict_allocno_ids ();
+ ira_build_conflicts ();
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ {
+ int n, nr;
+ ira_allocno_t a;
+ allocno_live_range_t r;
+ ira_allocno_iterator ai;
+
+ n = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ n += ALLOCNO_CONFLICT_ALLOCNOS_NUM (a);
+ nr = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
+ nr++;
+ fprintf (ira_dump_file, " regions=%d, blocks=%d, points=%d\n",
+ VEC_length (loop_p, ira_loops.larray), n_basic_blocks,
+ ira_max_point);
+ fprintf (ira_dump_file,
+ " allocnos=%d, copies=%d, conflicts=%d, ranges=%d\n",
+ ira_allocnos_num, ira_copies_num, n, nr);
+ }
+ return loops_p;
+}
+
+/* Release the data created by function ira_build. */
+void
+ira_destroy (void)
+{
+ finish_loop_tree_nodes ();
+ finish_copies ();
+ finish_allocnos ();
+ finish_cost_vectors ();
+ ira_finish_allocno_live_ranges ();
+}
diff --git a/gcc/ira-color.c b/gcc/ira-color.c
new file mode 100644
index 0000000..f3e4673
--- /dev/null
+++ b/gcc/ira-color.c
@@ -0,0 +1,2955 @@
+/* IRA allocation based on graph coloring.
+ 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 "rtl.h"
+#include "tm_p.h"
+#include "target.h"
+#include "regs.h"
+#include "flags.h"
+#include "sbitmap.h"
+#include "bitmap.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "expr.h"
+#include "toplev.h"
+#include "reload.h"
+#include "params.h"
+#include "df.h"
+#include "splay-tree.h"
+#include "ira-int.h"
+
+/* This file contains code for regional graph coloring, spill/restore
+ code placement optimization, and code helping the reload pass to do
+ a better job. */
+
+/* Bitmap of allocnos which should be colored. */
+static bitmap coloring_allocno_bitmap;
+
+/* Bitmap of allocnos which should be taken into account during
+ coloring. In general case it contains allocnos from
+ coloring_allocno_bitmap plus other already colored conflicting
+ allocnos. */
+static bitmap consideration_allocno_bitmap;
+
+/* TRUE if we coalesced some allocnos. In other words, if we got
+ loops formed by members first_coalesced_allocno and
+ next_coalesced_allocno containing more one allocno. */
+static bool allocno_coalesced_p;
+
+/* Bitmap used to prevent a repeated allocno processing because of
+ coalescing. */
+static bitmap processed_coalesced_allocno_bitmap;
+
+/* All allocnos sorted according their priorities. */
+static ira_allocno_t *sorted_allocnos;
+
+/* Vec representing the stack of allocnos used during coloring. */
+static VEC(ira_allocno_t,heap) *allocno_stack_vec;
+
+/* Array used to choose an allocno for spilling. */
+static ira_allocno_t *allocnos_for_spilling;
+
+/* Pool for splay tree nodes. */
+static alloc_pool splay_tree_node_pool;
+
+/* When an allocno is removed from the splay tree, it is put in the
+ following vector for subsequent inserting it into the splay tree
+ after putting all colorable allocnos onto the stack. The allocno
+ could be removed from and inserted to the splay tree every time
+ when its spilling priority is changed but such solution would be
+ more costly although simpler. */
+static VEC(ira_allocno_t,heap) *removed_splay_allocno_vec;
+
+
+
+/* This page contains functions used to choose hard registers for
+ allocnos. */
+
+/* Array whose element value is TRUE if the corresponding hard
+ register was already allocated for an allocno. */
+static bool allocated_hardreg_p[FIRST_PSEUDO_REGISTER];
+
+/* Array used to check already processed allocnos during the current
+ update_copy_costs call. */
+static int *allocno_update_cost_check;
+
+/* The current value of update_copy_cost call count. */
+static int update_cost_check;
+
+/* Allocate and initialize data necessary for function
+ update_copy_costs. */
+static void
+initiate_cost_update (void)
+{
+ allocno_update_cost_check
+ = (int *) ira_allocate (ira_allocnos_num * sizeof (int));
+ memset (allocno_update_cost_check, 0, ira_allocnos_num * sizeof (int));
+ update_cost_check = 0;
+}
+
+/* Deallocate data used by function update_copy_costs. */
+static void
+finish_cost_update (void)
+{
+ ira_free (allocno_update_cost_check);
+}
+
+/* This recursive function updates costs (decrease if DECR_P) of the
+ unassigned allocnos connected by copies with ALLOCNO. This update
+ increases chances to remove some copies. Copy cost is proportional
+ the copy frequency divided by DIVISOR. */
+static void
+update_copy_costs_1 (ira_allocno_t allocno, int hard_regno,
+ bool decr_p, int divisor)
+{
+ int i, cost, update_cost;
+ enum machine_mode mode;
+ enum reg_class rclass, cover_class;
+ ira_allocno_t another_allocno;
+ ira_copy_t cp, next_cp;
+
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ if (cover_class == NO_REGS)
+ return;
+ if (allocno_update_cost_check[ALLOCNO_NUM (allocno)] == update_cost_check)
+ return;
+ allocno_update_cost_check[ALLOCNO_NUM (allocno)] = update_cost_check;
+ ira_assert (hard_regno >= 0);
+ i = ira_class_hard_reg_index[cover_class][hard_regno];
+ ira_assert (i >= 0);
+ rclass = REGNO_REG_CLASS (hard_regno);
+ mode = ALLOCNO_MODE (allocno);
+ for (cp = ALLOCNO_COPIES (allocno); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == allocno)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_allocno = cp->second;
+ }
+ else if (cp->second == allocno)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_allocno = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ if (cover_class
+ != ALLOCNO_COVER_CLASS (another_allocno)
+ || ALLOCNO_ASSIGNED_P (another_allocno))
+ continue;
+ cost = (cp->second == allocno
+ ? ira_register_move_cost[mode][rclass]
+ [ALLOCNO_COVER_CLASS (another_allocno)]
+ : ira_register_move_cost[mode]
+ [ALLOCNO_COVER_CLASS (another_allocno)][rclass]);
+ if (decr_p)
+ cost = -cost;
+ ira_allocate_and_set_or_copy_costs
+ (&ALLOCNO_UPDATED_HARD_REG_COSTS (another_allocno), cover_class,
+ ALLOCNO_COVER_CLASS_COST (another_allocno),
+ ALLOCNO_HARD_REG_COSTS (another_allocno));
+ ira_allocate_and_set_or_copy_costs
+ (&ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno),
+ cover_class, 0,
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (another_allocno));
+ update_cost = cp->freq * cost / divisor;
+ ALLOCNO_UPDATED_HARD_REG_COSTS (another_allocno)[i] += update_cost;
+ ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno)[i]
+ += update_cost;
+ if (update_cost != 0)
+ update_copy_costs_1 (another_allocno, hard_regno,
+ decr_p, divisor * 4);
+ }
+}
+
+/* Update the cost of allocnos to increase chances to remove some
+ copies as the result of subsequent assignment. */
+static void
+update_copy_costs (ira_allocno_t allocno, bool decr_p)
+{
+ update_cost_check++;
+ update_copy_costs_1 (allocno, ALLOCNO_HARD_REGNO (allocno), decr_p, 1);
+}
+
+/* Sort allocnos according to the profit of usage of a hard register
+ instead of memory for them. */
+static int
+allocno_cost_compare_func (const void *v1p, const void *v2p)
+{
+ ira_allocno_t p1 = *(const ira_allocno_t *) v1p;
+ ira_allocno_t p2 = *(const ira_allocno_t *) v2p;
+ int c1, c2;
+
+ c1 = ALLOCNO_UPDATED_MEMORY_COST (p1) - ALLOCNO_COVER_CLASS_COST (p1);
+ c2 = ALLOCNO_UPDATED_MEMORY_COST (p2) - ALLOCNO_COVER_CLASS_COST (p2);
+ if (c1 - c2)
+ return c1 - c2;
+
+ /* If regs are equally good, sort by allocno numbers, so that the
+ results of qsort leave nothing to chance. */
+ return ALLOCNO_NUM (p1) - ALLOCNO_NUM (p2);
+}
+
+/* Print all allocnos coalesced with ALLOCNO. */
+static void
+print_coalesced_allocno (ira_allocno_t allocno)
+{
+ ira_allocno_t a;
+
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ ira_print_expanded_allocno (a);
+ if (a == allocno)
+ break;
+ fprintf (ira_dump_file, "+");
+ }
+}
+
+/* Choose a hard register for ALLOCNO (or for all coalesced allocnos
+ represented by ALLOCNO). If RETRY_P is TRUE, it means that the
+ function called from function `ira_reassign_conflict_allocnos' and
+ `allocno_reload_assign'. This function implements the optimistic
+ coalescing too: if we failed to assign a hard register to set of
+ the coalesced allocnos, we put them onto the coloring stack for
+ subsequent separate assigning. */
+static bool
+assign_hard_reg (ira_allocno_t allocno, bool retry_p)
+{
+ HARD_REG_SET conflicting_regs;
+ int i, j, hard_regno, best_hard_regno, class_size;
+ int cost, mem_cost, min_cost, full_cost, min_full_cost, add_cost;
+ int *a_costs;
+ int *conflict_costs;
+ enum reg_class cover_class, rclass;
+ enum machine_mode mode;
+ ira_allocno_t a, conflict_allocno;
+ ira_allocno_t another_allocno;
+ ira_allocno_conflict_iterator aci;
+ ira_copy_t cp, next_cp;
+ static int costs[FIRST_PSEUDO_REGISTER], full_costs[FIRST_PSEUDO_REGISTER];
+#ifdef STACK_REGS
+ bool no_stack_reg_p;
+#endif
+
+ ira_assert (! ALLOCNO_ASSIGNED_P (allocno));
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ class_size = ira_class_hard_regs_num[cover_class];
+ mode = ALLOCNO_MODE (allocno);
+ CLEAR_HARD_REG_SET (conflicting_regs);
+ best_hard_regno = -1;
+ memset (full_costs, 0, sizeof (int) * class_size);
+ mem_cost = 0;
+ if (allocno_coalesced_p)
+ bitmap_clear (processed_coalesced_allocno_bitmap);
+ memset (costs, 0, sizeof (int) * class_size);
+ memset (full_costs, 0, sizeof (int) * class_size);
+#ifdef STACK_REGS
+ no_stack_reg_p = false;
+#endif
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ mem_cost += ALLOCNO_UPDATED_MEMORY_COST (a);
+ IOR_HARD_REG_SET (conflicting_regs,
+ ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ ira_allocate_and_copy_costs (&ALLOCNO_UPDATED_HARD_REG_COSTS (a),
+ cover_class, ALLOCNO_HARD_REG_COSTS (a));
+ a_costs = ALLOCNO_UPDATED_HARD_REG_COSTS (a);
+#ifdef STACK_REGS
+ no_stack_reg_p = no_stack_reg_p || ALLOCNO_TOTAL_NO_STACK_REG_P (a);
+#endif
+ for (cost = ALLOCNO_COVER_CLASS_COST (a), i = 0; i < class_size; i++)
+ if (a_costs != NULL)
+ {
+ costs[i] += a_costs[i];
+ full_costs[i] += a_costs[i];
+ }
+ else
+ {
+ costs[i] += cost;
+ full_costs[i] += cost;
+ }
+ /* Take preferences of conflicting allocnos into account. */
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_allocno, aci)
+ /* Reload can give another class so we need to check all
+ allocnos. */
+ if (retry_p || bitmap_bit_p (consideration_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ {
+ ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno));
+ if (allocno_coalesced_p)
+ {
+ if (bitmap_bit_p (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ continue;
+ bitmap_set_bit (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno));
+ }
+ if (ALLOCNO_ASSIGNED_P (conflict_allocno))
+ {
+ if ((hard_regno = ALLOCNO_HARD_REGNO (conflict_allocno)) >= 0)
+ {
+ IOR_HARD_REG_SET
+ (conflicting_regs,
+ ira_reg_mode_hard_regset
+ [hard_regno][ALLOCNO_MODE (conflict_allocno)]);
+ if (hard_reg_set_subset_p (reg_class_contents[cover_class],
+ conflicting_regs))
+ goto fail;
+ }
+ continue;
+ }
+ else if (! ALLOCNO_MAY_BE_SPILLED_P (conflict_allocno))
+ {
+ ira_allocate_and_copy_costs
+ (&ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (conflict_allocno),
+ cover_class,
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (conflict_allocno));
+ conflict_costs
+ = ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (conflict_allocno);
+ if (conflict_costs != NULL)
+ for (j = class_size - 1; j >= 0; j--)
+ full_costs[j] -= conflict_costs[j];
+ }
+ }
+ if (a == allocno)
+ break;
+ }
+ /* Take copies into account. */
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == a)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_allocno = cp->second;
+ }
+ else if (cp->second == a)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_allocno = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ if (cover_class != ALLOCNO_COVER_CLASS (another_allocno)
+ || ALLOCNO_ASSIGNED_P (another_allocno))
+ continue;
+ ira_allocate_and_copy_costs
+ (&ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno),
+ cover_class, ALLOCNO_CONFLICT_HARD_REG_COSTS (another_allocno));
+ conflict_costs
+ = ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (another_allocno);
+ if (conflict_costs != NULL
+ && ! ALLOCNO_MAY_BE_SPILLED_P (another_allocno))
+ for (j = class_size - 1; j >= 0; j--)
+ full_costs[j] += conflict_costs[j];
+ }
+ if (a == allocno)
+ break;
+ }
+ min_cost = min_full_cost = INT_MAX;
+ /* We don't care about giving callee saved registers to allocnos no
+ living through calls because call clobbered registers are
+ allocated first (it is usual practice to put them first in
+ REG_ALLOC_ORDER). */
+ for (i = 0; i < class_size; i++)
+ {
+ hard_regno = ira_class_hard_regs[cover_class][i];
+#ifdef STACK_REGS
+ if (no_stack_reg_p
+ && FIRST_STACK_REG <= hard_regno && hard_regno <= LAST_STACK_REG)
+ continue;
+#endif
+ if (! ira_hard_reg_not_in_set_p (hard_regno, mode, conflicting_regs)
+ || TEST_HARD_REG_BIT (prohibited_class_mode_regs[cover_class][mode],
+ hard_regno))
+ continue;
+ cost = costs[i];
+ full_cost = full_costs[i];
+ if (! allocated_hardreg_p[hard_regno]
+ && ira_hard_reg_not_in_set_p (hard_regno, mode, call_used_reg_set))
+ /* We need to save/restore the hard register in
+ epilogue/prologue. Therefore we increase the cost. */
+ {
+ /* ??? If only part is call clobbered. */
+ rclass = REGNO_REG_CLASS (hard_regno);
+ add_cost = (ira_memory_move_cost[mode][rclass][0]
+ + ira_memory_move_cost[mode][rclass][1] - 1);
+ cost += add_cost;
+ full_cost += add_cost;
+ }
+ if (min_cost > cost)
+ min_cost = cost;
+ if (min_full_cost > full_cost)
+ {
+ min_full_cost = full_cost;
+ best_hard_regno = hard_regno;
+ ira_assert (hard_regno >= 0);
+ }
+ }
+ if (min_full_cost > mem_cost)
+ {
+ if (! retry_p && internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "(memory is more profitable %d vs %d) ",
+ mem_cost, min_full_cost);
+ best_hard_regno = -1;
+ }
+ fail:
+ if (best_hard_regno < 0
+ && ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno) != allocno)
+ {
+ for (j = 0, a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ sorted_allocnos[j++] = a;
+ if (a == allocno)
+ break;
+ }
+ qsort (sorted_allocnos, j, sizeof (ira_allocno_t),
+ allocno_cost_compare_func);
+ for (i = 0; i < j; i++)
+ {
+ a = sorted_allocnos[i];
+ ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a;
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a;
+ VEC_safe_push (ira_allocno_t, heap, allocno_stack_vec, a);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Pushing");
+ print_coalesced_allocno (a);
+ fprintf (ira_dump_file, "\n");
+ }
+ }
+ return false;
+ }
+ if (best_hard_regno >= 0)
+ allocated_hardreg_p[best_hard_regno] = true;
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ ALLOCNO_HARD_REGNO (a) = best_hard_regno;
+ ALLOCNO_ASSIGNED_P (a) = true;
+ if (best_hard_regno >= 0)
+ update_copy_costs (a, true);
+ ira_assert (ALLOCNO_COVER_CLASS (a) == cover_class);
+ /* We don't need updated costs anymore: */
+ ira_free_allocno_updated_costs (a);
+ if (a == allocno)
+ break;
+ }
+ return best_hard_regno >= 0;
+}
+
+
+
+/* This page contains the allocator based on the Chaitin-Briggs algorithm. */
+
+/* Bucket of allocnos that can colored currently without spilling. */
+static ira_allocno_t colorable_allocno_bucket;
+
+/* Bucket of allocnos that might be not colored currently without
+ spilling. */
+static ira_allocno_t uncolorable_allocno_bucket;
+
+/* Each element of the array contains the current number of allocnos
+ of given *cover* class in the uncolorable_bucket. */
+static int uncolorable_allocnos_num[N_REG_CLASSES];
+
+/* Add ALLOCNO to bucket *BUCKET_PTR. ALLOCNO should be not in a bucket
+ before the call. */
+static void
+add_ira_allocno_to_bucket (ira_allocno_t allocno, ira_allocno_t *bucket_ptr)
+{
+ ira_allocno_t first_allocno;
+ enum reg_class cover_class;
+
+ if (bucket_ptr == &uncolorable_allocno_bucket
+ && (cover_class = ALLOCNO_COVER_CLASS (allocno)) != NO_REGS)
+ {
+ uncolorable_allocnos_num[cover_class]++;
+ ira_assert (uncolorable_allocnos_num[cover_class] > 0);
+ }
+ first_allocno = *bucket_ptr;
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (allocno) = first_allocno;
+ ALLOCNO_PREV_BUCKET_ALLOCNO (allocno) = NULL;
+ if (first_allocno != NULL)
+ ALLOCNO_PREV_BUCKET_ALLOCNO (first_allocno) = allocno;
+ *bucket_ptr = allocno;
+}
+
+/* The function returns frequency and number of available hard
+ registers for allocnos coalesced with ALLOCNO. */
+static void
+get_coalesced_allocnos_attributes (ira_allocno_t allocno, int *freq, int *num)
+{
+ ira_allocno_t a;
+
+ *freq = 0;
+ *num = 0;
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ *freq += ALLOCNO_FREQ (a);
+ *num += ALLOCNO_AVAILABLE_REGS_NUM (a);
+ if (a == allocno)
+ break;
+ }
+}
+
+/* Compare two allocnos to define which allocno should be pushed first
+ into the coloring stack. If the return is a negative number, the
+ allocno given by the first parameter will be pushed first. In this
+ case such allocno has less priority than the second one and the
+ hard register will be assigned to it after assignment to the second
+ one. As the result of such assignment order, the second allocno
+ has a better chance to get the best hard register. */
+static int
+bucket_allocno_compare_func (const void *v1p, const void *v2p)
+{
+ ira_allocno_t a1 = *(const ira_allocno_t *) v1p;
+ ira_allocno_t a2 = *(const ira_allocno_t *) v2p;
+ int diff, a1_freq, a2_freq, a1_num, a2_num;
+
+ if ((diff = (int) ALLOCNO_COVER_CLASS (a2) - ALLOCNO_COVER_CLASS (a1)) != 0)
+ return diff;
+ get_coalesced_allocnos_attributes (a1, &a1_freq, &a1_num);
+ get_coalesced_allocnos_attributes (a2, &a2_freq, &a2_num);
+ if ((diff = a2_num - a1_num) != 0)
+ return diff;
+ else if ((diff = a1_freq - a2_freq) != 0)
+ return diff;
+ return ALLOCNO_NUM (a2) - ALLOCNO_NUM (a1);
+}
+
+/* Sort bucket *BUCKET_PTR and return the result through
+ BUCKET_PTR. */
+static void
+sort_bucket (ira_allocno_t *bucket_ptr)
+{
+ ira_allocno_t a, head;
+ int n;
+
+ for (n = 0, a = *bucket_ptr; a != NULL; a = ALLOCNO_NEXT_BUCKET_ALLOCNO (a))
+ sorted_allocnos[n++] = a;
+ if (n <= 1)
+ return;
+ qsort (sorted_allocnos, n, sizeof (ira_allocno_t),
+ bucket_allocno_compare_func);
+ head = NULL;
+ for (n--; n >= 0; n--)
+ {
+ a = sorted_allocnos[n];
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (a) = head;
+ ALLOCNO_PREV_BUCKET_ALLOCNO (a) = NULL;
+ if (head != NULL)
+ ALLOCNO_PREV_BUCKET_ALLOCNO (head) = a;
+ head = a;
+ }
+ *bucket_ptr = head;
+}
+
+/* Add ALLOCNO to bucket *BUCKET_PTR maintaining the order according
+ their priority. ALLOCNO should be not in a bucket before the
+ call. */
+static void
+add_ira_allocno_to_ordered_bucket (ira_allocno_t allocno,
+ ira_allocno_t *bucket_ptr)
+{
+ ira_allocno_t before, after;
+ enum reg_class cover_class;
+
+ if (bucket_ptr == &uncolorable_allocno_bucket
+ && (cover_class = ALLOCNO_COVER_CLASS (allocno)) != NO_REGS)
+ {
+ uncolorable_allocnos_num[cover_class]++;
+ ira_assert (uncolorable_allocnos_num[cover_class] > 0);
+ }
+ for (before = *bucket_ptr, after = NULL;
+ before != NULL;
+ after = before, before = ALLOCNO_NEXT_BUCKET_ALLOCNO (before))
+ if (bucket_allocno_compare_func (&allocno, &before) < 0)
+ break;
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (allocno) = before;
+ ALLOCNO_PREV_BUCKET_ALLOCNO (allocno) = after;
+ if (after == NULL)
+ *bucket_ptr = allocno;
+ else
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (after) = allocno;
+ if (before != NULL)
+ ALLOCNO_PREV_BUCKET_ALLOCNO (before) = allocno;
+}
+
+/* Delete ALLOCNO from bucket *BUCKET_PTR. It should be there before
+ the call. */
+static void
+delete_allocno_from_bucket (ira_allocno_t allocno, ira_allocno_t *bucket_ptr)
+{
+ ira_allocno_t prev_allocno, next_allocno;
+ enum reg_class cover_class;
+
+ if (bucket_ptr == &uncolorable_allocno_bucket
+ && (cover_class = ALLOCNO_COVER_CLASS (allocno)) != NO_REGS)
+ {
+ uncolorable_allocnos_num[cover_class]--;
+ ira_assert (uncolorable_allocnos_num[cover_class] >= 0);
+ }
+ prev_allocno = ALLOCNO_PREV_BUCKET_ALLOCNO (allocno);
+ next_allocno = ALLOCNO_NEXT_BUCKET_ALLOCNO (allocno);
+ if (prev_allocno != NULL)
+ ALLOCNO_NEXT_BUCKET_ALLOCNO (prev_allocno) = next_allocno;
+ else
+ {
+ ira_assert (*bucket_ptr == allocno);
+ *bucket_ptr = next_allocno;
+ }
+ if (next_allocno != NULL)
+ ALLOCNO_PREV_BUCKET_ALLOCNO (next_allocno) = prev_allocno;
+}
+
+/* Splay tree for each cover class. The trees are indexed by the
+ corresponding cover classes. Splay trees contain uncolorable
+ allocnos. */
+static splay_tree uncolorable_allocnos_splay_tree[N_REG_CLASSES];
+
+/* If the following macro is TRUE, splay tree is used to choose an
+ allocno of the corresponding cover class for spilling. When the
+ number uncolorable allocnos of given cover class decreases to some
+ threshold, linear array search is used to find the best allocno for
+ spilling. This threshold is actually pretty big because, although
+ splay trees asymptotically is much faster, each splay tree
+ operation is sufficiently costly especially taking cache locality
+ into account. */
+#define USE_SPLAY_P(CLASS) (uncolorable_allocnos_num[CLASS] > 4000)
+
+/* Put ALLOCNO onto the coloring stack without removing it from its
+ bucket. Pushing allocno to the coloring stack can result in moving
+ conflicting allocnos from the uncolorable bucket to the colorable
+ one. */
+static void
+push_ira_allocno_to_stack (ira_allocno_t allocno)
+{
+ int conflicts_num, conflict_size, size;
+ ira_allocno_t a, conflict_allocno;
+ enum reg_class cover_class;
+ ira_allocno_conflict_iterator aci;
+
+ ALLOCNO_IN_GRAPH_P (allocno) = false;
+ VEC_safe_push (ira_allocno_t, heap, allocno_stack_vec, allocno);
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ if (cover_class == NO_REGS)
+ return;
+ size = ira_reg_class_nregs[cover_class][ALLOCNO_MODE (allocno)];
+ if (allocno_coalesced_p)
+ bitmap_clear (processed_coalesced_allocno_bitmap);
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_allocno, aci)
+ if (bitmap_bit_p (coloring_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ {
+ ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_allocno));
+ if (allocno_coalesced_p)
+ {
+ if (bitmap_bit_p (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ continue;
+ bitmap_set_bit (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno));
+ }
+ if (ALLOCNO_IN_GRAPH_P (conflict_allocno)
+ && ! ALLOCNO_ASSIGNED_P (conflict_allocno))
+ {
+ conflicts_num = ALLOCNO_LEFT_CONFLICTS_NUM (conflict_allocno);
+ conflict_size
+ = (ira_reg_class_nregs
+ [cover_class][ALLOCNO_MODE (conflict_allocno)]);
+ ira_assert
+ (ALLOCNO_LEFT_CONFLICTS_NUM (conflict_allocno) >= size);
+ if (conflicts_num + conflict_size
+ <= ALLOCNO_AVAILABLE_REGS_NUM (conflict_allocno))
+ {
+ ALLOCNO_LEFT_CONFLICTS_NUM (conflict_allocno) -= size;
+ continue;
+ }
+ conflicts_num
+ = ALLOCNO_LEFT_CONFLICTS_NUM (conflict_allocno) - size;
+ if (uncolorable_allocnos_splay_tree[cover_class] != NULL
+ && !ALLOCNO_SPLAY_REMOVED_P (conflict_allocno)
+ && USE_SPLAY_P (cover_class))
+ {
+ ira_assert
+ (splay_tree_lookup
+ (uncolorable_allocnos_splay_tree[cover_class],
+ (splay_tree_key) conflict_allocno) != NULL);
+ splay_tree_remove
+ (uncolorable_allocnos_splay_tree[cover_class],
+ (splay_tree_key) conflict_allocno);
+ ALLOCNO_SPLAY_REMOVED_P (conflict_allocno) = true;
+ VEC_safe_push (ira_allocno_t, heap,
+ removed_splay_allocno_vec,
+ conflict_allocno);
+ }
+ ALLOCNO_LEFT_CONFLICTS_NUM (conflict_allocno) = conflicts_num;
+ if (conflicts_num + conflict_size
+ <= ALLOCNO_AVAILABLE_REGS_NUM (conflict_allocno))
+ {
+ delete_allocno_from_bucket (conflict_allocno,
+ &uncolorable_allocno_bucket);
+ add_ira_allocno_to_ordered_bucket (conflict_allocno,
+ &colorable_allocno_bucket);
+ }
+ }
+ }
+ if (a == allocno)
+ break;
+ }
+}
+
+/* Put ALLOCNO onto the coloring stack and remove it from its bucket.
+ The allocno is in the colorable bucket if COLORABLE_P is TRUE. */
+static void
+remove_allocno_from_bucket_and_push (ira_allocno_t allocno, bool colorable_p)
+{
+ enum reg_class cover_class;
+
+ if (colorable_p)
+ delete_allocno_from_bucket (allocno, &colorable_allocno_bucket);
+ else
+ delete_allocno_from_bucket (allocno, &uncolorable_allocno_bucket);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Pushing");
+ print_coalesced_allocno (allocno);
+ fprintf (ira_dump_file, "%s\n", colorable_p ? "" : "(potential spill)");
+ }
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ ira_assert ((colorable_p
+ && (ALLOCNO_LEFT_CONFLICTS_NUM (allocno)
+ + ira_reg_class_nregs[cover_class][ALLOCNO_MODE (allocno)]
+ <= ALLOCNO_AVAILABLE_REGS_NUM (allocno)))
+ || (! colorable_p
+ && (ALLOCNO_LEFT_CONFLICTS_NUM (allocno)
+ + ira_reg_class_nregs[cover_class][ALLOCNO_MODE
+ (allocno)]
+ > ALLOCNO_AVAILABLE_REGS_NUM (allocno))));
+ if (! colorable_p)
+ ALLOCNO_MAY_BE_SPILLED_P (allocno) = true;
+ push_ira_allocno_to_stack (allocno);
+}
+
+/* Put all allocnos from colorable bucket onto the coloring stack. */
+static void
+push_only_colorable (void)
+{
+ sort_bucket (&colorable_allocno_bucket);
+ for (;colorable_allocno_bucket != NULL;)
+ remove_allocno_from_bucket_and_push (colorable_allocno_bucket, true);
+}
+
+/* Puts ALLOCNO chosen for potential spilling onto the coloring
+ stack. */
+static void
+push_ira_allocno_to_spill (ira_allocno_t allocno)
+{
+ delete_allocno_from_bucket (allocno, &uncolorable_allocno_bucket);
+ ALLOCNO_MAY_BE_SPILLED_P (allocno) = true;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Pushing p%d(%d) (potential spill)\n",
+ ALLOCNO_NUM (allocno), ALLOCNO_REGNO (allocno));
+ push_ira_allocno_to_stack (allocno);
+}
+
+/* Return the frequency of exit edges (if EXIT_P) or entry from/to the
+ loop given by its LOOP_NODE. */
+int
+ira_loop_edge_freq (ira_loop_tree_node_t loop_node, int regno, bool exit_p)
+{
+ int freq, i;
+ edge_iterator ei;
+ edge e;
+ VEC (edge, heap) *edges;
+
+ ira_assert (loop_node->loop != NULL
+ && (regno < 0 || regno >= FIRST_PSEUDO_REGISTER));
+ freq = 0;
+ if (! exit_p)
+ {
+ FOR_EACH_EDGE (e, ei, loop_node->loop->header->preds)
+ if (e->src != loop_node->loop->latch
+ && (regno < 0
+ || (bitmap_bit_p (DF_LR_OUT (e->src), regno)
+ && bitmap_bit_p (DF_LR_IN (e->dest), regno))))
+ freq += EDGE_FREQUENCY (e);
+ }
+ else
+ {
+ edges = get_loop_exit_edges (loop_node->loop);
+ for (i = 0; VEC_iterate (edge, edges, i, e); i++)
+ if (regno < 0
+ || (bitmap_bit_p (DF_LR_OUT (e->src), regno)
+ && bitmap_bit_p (DF_LR_IN (e->dest), regno)))
+ freq += EDGE_FREQUENCY (e);
+ VEC_free (edge, heap, edges);
+ }
+
+ return REG_FREQ_FROM_EDGE_FREQ (freq);
+}
+
+/* Calculate and return the cost of putting allocno A into memory. */
+static int
+calculate_allocno_spill_cost (ira_allocno_t a)
+{
+ int regno, cost;
+ enum machine_mode mode;
+ enum reg_class rclass;
+ ira_allocno_t parent_allocno;
+ ira_loop_tree_node_t parent_node, loop_node;
+
+ regno = ALLOCNO_REGNO (a);
+ cost = ALLOCNO_UPDATED_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a);
+ if (ALLOCNO_CAP (a) != NULL)
+ return cost;
+ loop_node = ALLOCNO_LOOP_TREE_NODE (a);
+ if ((parent_node = loop_node->parent) == NULL)
+ return cost;
+ if ((parent_allocno = parent_node->regno_allocno_map[regno]) == NULL)
+ return cost;
+ mode = ALLOCNO_MODE (a);
+ rclass = ALLOCNO_COVER_CLASS (a);
+ if (ALLOCNO_HARD_REGNO (parent_allocno) < 0)
+ cost -= (ira_memory_move_cost[mode][rclass][0]
+ * ira_loop_edge_freq (loop_node, regno, true)
+ + ira_memory_move_cost[mode][rclass][1]
+ * ira_loop_edge_freq (loop_node, regno, false));
+ else
+ cost += ((ira_memory_move_cost[mode][rclass][1]
+ * ira_loop_edge_freq (loop_node, regno, true)
+ + ira_memory_move_cost[mode][rclass][0]
+ * ira_loop_edge_freq (loop_node, regno, false))
+ - (ira_register_move_cost[mode][rclass][rclass]
+ * (ira_loop_edge_freq (loop_node, regno, false)
+ + ira_loop_edge_freq (loop_node, regno, true))));
+ return cost;
+}
+
+/* Compare keys in the splay tree used to choose best allocno for
+ spilling. The best allocno has the minimal key. */
+static int
+allocno_spill_priority_compare (splay_tree_key k1, splay_tree_key k2)
+{
+ int pri1, pri2, diff;
+ ira_allocno_t a1 = (ira_allocno_t) k1, a2 = (ira_allocno_t) k2;
+
+ pri1 = (IRA_ALLOCNO_TEMP (a1)
+ / (ALLOCNO_LEFT_CONFLICTS_NUM (a1)
+ * ira_reg_class_nregs[ALLOCNO_COVER_CLASS (a1)][ALLOCNO_MODE (a1)]
+ + 1));
+ pri2 = (IRA_ALLOCNO_TEMP (a2)
+ / (ALLOCNO_LEFT_CONFLICTS_NUM (a2)
+ * ira_reg_class_nregs[ALLOCNO_COVER_CLASS (a2)][ALLOCNO_MODE (a2)]
+ + 1));
+ if ((diff = pri1 - pri2) != 0)
+ return diff;
+ if ((diff = IRA_ALLOCNO_TEMP (a1) - IRA_ALLOCNO_TEMP (a2)) != 0)
+ return diff;
+ return ALLOCNO_NUM (a1) - ALLOCNO_NUM (a2);
+}
+
+/* Allocate data of SIZE for the splay trees. We allocate only spay
+ tree roots or splay tree nodes. If you change this, please rewrite
+ the function. */
+static void *
+splay_tree_allocate (int size, void *data ATTRIBUTE_UNUSED)
+{
+ if (size != sizeof (struct splay_tree_node_s))
+ return ira_allocate (size);
+ return pool_alloc (splay_tree_node_pool);
+}
+
+/* Free data NODE for the splay trees. We allocate and free only spay
+ tree roots or splay tree nodes. If you change this, please rewrite
+ the function. */
+static void
+splay_tree_free (void *node, void *data ATTRIBUTE_UNUSED)
+{
+ int i;
+ enum reg_class cover_class;
+
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ if (node == uncolorable_allocnos_splay_tree[cover_class])
+ {
+ ira_free (node);
+ return;
+ }
+ }
+ pool_free (splay_tree_node_pool, node);
+}
+
+/* Push allocnos to the coloring stack. The order of allocnos in the
+ stack defines the order for the subsequent coloring. */
+static void
+push_allocnos_to_stack (void)
+{
+ ira_allocno_t allocno, a, i_allocno, *allocno_vec;
+ enum reg_class cover_class, rclass;
+ int allocno_pri, i_allocno_pri, allocno_cost, i_allocno_cost;
+ int i, j, num, cover_class_allocnos_num[N_REG_CLASSES];
+ ira_allocno_t *cover_class_allocnos[N_REG_CLASSES];
+ int cost;
+
+ /* Initialize. */
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ cover_class_allocnos_num[cover_class] = 0;
+ cover_class_allocnos[cover_class] = NULL;
+ uncolorable_allocnos_splay_tree[cover_class] = NULL;
+ }
+ /* Calculate uncolorable allocno spill costs. */
+ for (allocno = uncolorable_allocno_bucket;
+ allocno != NULL;
+ allocno = ALLOCNO_NEXT_BUCKET_ALLOCNO (allocno))
+ if ((cover_class = ALLOCNO_COVER_CLASS (allocno)) != NO_REGS)
+ {
+ cover_class_allocnos_num[cover_class]++;
+ cost = 0;
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ cost += calculate_allocno_spill_cost (a);
+ if (a == allocno)
+ break;
+ }
+ /* ??? Remove cost of copies between the coalesced
+ allocnos. */
+ IRA_ALLOCNO_TEMP (allocno) = cost;
+ }
+ /* Define place where to put uncolorable allocnos of the same cover
+ class. */
+ for (num = i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ ira_assert (cover_class_allocnos_num[cover_class]
+ == uncolorable_allocnos_num[cover_class]);
+ if (cover_class_allocnos_num[cover_class] != 0)
+ {
+ cover_class_allocnos[cover_class] = allocnos_for_spilling + num;
+ num += cover_class_allocnos_num[cover_class];
+ cover_class_allocnos_num[cover_class] = 0;
+ }
+ if (USE_SPLAY_P (cover_class))
+ uncolorable_allocnos_splay_tree[cover_class]
+ = splay_tree_new_with_allocator (allocno_spill_priority_compare,
+ NULL, NULL, splay_tree_allocate,
+ splay_tree_free, NULL);
+ }
+ ira_assert (num <= ira_allocnos_num);
+ /* Collect uncolorable allocnos of each cover class. */
+ for (allocno = uncolorable_allocno_bucket;
+ allocno != NULL;
+ allocno = ALLOCNO_NEXT_BUCKET_ALLOCNO (allocno))
+ if ((cover_class = ALLOCNO_COVER_CLASS (allocno)) != NO_REGS)
+ {
+ cover_class_allocnos
+ [cover_class][cover_class_allocnos_num[cover_class]++] = allocno;
+ if (uncolorable_allocnos_splay_tree[cover_class] != NULL)
+ splay_tree_insert (uncolorable_allocnos_splay_tree[cover_class],
+ (splay_tree_key) allocno,
+ (splay_tree_value) allocno);
+ }
+ for (;;)
+ {
+ push_only_colorable ();
+ allocno = uncolorable_allocno_bucket;
+ if (allocno == NULL)
+ break;
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ if (cover_class == NO_REGS)
+ {
+ push_ira_allocno_to_spill (allocno);
+ continue;
+ }
+ /* Potential spilling. */
+ ira_assert
+ (ira_reg_class_nregs[cover_class][ALLOCNO_MODE (allocno)] > 0);
+ if (USE_SPLAY_P (cover_class))
+ {
+ for (;VEC_length (ira_allocno_t, removed_splay_allocno_vec) != 0;)
+ {
+ allocno = VEC_pop (ira_allocno_t, removed_splay_allocno_vec);
+ ALLOCNO_SPLAY_REMOVED_P (allocno) = false;
+ rclass = ALLOCNO_COVER_CLASS (allocno);
+ if (ALLOCNO_LEFT_CONFLICTS_NUM (allocno)
+ + ira_reg_class_nregs [rclass][ALLOCNO_MODE (allocno)]
+ > ALLOCNO_AVAILABLE_REGS_NUM (allocno))
+ splay_tree_insert
+ (uncolorable_allocnos_splay_tree[rclass],
+ (splay_tree_key) allocno, (splay_tree_value) allocno);
+ }
+ allocno = ((ira_allocno_t)
+ splay_tree_min
+ (uncolorable_allocnos_splay_tree[cover_class])->key);
+ splay_tree_remove (uncolorable_allocnos_splay_tree[cover_class],
+ (splay_tree_key) allocno);
+ }
+ else
+ {
+ num = cover_class_allocnos_num[cover_class];
+ ira_assert (num > 0);
+ allocno_vec = cover_class_allocnos[cover_class];
+ allocno = NULL;
+ allocno_pri = allocno_cost = 0;
+ /* Sort uncolorable allocno to find the one with the lowest
+ spill cost. */
+ for (i = 0, j = num - 1; i <= j;)
+ {
+ i_allocno = allocno_vec[i];
+ if (! ALLOCNO_IN_GRAPH_P (i_allocno)
+ && ALLOCNO_IN_GRAPH_P (allocno_vec[j]))
+ {
+ i_allocno = allocno_vec[j];
+ allocno_vec[j] = allocno_vec[i];
+ allocno_vec[i] = i_allocno;
+ }
+ if (ALLOCNO_IN_GRAPH_P (i_allocno))
+ {
+ i++;
+ if (IRA_ALLOCNO_TEMP (i_allocno) == INT_MAX)
+ {
+ ira_allocno_t a;
+ int cost = 0;
+
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (i_allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ cost += calculate_allocno_spill_cost (i_allocno);
+ if (a == i_allocno)
+ break;
+ }
+ /* ??? Remove cost of copies between the coalesced
+ allocnos. */
+ IRA_ALLOCNO_TEMP (i_allocno) = cost;
+ }
+ i_allocno_cost = IRA_ALLOCNO_TEMP (i_allocno);
+ i_allocno_pri
+ = (i_allocno_cost
+ / (ALLOCNO_LEFT_CONFLICTS_NUM (i_allocno)
+ * ira_reg_class_nregs[ALLOCNO_COVER_CLASS
+ (i_allocno)]
+ [ALLOCNO_MODE (i_allocno)] + 1));
+ if (allocno == NULL || allocno_pri > i_allocno_pri
+ || (allocno_pri == i_allocno_pri
+ && (allocno_cost > i_allocno_cost
+ || (allocno_cost == i_allocno_cost
+ && (ALLOCNO_NUM (allocno)
+ > ALLOCNO_NUM (i_allocno))))))
+ {
+ allocno = i_allocno;
+ allocno_cost = i_allocno_cost;
+ allocno_pri = i_allocno_pri;
+ }
+ }
+ if (! ALLOCNO_IN_GRAPH_P (allocno_vec[j]))
+ j--;
+ }
+ ira_assert (allocno != NULL && j >= 0);
+ cover_class_allocnos_num[cover_class] = j + 1;
+ }
+ ira_assert (ALLOCNO_IN_GRAPH_P (allocno)
+ && ALLOCNO_COVER_CLASS (allocno) == cover_class
+ && (ALLOCNO_LEFT_CONFLICTS_NUM (allocno)
+ + ira_reg_class_nregs[cover_class][ALLOCNO_MODE
+ (allocno)]
+ > ALLOCNO_AVAILABLE_REGS_NUM (allocno)));
+ remove_allocno_from_bucket_and_push (allocno, false);
+ }
+ ira_assert (colorable_allocno_bucket == NULL
+ && uncolorable_allocno_bucket == NULL);
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ ira_assert (uncolorable_allocnos_num[cover_class] == 0);
+ if (uncolorable_allocnos_splay_tree[cover_class] != NULL)
+ splay_tree_delete (uncolorable_allocnos_splay_tree[cover_class]);
+ }
+}
+
+/* Pop the coloring stack and assign hard registers to the popped
+ allocnos. */
+static void
+pop_allocnos_from_stack (void)
+{
+ ira_allocno_t allocno;
+ enum reg_class cover_class;
+
+ for (;VEC_length (ira_allocno_t, allocno_stack_vec) != 0;)
+ {
+ allocno = VEC_pop (ira_allocno_t, allocno_stack_vec);
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Popping");
+ print_coalesced_allocno (allocno);
+ fprintf (ira_dump_file, " -- ");
+ }
+ if (cover_class == NO_REGS)
+ {
+ ALLOCNO_HARD_REGNO (allocno) = -1;
+ ALLOCNO_ASSIGNED_P (allocno) = true;
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (allocno) == NULL);
+ ira_assert
+ (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (allocno) == NULL);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "assign memory\n");
+ }
+ else if (assign_hard_reg (allocno, false))
+ {
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "assign reg %d\n",
+ ALLOCNO_HARD_REGNO (allocno));
+ }
+ else if (ALLOCNO_ASSIGNED_P (allocno))
+ {
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "spill\n");
+ }
+ ALLOCNO_IN_GRAPH_P (allocno) = true;
+ }
+}
+
+/* Set up number of available hard registers for ALLOCNO. */
+static void
+setup_allocno_available_regs_num (ira_allocno_t allocno)
+{
+ int i, n, hard_regs_num;
+ enum reg_class cover_class;
+ ira_allocno_t a;
+ HARD_REG_SET temp_set;
+
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ ALLOCNO_AVAILABLE_REGS_NUM (allocno) = ira_available_class_regs[cover_class];
+ if (cover_class == NO_REGS)
+ return;
+ CLEAR_HARD_REG_SET (temp_set);
+ ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) == allocno);
+ hard_regs_num = ira_class_hard_regs_num[cover_class];
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ IOR_HARD_REG_SET (temp_set, ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ if (a == allocno)
+ break;
+ }
+ for (n = 0, i = hard_regs_num - 1; i >= 0; i--)
+ if (TEST_HARD_REG_BIT (temp_set, ira_class_hard_regs[cover_class][i]))
+ n++;
+ if (internal_flag_ira_verbose > 2 && n > 0 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Reg %d of %s has %d regs less\n",
+ ALLOCNO_REGNO (allocno), reg_class_names[cover_class], n);
+ ALLOCNO_AVAILABLE_REGS_NUM (allocno) -= n;
+}
+
+/* Set up ALLOCNO_LEFT_CONFLICTS_NUM for ALLOCNO. */
+static void
+setup_allocno_left_conflicts_num (ira_allocno_t allocno)
+{
+ int i, hard_regs_num, hard_regno, conflict_allocnos_size;
+ ira_allocno_t a, conflict_allocno;
+ enum reg_class cover_class;
+ HARD_REG_SET temp_set;
+ ira_allocno_conflict_iterator aci;
+
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ hard_regs_num = ira_class_hard_regs_num[cover_class];
+ CLEAR_HARD_REG_SET (temp_set);
+ ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) == allocno);
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ IOR_HARD_REG_SET (temp_set, ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
+ if (a == allocno)
+ break;
+ }
+ AND_HARD_REG_SET (temp_set, reg_class_contents[cover_class]);
+ AND_COMPL_HARD_REG_SET (temp_set, ira_no_alloc_regs);
+ conflict_allocnos_size = 0;
+ if (! hard_reg_set_equal_p (temp_set, ira_zero_hard_reg_set))
+ for (i = 0; i < (int) hard_regs_num; i++)
+ {
+ hard_regno = ira_class_hard_regs[cover_class][i];
+ if (TEST_HARD_REG_BIT (temp_set, hard_regno))
+ {
+ conflict_allocnos_size++;
+ CLEAR_HARD_REG_BIT (temp_set, hard_regno);
+ if (hard_reg_set_equal_p (temp_set, ira_zero_hard_reg_set))
+ break;
+ }
+ }
+ CLEAR_HARD_REG_SET (temp_set);
+ if (allocno_coalesced_p)
+ bitmap_clear (processed_coalesced_allocno_bitmap);
+ if (cover_class != NO_REGS)
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_allocno, aci)
+ if (bitmap_bit_p (consideration_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ {
+ ira_assert (cover_class
+ == ALLOCNO_COVER_CLASS (conflict_allocno));
+ if (allocno_coalesced_p)
+ {
+ if (bitmap_bit_p (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno)))
+ continue;
+ bitmap_set_bit (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno));
+ }
+ if (! ALLOCNO_ASSIGNED_P (conflict_allocno))
+ conflict_allocnos_size
+ += (ira_reg_class_nregs
+ [cover_class][ALLOCNO_MODE (conflict_allocno)]);
+ else if ((hard_regno = ALLOCNO_HARD_REGNO (conflict_allocno))
+ >= 0)
+ {
+ int last = (hard_regno
+ + hard_regno_nregs
+ [hard_regno][ALLOCNO_MODE (conflict_allocno)]);
+
+ while (hard_regno < last)
+ {
+ if (! TEST_HARD_REG_BIT (temp_set, hard_regno))
+ {
+ conflict_allocnos_size++;
+ SET_HARD_REG_BIT (temp_set, hard_regno);
+ }
+ hard_regno++;
+ }
+ }
+ }
+ if (a == allocno)
+ break;
+ }
+ ALLOCNO_LEFT_CONFLICTS_NUM (allocno) = conflict_allocnos_size;
+}
+
+/* Put ALLOCNO in a bucket corresponding to its number and size of its
+ conflicting allocnos and hard registers. */
+static void
+put_allocno_into_bucket (ira_allocno_t allocno)
+{
+ int hard_regs_num;
+ enum reg_class cover_class;
+
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ hard_regs_num = ira_class_hard_regs_num[cover_class];
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) != allocno)
+ return;
+ ALLOCNO_IN_GRAPH_P (allocno) = true;
+ setup_allocno_left_conflicts_num (allocno);
+ setup_allocno_available_regs_num (allocno);
+ if (ALLOCNO_LEFT_CONFLICTS_NUM (allocno)
+ + ira_reg_class_nregs[cover_class][ALLOCNO_MODE (allocno)]
+ <= ALLOCNO_AVAILABLE_REGS_NUM (allocno))
+ add_ira_allocno_to_bucket (allocno, &colorable_allocno_bucket);
+ else
+ add_ira_allocno_to_bucket (allocno, &uncolorable_allocno_bucket);
+}
+
+/* The function is used to sort allocnos according to their execution
+ frequencies. */
+static int
+copy_freq_compare_func (const void *v1p, const void *v2p)
+{
+ ira_copy_t cp1 = *(const ira_copy_t *) v1p, cp2 = *(const ira_copy_t *) v2p;
+ int pri1, pri2;
+
+ pri1 = cp1->freq;
+ pri2 = cp2->freq;
+ if (pri2 - pri1)
+ return pri2 - pri1;
+
+ /* If freqencies are equal, sort by copies, so that the results of
+ qsort leave nothing to chance. */
+ return cp1->num - cp2->num;
+}
+
+/* Merge two sets of coalesced allocnos given correspondingly by
+ allocnos A1 and A2 (more accurately merging A2 set into A1
+ set). */
+static void
+merge_allocnos (ira_allocno_t a1, ira_allocno_t a2)
+{
+ ira_allocno_t a, first, last, next;
+
+ first = ALLOCNO_FIRST_COALESCED_ALLOCNO (a1);
+ if (first == ALLOCNO_FIRST_COALESCED_ALLOCNO (a2))
+ return;
+ for (last = a2, a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a2);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = first;
+ if (a == a2)
+ break;
+ last = a;
+ }
+ next = ALLOCNO_NEXT_COALESCED_ALLOCNO (first);
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (first) = a2;
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (last) = next;
+}
+
+/* Return TRUE if there are conflicting allocnos from two sets of
+ coalesced allocnos given correspondingly by allocnos A1 and A2. If
+ RELOAD_P is TRUE, we use live ranges to find conflicts because
+ conflicts are represented only for allocnos of the same cover class
+ and during the reload pass we coalesce allocnos for sharing stack
+ memory slots. */
+static bool
+coalesced_allocno_conflict_p (ira_allocno_t a1, ira_allocno_t a2,
+ bool reload_p)
+{
+ ira_allocno_t a, conflict_allocno;
+ ira_allocno_conflict_iterator aci;
+
+ if (allocno_coalesced_p)
+ {
+ bitmap_clear (processed_coalesced_allocno_bitmap);
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a1);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ bitmap_set_bit (processed_coalesced_allocno_bitmap, ALLOCNO_NUM (a));
+ if (a == a1)
+ break;
+ }
+ }
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a2);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ if (reload_p)
+ {
+ for (conflict_allocno = ALLOCNO_NEXT_COALESCED_ALLOCNO (a1);;
+ conflict_allocno
+ = ALLOCNO_NEXT_COALESCED_ALLOCNO (conflict_allocno))
+ {
+ if (ira_allocno_live_ranges_intersect_p (a, conflict_allocno))
+ return true;
+ if (conflict_allocno == a1)
+ break;
+ }
+ }
+ else
+ {
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_allocno, aci)
+ if (conflict_allocno == a1
+ || (allocno_coalesced_p
+ && bitmap_bit_p (processed_coalesced_allocno_bitmap,
+ ALLOCNO_NUM (conflict_allocno))))
+ return true;
+ }
+ if (a == a2)
+ break;
+ }
+ return false;
+}
+
+/* The major function for aggressive allocno coalescing. For the
+ reload pass (RELOAD_P) we coalesce only spilled allocnos. If some
+ allocnos have been coalesced, we set up flag
+ allocno_coalesced_p. */
+static void
+coalesce_allocnos (bool reload_p)
+{
+ ira_allocno_t a;
+ ira_copy_t cp, next_cp, *sorted_copies;
+ enum reg_class cover_class;
+ enum machine_mode mode;
+ unsigned int j;
+ int i, n, cp_num, regno;
+ bitmap_iterator bi;
+
+ sorted_copies = (ira_copy_t *) ira_allocate (ira_copies_num
+ * sizeof (ira_copy_t));
+ cp_num = 0;
+ /* Collect copies. */
+ EXECUTE_IF_SET_IN_BITMAP (coloring_allocno_bitmap, 0, j, bi)
+ {
+ a = ira_allocnos[j];
+ regno = ALLOCNO_REGNO (a);
+ if ((! reload_p && ALLOCNO_ASSIGNED_P (a))
+ || (reload_p
+ && (! ALLOCNO_ASSIGNED_P (a) || ALLOCNO_HARD_REGNO (a) >= 0
+ || (regno < ira_reg_equiv_len
+ && (ira_reg_equiv_const[regno] != NULL_RTX
+ || ira_reg_equiv_invariant_p[regno])))))
+ continue;
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ mode = ALLOCNO_MODE (a);
+ for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == a)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ regno = ALLOCNO_REGNO (cp->second);
+ if ((reload_p
+ || (ALLOCNO_COVER_CLASS (cp->second) == cover_class
+ && ALLOCNO_MODE (cp->second) == mode))
+ && cp->insn != NULL
+ && ((! reload_p && ! ALLOCNO_ASSIGNED_P (cp->second))
+ || (reload_p
+ && ALLOCNO_ASSIGNED_P (cp->second)
+ && ALLOCNO_HARD_REGNO (cp->second) < 0
+ && (regno >= ira_reg_equiv_len
+ || (! ira_reg_equiv_invariant_p[regno]
+ && ira_reg_equiv_const[regno] == NULL_RTX)))))
+ sorted_copies[cp_num++] = cp;
+ }
+ else if (cp->second == a)
+ next_cp = cp->next_second_allocno_copy;
+ else
+ gcc_unreachable ();
+ }
+ }
+ qsort (sorted_copies, cp_num, sizeof (ira_copy_t), copy_freq_compare_func);
+ /* Coalesced copies, most frequently executed first. */
+ for (; cp_num != 0;)
+ {
+ for (i = 0; i < cp_num; i++)
+ {
+ cp = sorted_copies[i];
+ if (! coalesced_allocno_conflict_p (cp->first, cp->second, reload_p))
+ {
+ allocno_coalesced_p = true;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf
+ (ira_dump_file,
+ " Coalescing copy %d:a%dr%d-a%dr%d (freq=%d)\n",
+ cp->num, ALLOCNO_NUM (cp->first), ALLOCNO_REGNO (cp->first),
+ ALLOCNO_NUM (cp->second), ALLOCNO_REGNO (cp->second),
+ cp->freq);
+ merge_allocnos (cp->first, cp->second);
+ i++;
+ break;
+ }
+ }
+ /* Collect the rest of copies. */
+ for (n = 0; i < cp_num; i++)
+ {
+ cp = sorted_copies[i];
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (cp->first)
+ != ALLOCNO_FIRST_COALESCED_ALLOCNO (cp->second))
+ sorted_copies[n++] = cp;
+ }
+ cp_num = n;
+ }
+ ira_free (sorted_copies);
+}
+
+/* Chaitin-Briggs coloring for allocnos in COLORING_ALLOCNO_BITMAP
+ taking into account allocnos in CONSIDERATION_ALLOCNO_BITMAP. */
+static void
+color_allocnos (void)
+{
+ unsigned int i;
+ bitmap_iterator bi;
+ ira_allocno_t a;
+
+ allocno_coalesced_p = false;
+ processed_coalesced_allocno_bitmap = ira_allocate_bitmap ();
+ if (flag_ira_coalesce)
+ coalesce_allocnos (false);
+ /* Put the allocnos into the corresponding buckets. */
+ colorable_allocno_bucket = NULL;
+ uncolorable_allocno_bucket = NULL;
+ EXECUTE_IF_SET_IN_BITMAP (coloring_allocno_bitmap, 0, i, bi)
+ {
+ a = ira_allocnos[i];
+ if (ALLOCNO_COVER_CLASS (a) == NO_REGS)
+ {
+ ALLOCNO_HARD_REGNO (a) = -1;
+ ALLOCNO_ASSIGNED_P (a) = true;
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
+ ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Spill");
+ print_coalesced_allocno (a);
+ fprintf (ira_dump_file, "\n");
+ }
+ continue;
+ }
+ put_allocno_into_bucket (a);
+ }
+ push_allocnos_to_stack ();
+ pop_allocnos_from_stack ();
+ if (flag_ira_coalesce)
+ /* We don't need coalesced allocnos for ira_reassign_pseudos. */
+ EXECUTE_IF_SET_IN_BITMAP (coloring_allocno_bitmap, 0, i, bi)
+ {
+ a = ira_allocnos[i];
+ ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a;
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a;
+ }
+ ira_free_bitmap (processed_coalesced_allocno_bitmap);
+ allocno_coalesced_p = false;
+}
+
+
+
+/* Output information about the loop given by its LOOP_TREE_NODE. */
+static void
+print_loop_title (ira_loop_tree_node_t loop_tree_node)
+{
+ unsigned int j;
+ bitmap_iterator bi;
+
+ ira_assert (loop_tree_node->loop != NULL);
+ fprintf (ira_dump_file,
+ "\n Loop %d (parent %d, header bb%d, depth %d)\n ref:",
+ loop_tree_node->loop->num,
+ (loop_tree_node->parent == NULL
+ ? -1 : loop_tree_node->parent->loop->num),
+ loop_tree_node->loop->header->index,
+ loop_depth (loop_tree_node->loop));
+ EXECUTE_IF_SET_IN_BITMAP (loop_tree_node->mentioned_allocnos, 0, j, bi)
+ fprintf (ira_dump_file, " %dr%d", j, ALLOCNO_REGNO (ira_allocnos[j]));
+ fprintf (ira_dump_file, "\n modified regnos:");
+ EXECUTE_IF_SET_IN_BITMAP (loop_tree_node->modified_regnos, 0, j, bi)
+ fprintf (ira_dump_file, " %d", j);
+ fprintf (ira_dump_file, "\n border:");
+ EXECUTE_IF_SET_IN_BITMAP (loop_tree_node->border_allocnos, 0, j, bi)
+ fprintf (ira_dump_file, " %dr%d", j, ALLOCNO_REGNO (ira_allocnos[j]));
+ fprintf (ira_dump_file, "\n Pressure:");
+ for (j = 0; (int) j < ira_reg_class_cover_size; j++)
+ {
+ enum reg_class cover_class;
+
+ cover_class = ira_reg_class_cover[j];
+ if (loop_tree_node->reg_pressure[cover_class] == 0)
+ continue;
+ fprintf (ira_dump_file, " %s=%d", reg_class_names[cover_class],
+ loop_tree_node->reg_pressure[cover_class]);
+ }
+ fprintf (ira_dump_file, "\n");
+}
+
+/* Color the allocnos inside loop (in the extreme case it can be all
+ of the function) given the corresponding LOOP_TREE_NODE. The
+ function is called for each loop during top-down traverse of the
+ loop tree. */
+static void
+color_pass (ira_loop_tree_node_t loop_tree_node)
+{
+ int regno, hard_regno, index = -1;
+ int cost, exit_freq, enter_freq;
+ unsigned int j;
+ bitmap_iterator bi;
+ enum machine_mode mode;
+ enum reg_class rclass, cover_class;
+ ira_allocno_t a, subloop_allocno;
+ ira_loop_tree_node_t subloop_node;
+
+ ira_assert (loop_tree_node->bb == NULL);
+ if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
+ print_loop_title (loop_tree_node);
+
+ bitmap_copy (coloring_allocno_bitmap, loop_tree_node->mentioned_allocnos);
+ bitmap_ior_into (coloring_allocno_bitmap, loop_tree_node->border_allocnos);
+ bitmap_copy (consideration_allocno_bitmap, coloring_allocno_bitmap);
+ EXECUTE_IF_SET_IN_BITMAP (consideration_allocno_bitmap, 0, j, bi)
+ {
+ a = ira_allocnos[j];
+ if (! ALLOCNO_ASSIGNED_P (a))
+ continue;
+ bitmap_clear_bit (coloring_allocno_bitmap, ALLOCNO_NUM (a));
+ }
+ /* Color all mentioned allocnos including transparent ones. */
+ color_allocnos ();
+ /* Process caps. They are processed just once. */
+ if (flag_ira_algorithm == IRA_ALGORITHM_MIXED
+ || flag_ira_algorithm == IRA_ALGORITHM_REGIONAL)
+ EXECUTE_IF_SET_IN_BITMAP (loop_tree_node->mentioned_allocnos, 0, j, bi)
+ {
+ a = ira_allocnos[j];
+ if (ALLOCNO_CAP_MEMBER (a) == NULL)
+ continue;
+ /* Remove from processing in the next loop. */
+ bitmap_clear_bit (consideration_allocno_bitmap, j);
+ rclass = ALLOCNO_COVER_CLASS (a);
+ if ((flag_ira_algorithm == IRA_ALGORITHM_MIXED
+ && loop_tree_node->reg_pressure[rclass]
+ <= ira_available_class_regs[rclass]))
+ {
+ mode = ALLOCNO_MODE (a);
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ if (hard_regno >= 0)
+ {
+ index = ira_class_hard_reg_index[rclass][hard_regno];
+ ira_assert (index >= 0);
+ }
+ regno = ALLOCNO_REGNO (a);
+ subloop_allocno = ALLOCNO_CAP_MEMBER (a);
+ subloop_node = ALLOCNO_LOOP_TREE_NODE (subloop_allocno);
+ ira_assert (!ALLOCNO_ASSIGNED_P (subloop_allocno));
+ ALLOCNO_HARD_REGNO (subloop_allocno) = hard_regno;
+ ALLOCNO_ASSIGNED_P (subloop_allocno) = true;
+ if (hard_regno >= 0)
+ update_copy_costs (subloop_allocno, true);
+ /* We don't need updated costs anymore: */
+ ira_free_allocno_updated_costs (subloop_allocno);
+ }
+ }
+ /* Update costs of the corresponding allocnos (not caps) in the
+ subloops. */
+ for (subloop_node = loop_tree_node->subloops;
+ subloop_node != NULL;
+ subloop_node = subloop_node->subloop_next)
+ {
+ ira_assert (subloop_node->bb == NULL);
+ EXECUTE_IF_SET_IN_BITMAP (consideration_allocno_bitmap, 0, j, bi)
+ {
+ a = ira_allocnos[j];
+ ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
+ mode = ALLOCNO_MODE (a);
+ rclass = ALLOCNO_COVER_CLASS (a);
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ if (hard_regno >= 0)
+ {
+ index = ira_class_hard_reg_index[rclass][hard_regno];
+ ira_assert (index >= 0);
+ }
+ regno = ALLOCNO_REGNO (a);
+ /* ??? conflict costs */
+ subloop_allocno = subloop_node->regno_allocno_map[regno];
+ if (subloop_allocno == NULL
+ || ALLOCNO_CAP (subloop_allocno) != NULL)
+ continue;
+ if ((flag_ira_algorithm == IRA_ALGORITHM_MIXED
+ && (loop_tree_node->reg_pressure[rclass]
+ <= ira_available_class_regs[rclass]))
+ || (hard_regno < 0
+ && ! bitmap_bit_p (subloop_node->mentioned_allocnos,
+ ALLOCNO_NUM (subloop_allocno))))
+ {
+ if (! ALLOCNO_ASSIGNED_P (subloop_allocno))
+ {
+ ALLOCNO_HARD_REGNO (subloop_allocno) = hard_regno;
+ ALLOCNO_ASSIGNED_P (subloop_allocno) = true;
+ if (hard_regno >= 0)
+ update_copy_costs (subloop_allocno, true);
+ /* We don't need updated costs anymore: */
+ ira_free_allocno_updated_costs (subloop_allocno);
+ }
+ continue;
+ }
+ exit_freq = ira_loop_edge_freq (subloop_node, regno, true);
+ enter_freq = ira_loop_edge_freq (subloop_node, regno, false);
+ ira_assert (regno < ira_reg_equiv_len);
+ if (ira_reg_equiv_invariant_p[regno]
+ || ira_reg_equiv_const[regno] != NULL_RTX)
+ {
+ if (! ALLOCNO_ASSIGNED_P (subloop_allocno))
+ {
+ ALLOCNO_HARD_REGNO (subloop_allocno) = hard_regno;
+ ALLOCNO_ASSIGNED_P (subloop_allocno) = true;
+ if (hard_regno >= 0)
+ update_copy_costs (subloop_allocno, true);
+ /* We don't need updated costs anymore: */
+ ira_free_allocno_updated_costs (subloop_allocno);
+ }
+ }
+ else if (hard_regno < 0)
+ {
+ ALLOCNO_UPDATED_MEMORY_COST (subloop_allocno)
+ -= ((ira_memory_move_cost[mode][rclass][1] * enter_freq)
+ + (ira_memory_move_cost[mode][rclass][0] * exit_freq));
+ }
+ else
+ {
+ cover_class = ALLOCNO_COVER_CLASS (subloop_allocno);
+ ira_allocate_and_set_costs
+ (&ALLOCNO_HARD_REG_COSTS (subloop_allocno), cover_class,
+ ALLOCNO_COVER_CLASS_COST (subloop_allocno));
+ ira_allocate_and_set_costs
+ (&ALLOCNO_CONFLICT_HARD_REG_COSTS (subloop_allocno),
+ cover_class, 0);
+ cost = (ira_register_move_cost[mode][rclass][rclass]
+ * (exit_freq + enter_freq));
+ ALLOCNO_HARD_REG_COSTS (subloop_allocno)[index] -= cost;
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (subloop_allocno)[index]
+ -= cost;
+ ALLOCNO_UPDATED_MEMORY_COST (subloop_allocno)
+ += (ira_memory_move_cost[mode][rclass][0] * enter_freq
+ + ira_memory_move_cost[mode][rclass][1] * exit_freq);
+ if (ALLOCNO_COVER_CLASS_COST (subloop_allocno)
+ > ALLOCNO_HARD_REG_COSTS (subloop_allocno)[index])
+ ALLOCNO_COVER_CLASS_COST (subloop_allocno)
+ = ALLOCNO_HARD_REG_COSTS (subloop_allocno)[index];
+ }
+ }
+ }
+}
+
+/* Initialize the common data for coloring and calls functions to do
+ Chaitin-Briggs and regional coloring. */
+static void
+do_coloring (void)
+{
+ coloring_allocno_bitmap = ira_allocate_bitmap ();
+ allocnos_for_spilling
+ = (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
+ * ira_allocnos_num);
+ splay_tree_node_pool = create_alloc_pool ("splay tree nodes",
+ sizeof (struct splay_tree_node_s),
+ 100);
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "\n**** Allocnos coloring:\n\n");
+
+ ira_traverse_loop_tree (false, ira_loop_tree_root, color_pass, NULL);
+
+ if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
+ ira_print_disposition (ira_dump_file);
+
+ free_alloc_pool (splay_tree_node_pool);
+ ira_free_bitmap (coloring_allocno_bitmap);
+ ira_free (allocnos_for_spilling);
+}
+
+
+
+/* Move spill/restore code, which are to be generated in ira-emit.c,
+ to less frequent points (if it is profitable) by reassigning some
+ allocnos (in loop with subloops containing in another loop) to
+ memory which results in longer live-range where the corresponding
+ pseudo-registers will be in memory. */
+static void
+move_spill_restore (void)
+{
+ int cost, regno, hard_regno, hard_regno2, index;
+ bool changed_p;
+ int enter_freq, exit_freq;
+ enum machine_mode mode;
+ enum reg_class rclass;
+ ira_allocno_t a, parent_allocno, subloop_allocno;
+ ira_loop_tree_node_t parent, loop_node, subloop_node;
+ ira_allocno_iterator ai;
+
+ for (;;)
+ {
+ changed_p = false;
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "New iteration of spill/restore move\n");
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ regno = ALLOCNO_REGNO (a);
+ loop_node = ALLOCNO_LOOP_TREE_NODE (a);
+ if (ALLOCNO_CAP_MEMBER (a) != NULL
+ || ALLOCNO_CAP (a) != NULL
+ || (hard_regno = ALLOCNO_HARD_REGNO (a)) < 0
+ || loop_node->children == NULL
+ /* don't do the optimization because it can create
+ copies and the reload pass can spill the allocno set
+ by copy although the allocno will not get memory
+ slot. */
+ || ira_reg_equiv_invariant_p[regno]
+ || ira_reg_equiv_const[regno] != NULL_RTX
+ || !bitmap_bit_p (loop_node->border_allocnos, ALLOCNO_NUM (a)))
+ continue;
+ mode = ALLOCNO_MODE (a);
+ rclass = ALLOCNO_COVER_CLASS (a);
+ index = ira_class_hard_reg_index[rclass][hard_regno];
+ ira_assert (index >= 0);
+ cost = (ALLOCNO_MEMORY_COST (a)
+ - (ALLOCNO_HARD_REG_COSTS (a) == NULL
+ ? ALLOCNO_COVER_CLASS_COST (a)
+ : ALLOCNO_HARD_REG_COSTS (a)[index]));
+ for (subloop_node = loop_node->subloops;
+ subloop_node != NULL;
+ subloop_node = subloop_node->subloop_next)
+ {
+ ira_assert (subloop_node->bb == NULL);
+ subloop_allocno = subloop_node->regno_allocno_map[regno];
+ if (subloop_allocno == NULL)
+ continue;
+ /* We have accumulated cost. To get the real cost of
+ allocno usage in the loop we should subtract costs of
+ the subloop allocnos. */
+ cost -= (ALLOCNO_MEMORY_COST (subloop_allocno)
+ - (ALLOCNO_HARD_REG_COSTS (subloop_allocno) == NULL
+ ? ALLOCNO_COVER_CLASS_COST (subloop_allocno)
+ : ALLOCNO_HARD_REG_COSTS (subloop_allocno)[index]));
+ exit_freq = ira_loop_edge_freq (subloop_node, regno, true);
+ enter_freq = ira_loop_edge_freq (subloop_node, regno, false);
+ if ((hard_regno2 = ALLOCNO_HARD_REGNO (subloop_allocno)) < 0)
+ cost -= (ira_memory_move_cost[mode][rclass][0] * exit_freq
+ + ira_memory_move_cost[mode][rclass][1] * enter_freq);
+ else
+ {
+ cost
+ += (ira_memory_move_cost[mode][rclass][0] * exit_freq
+ + ira_memory_move_cost[mode][rclass][1] * enter_freq);
+ if (hard_regno2 != hard_regno)
+ cost -= (ira_register_move_cost[mode][rclass][rclass]
+ * (exit_freq + enter_freq));
+ }
+ }
+ if ((parent = loop_node->parent) != NULL
+ && (parent_allocno = parent->regno_allocno_map[regno]) != NULL)
+ {
+ exit_freq = ira_loop_edge_freq (loop_node, regno, true);
+ enter_freq = ira_loop_edge_freq (loop_node, regno, false);
+ if ((hard_regno2 = ALLOCNO_HARD_REGNO (parent_allocno)) < 0)
+ cost -= (ira_memory_move_cost[mode][rclass][0] * exit_freq
+ + ira_memory_move_cost[mode][rclass][1] * enter_freq);
+ else
+ {
+ cost
+ += (ira_memory_move_cost[mode][rclass][1] * exit_freq
+ + ira_memory_move_cost[mode][rclass][0] * enter_freq);
+ if (hard_regno2 != hard_regno)
+ cost -= (ira_register_move_cost[mode][rclass][rclass]
+ * (exit_freq + enter_freq));
+ }
+ }
+ if (cost < 0)
+ {
+ ALLOCNO_HARD_REGNO (a) = -1;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf
+ (ira_dump_file,
+ " Moving spill/restore for a%dr%d up from loop %d",
+ ALLOCNO_NUM (a), regno, loop_node->loop->num);
+ fprintf (ira_dump_file, " - profit %d\n", -cost);
+ }
+ changed_p = true;
+ }
+ }
+ if (! changed_p)
+ break;
+ }
+}
+
+
+
+/* Update current hard reg costs and current conflict hard reg costs
+ for allocno A. It is done by processing its copies containing
+ other allocnos already assigned. */
+static void
+update_curr_costs (ira_allocno_t a)
+{
+ int i, hard_regno, cost;
+ enum machine_mode mode;
+ enum reg_class cover_class, rclass;
+ ira_allocno_t another_a;
+ ira_copy_t cp, next_cp;
+
+ ira_assert (! ALLOCNO_ASSIGNED_P (a));
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ if (cover_class == NO_REGS)
+ return;
+ mode = ALLOCNO_MODE (a);
+ for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
+ {
+ if (cp->first == a)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_a = cp->second;
+ }
+ else if (cp->second == a)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_a = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ if (cover_class != ALLOCNO_COVER_CLASS (another_a)
+ || ! ALLOCNO_ASSIGNED_P (another_a)
+ || (hard_regno = ALLOCNO_HARD_REGNO (another_a)) < 0)
+ continue;
+ rclass = REGNO_REG_CLASS (hard_regno);
+ i = ira_class_hard_reg_index[cover_class][hard_regno];
+ ira_assert (i >= 0);
+ cost = (cp->first == a
+ ? ira_register_move_cost[mode][rclass][cover_class]
+ : ira_register_move_cost[mode][cover_class][rclass]);
+ ira_allocate_and_set_or_copy_costs
+ (&ALLOCNO_UPDATED_HARD_REG_COSTS (a),
+ cover_class, ALLOCNO_COVER_CLASS_COST (a),
+ ALLOCNO_HARD_REG_COSTS (a));
+ ira_allocate_and_set_or_copy_costs
+ (&ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a),
+ cover_class, 0, ALLOCNO_CONFLICT_HARD_REG_COSTS (a));
+ ALLOCNO_UPDATED_HARD_REG_COSTS (a)[i] -= cp->freq * cost;
+ ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a)[i] -= cp->freq * cost;
+ }
+}
+
+/* Map: allocno number -> allocno priority. */
+static int *allocno_priorities;
+
+/* Allocate array ALLOCNO_PRIORITIES and set up priorities for N allocnos in
+ array CONSIDERATION_ALLOCNOS. */
+static void
+start_allocno_priorities (ira_allocno_t *consideration_allocnos, int n)
+{
+ int i, length;
+ ira_allocno_t a;
+ allocno_live_range_t r;
+
+ for (i = 0; i < n; i++)
+ {
+ a = consideration_allocnos[i];
+ for (length = 0, r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
+ length += r->finish - r->start + 1;
+ if (length == 0)
+ {
+ allocno_priorities[ALLOCNO_NUM (a)] = 0;
+ continue;
+ }
+ ira_assert (length > 0 && ALLOCNO_NREFS (a) >= 0);
+ allocno_priorities[ALLOCNO_NUM (a)]
+ = (((double) (floor_log2 (ALLOCNO_NREFS (a)) * ALLOCNO_FREQ (a))
+ / length)
+ * (10000 / REG_FREQ_MAX) * PSEUDO_REGNO_SIZE (ALLOCNO_REGNO (a)));
+ }
+}
+
+/* Sort allocnos according to their priorities which are calculated
+ analogous to ones in file `global.c'. */
+static int
+allocno_priority_compare_func (const void *v1p, const void *v2p)
+{
+ ira_allocno_t a1 = *(const ira_allocno_t *) v1p;
+ ira_allocno_t a2 = *(const ira_allocno_t *) v2p;
+ int pri1, pri2;
+
+ pri1 = allocno_priorities[ALLOCNO_NUM (a1)];
+ pri2 = allocno_priorities[ALLOCNO_NUM (a2)];
+ if (pri2 - pri1)
+ return pri2 - pri1;
+
+ /* If regs are equally good, sort by allocnos, so that the results of
+ qsort leave nothing to chance. */
+ return ALLOCNO_NUM (a1) - ALLOCNO_NUM (a2);
+}
+
+/* Try to assign hard registers to the unassigned allocnos and
+ allocnos conflicting with them or conflicting with allocnos whose
+ regno >= START_REGNO. The function is called after ira_flattening,
+ so more allocnos (including ones created in ira-emit.c) will have a
+ chance to get a hard register. We use simple assignment algorithm
+ based on priorities. */
+void
+ira_reassign_conflict_allocnos (int start_regno)
+{
+ int i, allocnos_to_color_num;
+ ira_allocno_t a, conflict_a;
+ ira_allocno_conflict_iterator aci;
+ enum reg_class cover_class;
+ bitmap allocnos_to_color;
+ ira_allocno_iterator ai;
+
+ allocnos_to_color = ira_allocate_bitmap ();
+ allocnos_to_color_num = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (! ALLOCNO_ASSIGNED_P (a)
+ && ! bitmap_bit_p (allocnos_to_color, ALLOCNO_NUM (a)))
+ {
+ if (ALLOCNO_COVER_CLASS (a) != NO_REGS)
+ sorted_allocnos[allocnos_to_color_num++] = a;
+ else
+ {
+ ALLOCNO_ASSIGNED_P (a) = true;
+ ALLOCNO_HARD_REGNO (a) = -1;
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
+ ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
+ }
+ bitmap_set_bit (allocnos_to_color, ALLOCNO_NUM (a));
+ }
+ if (ALLOCNO_REGNO (a) < start_regno
+ || (cover_class = ALLOCNO_COVER_CLASS (a)) == NO_REGS)
+ continue;
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_a, aci)
+ {
+ ira_assert (cover_class == ALLOCNO_COVER_CLASS (conflict_a));
+ if (bitmap_bit_p (allocnos_to_color, ALLOCNO_NUM (conflict_a)))
+ continue;
+ bitmap_set_bit (allocnos_to_color, ALLOCNO_NUM (conflict_a));
+ sorted_allocnos[allocnos_to_color_num++] = conflict_a;
+ }
+ }
+ ira_free_bitmap (allocnos_to_color);
+ if (allocnos_to_color_num > 1)
+ {
+ start_allocno_priorities (sorted_allocnos, allocnos_to_color_num);
+ qsort (sorted_allocnos, allocnos_to_color_num, sizeof (ira_allocno_t),
+ allocno_priority_compare_func);
+ }
+ for (i = 0; i < allocnos_to_color_num; i++)
+ {
+ a = sorted_allocnos[i];
+ ALLOCNO_ASSIGNED_P (a) = false;
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
+ ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
+ update_curr_costs (a);
+ }
+ for (i = 0; i < allocnos_to_color_num; i++)
+ {
+ a = sorted_allocnos[i];
+ if (assign_hard_reg (a, true))
+ {
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf
+ (ira_dump_file,
+ " Secondary allocation: assign hard reg %d to reg %d\n",
+ ALLOCNO_HARD_REGNO (a), ALLOCNO_REGNO (a));
+ }
+ }
+}
+
+
+
+/* This page contains code to coalesce memory stack slots used by
+ spilled allocnos. This results in smaller stack frame, better data
+ locality, and in smaller code for some architectures like
+ x86/x86_64 where insn size depends on address displacement value.
+ On the other hand, it can worsen insn scheduling after the RA but
+ in practice it is less important than smaller stack frames. */
+
+/* Usage cost and order number of coalesced allocno set to which
+ given pseudo register belongs to. */
+static int *regno_coalesced_allocno_cost;
+static int *regno_coalesced_allocno_num;
+
+/* Sort pseudos according frequencies of coalesced allocno sets they
+ belong to (putting most frequently ones first), and according to
+ coalesced allocno set order numbers. */
+static int
+coalesced_pseudo_reg_freq_compare (const void *v1p, const void *v2p)
+{
+ const int regno1 = *(const int *) v1p;
+ const int regno2 = *(const int *) v2p;
+ int diff;
+
+ if ((diff = (regno_coalesced_allocno_cost[regno2]
+ - regno_coalesced_allocno_cost[regno1])) != 0)
+ return diff;
+ if ((diff = (regno_coalesced_allocno_num[regno1]
+ - regno_coalesced_allocno_num[regno2])) != 0)
+ return diff;
+ return regno1 - regno2;
+}
+
+/* Widest width in which each pseudo reg is referred to (via subreg).
+ It is used for sorting pseudo registers. */
+static unsigned int *regno_max_ref_width;
+
+/* Redefine STACK_GROWS_DOWNWARD in terms of 0 or 1. */
+#ifdef STACK_GROWS_DOWNWARD
+# undef STACK_GROWS_DOWNWARD
+# define STACK_GROWS_DOWNWARD 1
+#else
+# define STACK_GROWS_DOWNWARD 0
+#endif
+
+/* Sort pseudos according their slot numbers (putting ones with
+ smaller numbers first, or last when the frame pointer is not
+ needed). */
+static int
+coalesced_pseudo_reg_slot_compare (const void *v1p, const void *v2p)
+{
+ const int regno1 = *(const int *) v1p;
+ const int regno2 = *(const int *) v2p;
+ ira_allocno_t a1 = ira_regno_allocno_map[regno1];
+ ira_allocno_t a2 = ira_regno_allocno_map[regno2];
+ int diff, slot_num1, slot_num2;
+ int total_size1, total_size2;
+
+ if (a1 == NULL || ALLOCNO_HARD_REGNO (a1) >= 0)
+ {
+ if (a2 == NULL || ALLOCNO_HARD_REGNO (a2) >= 0)
+ return (const int *) v1p - (const int *) v2p; /* Save the order. */
+ return 1;
+ }
+ else if (a2 == NULL || ALLOCNO_HARD_REGNO (a2) >= 0)
+ return -1;
+ slot_num1 = -ALLOCNO_HARD_REGNO (a1);
+ slot_num2 = -ALLOCNO_HARD_REGNO (a2);
+ if ((diff = slot_num1 - slot_num2) != 0)
+ return (frame_pointer_needed
+ || !FRAME_GROWS_DOWNWARD == STACK_GROWS_DOWNWARD ? diff : -diff);
+ total_size1 = MAX (PSEUDO_REGNO_BYTES (regno1), regno_max_ref_width[regno1]);
+ total_size2 = MAX (PSEUDO_REGNO_BYTES (regno2), regno_max_ref_width[regno2]);
+ if ((diff = total_size2 - total_size1) != 0)
+ return diff;
+ return (const int *) v1p - (const int *) v2p; /* Save the order. */
+}
+
+/* Setup REGNO_COALESCED_ALLOCNO_COST and REGNO_COALESCED_ALLOCNO_NUM
+ for coalesced allocno sets containing allocnos with their regnos
+ given in array PSEUDO_REGNOS of length N. */
+static void
+setup_coalesced_allocno_costs_and_nums (int *pseudo_regnos, int n)
+{
+ int i, num, regno, cost;
+ ira_allocno_t allocno, a;
+
+ for (num = i = 0; i < n; i++)
+ {
+ regno = pseudo_regnos[i];
+ allocno = ira_regno_allocno_map[regno];
+ if (allocno == NULL)
+ {
+ regno_coalesced_allocno_cost[regno] = 0;
+ regno_coalesced_allocno_num[regno] = ++num;
+ continue;
+ }
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) != allocno)
+ continue;
+ num++;
+ for (cost = 0, a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ cost += ALLOCNO_FREQ (a);
+ if (a == allocno)
+ break;
+ }
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ regno_coalesced_allocno_num[ALLOCNO_REGNO (a)] = num;
+ regno_coalesced_allocno_cost[ALLOCNO_REGNO (a)] = cost;
+ if (a == allocno)
+ break;
+ }
+ }
+}
+
+/* Collect spilled allocnos representing coalesced allocno sets (the
+ first coalesced allocno). The collected allocnos are returned
+ through array SPILLED_COALESCED_ALLOCNOS. The function returns the
+ number of the collected allocnos. The allocnos are given by their
+ regnos in array PSEUDO_REGNOS of length N. */
+static int
+collect_spilled_coalesced_allocnos (int *pseudo_regnos, int n,
+ ira_allocno_t *spilled_coalesced_allocnos)
+{
+ int i, num, regno;
+ ira_allocno_t allocno;
+
+ for (num = i = 0; i < n; i++)
+ {
+ regno = pseudo_regnos[i];
+ allocno = ira_regno_allocno_map[regno];
+ if (allocno == NULL || ALLOCNO_HARD_REGNO (allocno) >= 0
+ || ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) != allocno)
+ continue;
+ spilled_coalesced_allocnos[num++] = allocno;
+ }
+ return num;
+}
+
+/* We have coalesced allocnos involving in copies. Coalesce allocnos
+ further in order to share the same memory stack slot. Allocnos
+ representing sets of allocnos coalesced before the call are given
+ in array SPILLED_COALESCED_ALLOCNOS of length NUM. Return TRUE if
+ some allocnos were coalesced in the function. */
+static bool
+coalesce_spill_slots (ira_allocno_t *spilled_coalesced_allocnos, int num)
+{
+ int i, j;
+ ira_allocno_t allocno, a;
+ bool merged_p = false;
+
+ /* Coalesce non-conflicting spilled allocnos preferring most
+ frequently used. */
+ for (i = 0; i < num; i++)
+ {
+ allocno = spilled_coalesced_allocnos[i];
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) != allocno
+ || (ALLOCNO_REGNO (allocno) < ira_reg_equiv_len
+ && (ira_reg_equiv_invariant_p[ALLOCNO_REGNO (allocno)]
+ || ira_reg_equiv_const[ALLOCNO_REGNO (allocno)] != NULL_RTX)))
+ continue;
+ for (j = 0; j < i; j++)
+ {
+ a = spilled_coalesced_allocnos[j];
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (a) != a
+ || (ALLOCNO_REGNO (a) < ira_reg_equiv_len
+ && (ira_reg_equiv_invariant_p[ALLOCNO_REGNO (a)]
+ || ira_reg_equiv_const[ALLOCNO_REGNO (a)] != NULL_RTX))
+ || coalesced_allocno_conflict_p (allocno, a, true))
+ continue;
+ allocno_coalesced_p = true;
+ merged_p = true;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Coalescing spilled allocnos a%dr%d->a%dr%d\n",
+ ALLOCNO_NUM (allocno), ALLOCNO_REGNO (allocno),
+ ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
+ merge_allocnos (a, allocno);
+ ira_assert (ALLOCNO_FIRST_COALESCED_ALLOCNO (a) == a);
+ }
+ }
+ return merged_p;
+}
+
+/* Sort pseudo-register numbers in array PSEUDO_REGNOS of length N for
+ subsequent assigning stack slots to them in the reload pass. To do
+ this we coalesce spilled allocnos first to decrease the number of
+ memory-memory move insns. This function is called by the
+ reload. */
+void
+ira_sort_regnos_for_alter_reg (int *pseudo_regnos, int n,
+ unsigned int *reg_max_ref_width)
+{
+ int max_regno = max_reg_num ();
+ int i, regno, num, slot_num;
+ ira_allocno_t allocno, a;
+ ira_allocno_iterator ai;
+ ira_allocno_t *spilled_coalesced_allocnos;
+
+ processed_coalesced_allocno_bitmap = ira_allocate_bitmap ();
+ /* Set up allocnos can be coalesced. */
+ coloring_allocno_bitmap = ira_allocate_bitmap ();
+ for (i = 0; i < n; i++)
+ {
+ regno = pseudo_regnos[i];
+ allocno = ira_regno_allocno_map[regno];
+ if (allocno != NULL)
+ bitmap_set_bit (coloring_allocno_bitmap,
+ ALLOCNO_NUM (allocno));
+ }
+ allocno_coalesced_p = false;
+ coalesce_allocnos (true);
+ ira_free_bitmap (coloring_allocno_bitmap);
+ regno_coalesced_allocno_cost
+ = (int *) ira_allocate (max_regno * sizeof (int));
+ regno_coalesced_allocno_num
+ = (int *) ira_allocate (max_regno * sizeof (int));
+ memset (regno_coalesced_allocno_num, 0, max_regno * sizeof (int));
+ setup_coalesced_allocno_costs_and_nums (pseudo_regnos, n);
+ /* Sort regnos according frequencies of the corresponding coalesced
+ allocno sets. */
+ qsort (pseudo_regnos, n, sizeof (int), coalesced_pseudo_reg_freq_compare);
+ spilled_coalesced_allocnos
+ = (ira_allocno_t *) ira_allocate (ira_allocnos_num
+ * sizeof (ira_allocno_t));
+ /* Collect allocnos representing the spilled coalesced allocno
+ sets. */
+ num = collect_spilled_coalesced_allocnos (pseudo_regnos, n,
+ spilled_coalesced_allocnos);
+ if (flag_ira_share_spill_slots
+ && coalesce_spill_slots (spilled_coalesced_allocnos, num))
+ {
+ setup_coalesced_allocno_costs_and_nums (pseudo_regnos, n);
+ qsort (pseudo_regnos, n, sizeof (int),
+ coalesced_pseudo_reg_freq_compare);
+ num = collect_spilled_coalesced_allocnos (pseudo_regnos, n,
+ spilled_coalesced_allocnos);
+ }
+ ira_free_bitmap (processed_coalesced_allocno_bitmap);
+ allocno_coalesced_p = false;
+ /* Assign stack slot numbers to spilled allocno sets, use smaller
+ numbers for most frequently used coalesced allocnos. -1 is
+ reserved for dynamic search of stack slots for pseudos spilled by
+ the reload. */
+ slot_num = 1;
+ for (i = 0; i < num; i++)
+ {
+ allocno = spilled_coalesced_allocnos[i];
+ if (ALLOCNO_FIRST_COALESCED_ALLOCNO (allocno) != allocno
+ || ALLOCNO_HARD_REGNO (allocno) >= 0
+ || (ALLOCNO_REGNO (allocno) < ira_reg_equiv_len
+ && (ira_reg_equiv_invariant_p[ALLOCNO_REGNO (allocno)]
+ || ira_reg_equiv_const[ALLOCNO_REGNO (allocno)] != NULL_RTX)))
+ continue;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Slot %d (freq,size):", slot_num);
+ slot_num++;
+ for (a = ALLOCNO_NEXT_COALESCED_ALLOCNO (allocno);;
+ a = ALLOCNO_NEXT_COALESCED_ALLOCNO (a))
+ {
+ ira_assert (ALLOCNO_HARD_REGNO (a) < 0);
+ ALLOCNO_HARD_REGNO (a) = -slot_num;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " a%dr%d(%d,%d)",
+ ALLOCNO_NUM (a), ALLOCNO_REGNO (a), ALLOCNO_FREQ (a),
+ MAX (PSEUDO_REGNO_BYTES (ALLOCNO_REGNO (a)),
+ reg_max_ref_width[ALLOCNO_REGNO (a)]));
+
+ if (a == allocno)
+ break;
+ }
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "\n");
+ }
+ ira_spilled_reg_stack_slots_num = slot_num - 1;
+ ira_free (spilled_coalesced_allocnos);
+ /* Sort regnos according the slot numbers. */
+ regno_max_ref_width = reg_max_ref_width;
+ qsort (pseudo_regnos, n, sizeof (int), coalesced_pseudo_reg_slot_compare);
+ /* Uncoalesce allocnos which is necessary for (re)assigning during
+ the reload pass. */
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ ALLOCNO_FIRST_COALESCED_ALLOCNO (a) = a;
+ ALLOCNO_NEXT_COALESCED_ALLOCNO (a) = a;
+ }
+ ira_free (regno_coalesced_allocno_num);
+ ira_free (regno_coalesced_allocno_cost);
+}
+
+
+
+/* This page contains code used by the reload pass to improve the
+ final code. */
+
+/* The function is called from reload to mark changes in the
+ allocation of REGNO made by the reload. Remember that reg_renumber
+ reflects the change result. */
+void
+ira_mark_allocation_change (int regno)
+{
+ ira_allocno_t a = ira_regno_allocno_map[regno];
+ int old_hard_regno, hard_regno, cost;
+ enum reg_class cover_class = ALLOCNO_COVER_CLASS (a);
+
+ ira_assert (a != NULL);
+ hard_regno = reg_renumber[regno];
+ if ((old_hard_regno = ALLOCNO_HARD_REGNO (a)) == hard_regno)
+ return;
+ if (old_hard_regno < 0)
+ cost = -ALLOCNO_MEMORY_COST (a);
+ else
+ {
+ ira_assert (ira_class_hard_reg_index[cover_class][old_hard_regno] >= 0);
+ cost = -(ALLOCNO_HARD_REG_COSTS (a) == NULL
+ ? ALLOCNO_COVER_CLASS_COST (a)
+ : ALLOCNO_HARD_REG_COSTS (a)
+ [ira_class_hard_reg_index[cover_class][old_hard_regno]]);
+ update_copy_costs (a, false);
+ }
+ ira_overall_cost -= cost;
+ ALLOCNO_HARD_REGNO (a) = hard_regno;
+ if (hard_regno < 0)
+ {
+ ALLOCNO_HARD_REGNO (a) = -1;
+ cost += ALLOCNO_MEMORY_COST (a);
+ }
+ else if (ira_class_hard_reg_index[cover_class][hard_regno] >= 0)
+ {
+ cost += (ALLOCNO_HARD_REG_COSTS (a) == NULL
+ ? ALLOCNO_COVER_CLASS_COST (a)
+ : ALLOCNO_HARD_REG_COSTS (a)
+ [ira_class_hard_reg_index[cover_class][hard_regno]]);
+ update_copy_costs (a, true);
+ }
+ else
+ /* Reload changed class of the allocno. */
+ cost = 0;
+ ira_overall_cost += cost;
+}
+
+/* This function is called when reload deletes memory-memory move. In
+ this case we marks that the allocation of the corresponding
+ allocnos should be not changed in future. Otherwise we risk to get
+ a wrong code. */
+void
+ira_mark_memory_move_deletion (int dst_regno, int src_regno)
+{
+ ira_allocno_t dst = ira_regno_allocno_map[dst_regno];
+ ira_allocno_t src = ira_regno_allocno_map[src_regno];
+
+ ira_assert (dst != NULL && src != NULL
+ && ALLOCNO_HARD_REGNO (dst) < 0
+ && ALLOCNO_HARD_REGNO (src) < 0);
+ ALLOCNO_DONT_REASSIGN_P (dst) = true;
+ ALLOCNO_DONT_REASSIGN_P (src) = true;
+}
+
+/* Try to assign a hard register (except for FORBIDDEN_REGS) to
+ allocno A and return TRUE in the case of success. That is an
+ analog of retry_global_alloc for IRA. */
+static bool
+allocno_reload_assign (ira_allocno_t a, HARD_REG_SET forbidden_regs)
+{
+ int hard_regno;
+ enum reg_class cover_class;
+ int regno = ALLOCNO_REGNO (a);
+
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), forbidden_regs);
+ if (! flag_caller_saves && ALLOCNO_CALLS_CROSSED_NUM (a) != 0)
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), call_used_reg_set);
+ ALLOCNO_ASSIGNED_P (a) = false;
+ ira_assert (ALLOCNO_UPDATED_HARD_REG_COSTS (a) == NULL);
+ ira_assert (ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS (a) == NULL);
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ update_curr_costs (a);
+ assign_hard_reg (a, true);
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ reg_renumber[regno] = hard_regno;
+ if (hard_regno < 0)
+ ALLOCNO_HARD_REGNO (a) = -1;
+ else
+ {
+ ira_assert (ira_class_hard_reg_index[cover_class][hard_regno] >= 0);
+ ira_overall_cost -= (ALLOCNO_MEMORY_COST (a)
+ - (ALLOCNO_HARD_REG_COSTS (a) == NULL
+ ? ALLOCNO_COVER_CLASS_COST (a)
+ : ALLOCNO_HARD_REG_COSTS (a)
+ [ira_class_hard_reg_index
+ [cover_class][hard_regno]]));
+ if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0
+ && ! ira_hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a),
+ call_used_reg_set))
+ {
+ ira_assert (flag_caller_saves);
+ caller_save_needed = 1;
+ }
+ }
+
+ /* If we found a hard register, modify the RTL for the pseudo
+ register to show the hard register, and mark the pseudo register
+ live. */
+ if (reg_renumber[regno] >= 0)
+ {
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, ": reassign to %d\n", reg_renumber[regno]);
+ SET_REGNO (regno_reg_rtx[regno], reg_renumber[regno]);
+ mark_home_live (regno);
+ }
+ else if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "\n");
+
+ return reg_renumber[regno] >= 0;
+}
+
+/* Sort pseudos according their usage frequencies (putting most
+ frequently ones first). */
+static int
+pseudo_reg_compare (const void *v1p, const void *v2p)
+{
+ int regno1 = *(const int *) v1p;
+ int regno2 = *(const int *) v2p;
+ int diff;
+
+ if ((diff = REG_FREQ (regno2) - REG_FREQ (regno1)) != 0)
+ return diff;
+ return regno1 - regno2;
+}
+
+/* Try to allocate hard registers to SPILLED_PSEUDO_REGS (there are
+ NUM of them) or spilled pseudos conflicting with pseudos in
+ SPILLED_PSEUDO_REGS. Return TRUE and update SPILLED, if the
+ allocation has been changed. The function doesn't use
+ BAD_SPILL_REGS and hard registers in PSEUDO_FORBIDDEN_REGS and
+ PSEUDO_PREVIOUS_REGS for the corresponding pseudos. The function
+ is called by the reload pass at the end of each reload
+ iteration. */
+bool
+ira_reassign_pseudos (int *spilled_pseudo_regs, int num,
+ HARD_REG_SET bad_spill_regs,
+ HARD_REG_SET *pseudo_forbidden_regs,
+ HARD_REG_SET *pseudo_previous_regs, bitmap spilled)
+{
+ int i, m, n, regno;
+ bool changed_p;
+ ira_allocno_t a, conflict_a;
+ HARD_REG_SET forbidden_regs;
+ ira_allocno_conflict_iterator aci;
+
+ if (num > 1)
+ qsort (spilled_pseudo_regs, num, sizeof (int), pseudo_reg_compare);
+ changed_p = false;
+ /* Try to assign hard registers to pseudos from
+ SPILLED_PSEUDO_REGS. */
+ for (m = i = 0; i < num; i++)
+ {
+ regno = spilled_pseudo_regs[i];
+ COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs);
+ IOR_HARD_REG_SET (forbidden_regs, pseudo_forbidden_regs[regno]);
+ IOR_HARD_REG_SET (forbidden_regs, pseudo_previous_regs[regno]);
+ gcc_assert (reg_renumber[regno] < 0);
+ a = ira_regno_allocno_map[regno];
+ ira_mark_allocation_change (regno);
+ ira_assert (reg_renumber[regno] < 0);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Spill %d(a%d), cost=%d", regno, ALLOCNO_NUM (a),
+ ALLOCNO_MEMORY_COST (a)
+ - ALLOCNO_COVER_CLASS_COST (a));
+ allocno_reload_assign (a, forbidden_regs);
+ if (reg_renumber[regno] >= 0)
+ {
+ CLEAR_REGNO_REG_SET (spilled, regno);
+ changed_p = true;
+ }
+ else
+ spilled_pseudo_regs[m++] = regno;
+ }
+ if (m == 0)
+ return changed_p;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, " Spilled regs");
+ for (i = 0; i < m; i++)
+ fprintf (ira_dump_file, " %d", spilled_pseudo_regs[i]);
+ fprintf (ira_dump_file, "\n");
+ }
+ /* Try to assign hard registers to pseudos conflicting with ones
+ from SPILLED_PSEUDO_REGS. */
+ for (i = n = 0; i < m; i++)
+ {
+ regno = spilled_pseudo_regs[i];
+ a = ira_regno_allocno_map[regno];
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_a, aci)
+ if (ALLOCNO_HARD_REGNO (conflict_a) < 0
+ && ! ALLOCNO_DONT_REASSIGN_P (conflict_a)
+ && ! bitmap_bit_p (consideration_allocno_bitmap,
+ ALLOCNO_NUM (conflict_a)))
+ {
+ sorted_allocnos[n++] = conflict_a;
+ bitmap_set_bit (consideration_allocno_bitmap,
+ ALLOCNO_NUM (conflict_a));
+ }
+ }
+ if (n != 0)
+ {
+ start_allocno_priorities (sorted_allocnos, n);
+ qsort (sorted_allocnos, n, sizeof (ira_allocno_t),
+ allocno_priority_compare_func);
+ for (i = 0; i < n; i++)
+ {
+ a = sorted_allocnos[i];
+ regno = ALLOCNO_REGNO (a);
+ COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs);
+ IOR_HARD_REG_SET (forbidden_regs, pseudo_forbidden_regs[regno]);
+ IOR_HARD_REG_SET (forbidden_regs, pseudo_previous_regs[regno]);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Try assign %d(a%d), cost=%d",
+ regno, ALLOCNO_NUM (a),
+ ALLOCNO_MEMORY_COST (a)
+ - ALLOCNO_COVER_CLASS_COST (a));
+ if (allocno_reload_assign (a, forbidden_regs))
+ {
+ changed_p = true;
+ bitmap_clear_bit (spilled, regno);
+ }
+ }
+ }
+ return changed_p;
+}
+
+/* The function is called by reload and returns already allocated
+ stack slot (if any) for REGNO with given INHERENT_SIZE and
+ TOTAL_SIZE. In the case of failure to find a slot which can be
+ used for REGNO, the function returns NULL. */
+rtx
+ira_reuse_stack_slot (int regno, unsigned int inherent_size,
+ unsigned int total_size)
+{
+ unsigned int i;
+ int slot_num, best_slot_num;
+ int cost, best_cost;
+ ira_copy_t cp, next_cp;
+ ira_allocno_t another_allocno, allocno = ira_regno_allocno_map[regno];
+ rtx x;
+ bitmap_iterator bi;
+ struct ira_spilled_reg_stack_slot *slot = NULL;
+
+ ira_assert (flag_ira && inherent_size == PSEUDO_REGNO_BYTES (regno)
+ && inherent_size <= total_size
+ && ALLOCNO_HARD_REGNO (allocno) < 0);
+ if (! flag_ira_share_spill_slots)
+ return NULL_RTX;
+ slot_num = -ALLOCNO_HARD_REGNO (allocno) - 2;
+ if (slot_num != -1)
+ {
+ slot = &ira_spilled_reg_stack_slots[slot_num];
+ x = slot->mem;
+ }
+ else
+ {
+ best_cost = best_slot_num = -1;
+ x = NULL_RTX;
+ /* It means that the pseudo was spilled in the reload pass, try
+ to reuse a slot. */
+ for (slot_num = 0;
+ slot_num < ira_spilled_reg_stack_slots_num;
+ slot_num++)
+ {
+ slot = &ira_spilled_reg_stack_slots[slot_num];
+ if (slot->mem == NULL_RTX)
+ continue;
+ if (slot->width < total_size
+ || GET_MODE_SIZE (GET_MODE (slot->mem)) < inherent_size)
+ continue;
+
+ EXECUTE_IF_SET_IN_BITMAP (&slot->spilled_regs,
+ FIRST_PSEUDO_REGISTER, i, bi)
+ {
+ another_allocno = ira_regno_allocno_map[i];
+ if (ira_allocno_live_ranges_intersect_p (allocno,
+ another_allocno))
+ goto cont;
+ }
+ for (cost = 0, cp = ALLOCNO_COPIES (allocno);
+ cp != NULL;
+ cp = next_cp)
+ {
+ if (cp->first == allocno)
+ {
+ next_cp = cp->next_first_allocno_copy;
+ another_allocno = cp->second;
+ }
+ else if (cp->second == allocno)
+ {
+ next_cp = cp->next_second_allocno_copy;
+ another_allocno = cp->first;
+ }
+ else
+ gcc_unreachable ();
+ if (cp->insn == NULL_RTX)
+ continue;
+ if (bitmap_bit_p (&slot->spilled_regs,
+ ALLOCNO_REGNO (another_allocno)))
+ cost += cp->freq;
+ }
+ if (cost > best_cost)
+ {
+ best_cost = cost;
+ best_slot_num = slot_num;
+ }
+ cont:
+ ;
+ }
+ if (best_cost >= 0)
+ {
+ slot = &ira_spilled_reg_stack_slots[best_slot_num];
+ SET_REGNO_REG_SET (&slot->spilled_regs, regno);
+ x = slot->mem;
+ ALLOCNO_HARD_REGNO (allocno) = -best_slot_num - 2;
+ }
+ }
+ if (x != NULL_RTX)
+ {
+ ira_assert (slot->width >= total_size);
+ EXECUTE_IF_SET_IN_BITMAP (&slot->spilled_regs,
+ FIRST_PSEUDO_REGISTER, i, bi)
+ {
+ ira_assert (! ira_pseudo_live_ranges_intersect_p (regno, i));
+ }
+ SET_REGNO_REG_SET (&slot->spilled_regs, regno);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file)
+ {
+ fprintf (ira_dump_file, " Assigning %d(freq=%d) slot %d of",
+ regno, REG_FREQ (regno), slot_num);
+ EXECUTE_IF_SET_IN_BITMAP (&slot->spilled_regs,
+ FIRST_PSEUDO_REGISTER, i, bi)
+ {
+ if ((unsigned) regno != i)
+ fprintf (ira_dump_file, " %d", i);
+ }
+ fprintf (ira_dump_file, "\n");
+ }
+ }
+ return x;
+}
+
+/* This is called by reload every time a new stack slot X with
+ TOTAL_SIZE was allocated for REGNO. We store this info for
+ subsequent ira_reuse_stack_slot calls. */
+void
+ira_mark_new_stack_slot (rtx x, int regno, unsigned int total_size)
+{
+ struct ira_spilled_reg_stack_slot *slot;
+ int slot_num;
+ ira_allocno_t allocno;
+
+ ira_assert (flag_ira && PSEUDO_REGNO_BYTES (regno) <= total_size);
+ allocno = ira_regno_allocno_map[regno];
+ slot_num = -ALLOCNO_HARD_REGNO (allocno) - 2;
+ if (slot_num == -1)
+ {
+ slot_num = ira_spilled_reg_stack_slots_num++;
+ ALLOCNO_HARD_REGNO (allocno) = -slot_num - 2;
+ }
+ slot = &ira_spilled_reg_stack_slots[slot_num];
+ INIT_REG_SET (&slot->spilled_regs);
+ SET_REGNO_REG_SET (&slot->spilled_regs, regno);
+ slot->mem = x;
+ slot->width = total_size;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file)
+ fprintf (ira_dump_file, " Assigning %d(freq=%d) a new slot %d\n",
+ regno, REG_FREQ (regno), slot_num);
+}
+
+
+/* Return spill cost for pseudo-registers whose numbers are in array
+ REGNOS (with a negative number as an end marker) for reload with
+ given IN and OUT for INSN. Return also number points (through
+ EXCESS_PRESSURE_LIVE_LENGTH) where the pseudo-register lives and
+ the register pressure is high, number of references of the
+ pseudo-registers (through NREFS), number of callee-clobbered
+ hard-registers occupied by the pseudo-registers (through
+ CALL_USED_COUNT), and the first hard regno occupied by the
+ pseudo-registers (through FIRST_HARD_REGNO). */
+static int
+calculate_spill_cost (int *regnos, rtx in, rtx out, rtx insn,
+ int *excess_pressure_live_length,
+ int *nrefs, int *call_used_count, int *first_hard_regno)
+{
+ int i, cost, regno, hard_regno, j, count, saved_cost, nregs;
+ bool in_p, out_p;
+ int length;
+ ira_allocno_t a;
+
+ *nrefs = 0;
+ for (length = count = cost = i = 0;; i++)
+ {
+ regno = regnos[i];
+ if (regno < 0)
+ break;
+ *nrefs += REG_N_REFS (regno);
+ hard_regno = reg_renumber[regno];
+ ira_assert (hard_regno >= 0);
+ a = ira_regno_allocno_map[regno];
+ length += ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
+ cost += ALLOCNO_MEMORY_COST (a) - ALLOCNO_COVER_CLASS_COST (a);
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (a)];
+ for (j = 0; j < nregs; j++)
+ if (! TEST_HARD_REG_BIT (call_used_reg_set, hard_regno + j))
+ break;
+ if (j == nregs)
+ count++;
+ in_p = in && REG_P (in) && (int) REGNO (in) == hard_regno;
+ out_p = out && REG_P (out) && (int) REGNO (out) == hard_regno;
+ if ((in_p || out_p)
+ && find_regno_note (insn, REG_DEAD, hard_regno) != NULL_RTX)
+ {
+ saved_cost = 0;
+ if (in_p)
+ saved_cost += ira_memory_move_cost
+ [ALLOCNO_MODE (a)][ALLOCNO_COVER_CLASS (a)][1];
+ if (out_p)
+ saved_cost
+ += ira_memory_move_cost
+ [ALLOCNO_MODE (a)][ALLOCNO_COVER_CLASS (a)][0];
+ cost -= REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn)) * saved_cost;
+ }
+ }
+ *excess_pressure_live_length = length;
+ *call_used_count = count;
+ hard_regno = -1;
+ if (regnos[0] >= 0)
+ {
+ hard_regno = reg_renumber[regnos[0]];
+ }
+ *first_hard_regno = hard_regno;
+ return cost;
+}
+
+/* Return TRUE if spilling pseudo-registers whose numbers are in array
+ REGNOS is better than spilling pseudo-registers with numbers in
+ OTHER_REGNOS for reload with given IN and OUT for INSN. The
+ function used by the reload pass to make better register spilling
+ decisions. */
+bool
+ira_better_spill_reload_regno_p (int *regnos, int *other_regnos,
+ rtx in, rtx out, rtx insn)
+{
+ int cost, other_cost;
+ int length, other_length;
+ int nrefs, other_nrefs;
+ int call_used_count, other_call_used_count;
+ int hard_regno, other_hard_regno;
+
+ cost = calculate_spill_cost (regnos, in, out, insn,
+ &length, &nrefs, &call_used_count, &hard_regno);
+ other_cost = calculate_spill_cost (other_regnos, in, out, insn,
+ &other_length, &other_nrefs,
+ &other_call_used_count,
+ &other_hard_regno);
+ if (nrefs == 0 && other_nrefs != 0)
+ return true;
+ if (nrefs != 0 && other_nrefs == 0)
+ return false;
+ if (cost != other_cost)
+ return cost < other_cost;
+ if (length != other_length)
+ return length > other_length;
+#ifdef REG_ALLOC_ORDER
+ if (hard_regno >= 0 && other_hard_regno >= 0)
+ return (inv_reg_alloc_order[hard_regno]
+ < inv_reg_alloc_order[other_hard_regno]);
+#else
+ if (call_used_count != other_call_used_count)
+ return call_used_count > other_call_used_count;
+#endif
+ return false;
+}
+
+
+
+/* Allocate and initialize data necessary for assign_hard_reg. */
+void
+ira_initiate_assign (void)
+{
+ sorted_allocnos
+ = (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
+ * ira_allocnos_num);
+ consideration_allocno_bitmap = ira_allocate_bitmap ();
+ initiate_cost_update ();
+ allocno_priorities = (int *) ira_allocate (sizeof (int) * ira_allocnos_num);
+}
+
+/* Deallocate data used by assign_hard_reg. */
+void
+ira_finish_assign (void)
+{
+ ira_free (sorted_allocnos);
+ ira_free_bitmap (consideration_allocno_bitmap);
+ finish_cost_update ();
+ ira_free (allocno_priorities);
+}
+
+
+
+/* Entry function doing color-based register allocation. */
+void
+ira_color (void)
+{
+ allocno_stack_vec = VEC_alloc (ira_allocno_t, heap, ira_allocnos_num);
+ removed_splay_allocno_vec
+ = VEC_alloc (ira_allocno_t, heap, ira_allocnos_num);
+ memset (allocated_hardreg_p, 0, sizeof (allocated_hardreg_p));
+ ira_initiate_assign ();
+ do_coloring ();
+ ira_finish_assign ();
+ VEC_free (ira_allocno_t, heap, removed_splay_allocno_vec);
+ VEC_free (ira_allocno_t, heap, allocno_stack_vec);
+ move_spill_restore ();
+}
+
+
+
+/* This page contains a simple register allocator without usage of
+ allocno conflicts. This is used for fast allocation for -O0. */
+
+/* Do register allocation by not using allocno conflicts. It uses
+ only allocno live ranges. The algorithm is close to Chow's
+ priority coloring. */
+void
+ira_fast_allocation (void)
+{
+ int i, j, k, l, num, class_size, hard_regno;
+#ifdef STACK_REGS
+ bool no_stack_reg_p;
+#endif
+ enum reg_class cover_class;
+ enum machine_mode mode;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+ allocno_live_range_t r;
+ HARD_REG_SET conflict_hard_regs, *used_hard_regs;
+
+ allocno_priorities = (int *) ira_allocate (sizeof (int) * ira_allocnos_num);
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ l = ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a);
+ if (l <= 0)
+ l = 1;
+ allocno_priorities[ALLOCNO_NUM (a)]
+ = (((double) (floor_log2 (ALLOCNO_NREFS (a))
+ * (ALLOCNO_MEMORY_COST (a)
+ - ALLOCNO_COVER_CLASS_COST (a))) / l)
+ * (10000 / REG_FREQ_MAX)
+ * ira_reg_class_nregs[ALLOCNO_COVER_CLASS (a)][ALLOCNO_MODE (a)]);
+ }
+ used_hard_regs = (HARD_REG_SET *) ira_allocate (sizeof (HARD_REG_SET)
+ * ira_max_point);
+ for (i = 0; i < ira_max_point; i++)
+ CLEAR_HARD_REG_SET (used_hard_regs[i]);
+ sorted_allocnos = (ira_allocno_t *) ira_allocate (sizeof (ira_allocno_t)
+ * ira_allocnos_num);
+ num = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ sorted_allocnos[num++] = a;
+ qsort (sorted_allocnos, ira_allocnos_num, sizeof (ira_allocno_t),
+ allocno_priority_compare_func);
+ for (i = 0; i < num; i++)
+ {
+ a = sorted_allocnos[i];
+ COPY_HARD_REG_SET (conflict_hard_regs, ALLOCNO_CONFLICT_HARD_REGS (a));
+ for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
+ for (j = r->start; j <= r->finish; j++)
+ IOR_HARD_REG_SET (conflict_hard_regs, used_hard_regs[j]);
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ ALLOCNO_ASSIGNED_P (a) = true;
+ ALLOCNO_HARD_REGNO (a) = -1;
+ if (hard_reg_set_subset_p (reg_class_contents[cover_class],
+ conflict_hard_regs))
+ continue;
+ mode = ALLOCNO_MODE (a);
+#ifdef STACK_REGS
+ no_stack_reg_p = ALLOCNO_NO_STACK_REG_P (a);
+#endif
+ class_size = ira_class_hard_regs_num[cover_class];
+ for (j = 0; j < class_size; j++)
+ {
+ hard_regno = ira_class_hard_regs[cover_class][j];
+#ifdef STACK_REGS
+ if (no_stack_reg_p && FIRST_STACK_REG <= hard_regno
+ && hard_regno <= LAST_STACK_REG)
+ continue;
+#endif
+ if (!ira_hard_reg_not_in_set_p (hard_regno, mode, conflict_hard_regs)
+ || (TEST_HARD_REG_BIT
+ (prohibited_class_mode_regs[cover_class][mode], hard_regno)))
+ continue;
+ ALLOCNO_HARD_REGNO (a) = hard_regno;
+ for (r = ALLOCNO_LIVE_RANGES (a); r != NULL; r = r->next)
+ for (k = r->start; k <= r->finish; k++)
+ IOR_HARD_REG_SET (used_hard_regs[k],
+ ira_reg_mode_hard_regset[hard_regno][mode]);
+ break;
+ }
+ }
+ ira_free (sorted_allocnos);
+ ira_free (used_hard_regs);
+ ira_free (allocno_priorities);
+ if (internal_flag_ira_verbose > 1 && ira_dump_file != NULL)
+ ira_print_disposition (ira_dump_file);
+}
diff --git a/gcc/ira-conflicts.c b/gcc/ira-conflicts.c
new file mode 100644
index 0000000..04d3e42
--- /dev/null
+++ b/gcc/ira-conflicts.c
@@ -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);
+}
diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c
new file mode 100644
index 0000000..7749020
--- /dev/null
+++ b/gcc/ira-costs.c
@@ -0,0 +1,1594 @@
+/* IRA hard register and memory cost calculation for allocnos.
+ 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 "hard-reg-set.h"
+#include "rtl.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "flags.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "addresses.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "toplev.h"
+#include "target.h"
+#include "params.h"
+#include "ira-int.h"
+
+/* The file contains code is similar to one in regclass but the code
+ works on the allocno basis. */
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+/* Indexed by n, is TRUE if allocno with number N is used in an
+ auto-inc or auto-dec context. */
+static bool *in_inc_dec;
+#endif
+
+/* The `costs' struct records the cost of using hard registers of each
+ class considered for the calculation and of using memory for each
+ allocno. */
+struct costs
+{
+ int mem_cost;
+ /* Costs for register classes start here. We process only some
+ register classes (cover classes on the 1st cost calculation
+ iteration and important classes on the 2nd iteration). */
+ int cost[1];
+};
+
+/* Initialized once. It is a maximal possible size of the allocated
+ struct costs. */
+static int max_struct_costs_size;
+
+/* Allocated and initialized once, and used to initialize cost values
+ for each insn. */
+static struct costs *init_cost;
+
+/* Allocated once, and used for temporary purposes. */
+static struct costs *temp_costs;
+
+/* Allocated once, and used for the cost calculation. */
+static struct costs *op_costs[MAX_RECOG_OPERANDS];
+static struct costs *this_op_costs[MAX_RECOG_OPERANDS];
+
+/* Original and accumulated costs of each class for each allocno. */
+static struct costs *allocno_costs, *total_costs;
+
+/* Classes used for cost calculation. They may be different on
+ different iterations of the cost calculations or in different
+ optimization modes. */
+static enum reg_class *cost_classes;
+
+/* The size of the previous array. */
+static int cost_classes_num;
+
+/* Map: cost class -> order number (they start with 0) of the cost
+ class. */
+static int cost_class_nums[N_REG_CLASSES];
+
+/* It is the current size of struct costs. */
+static int struct_costs_size;
+
+/* Return pointer to structure containing costs of allocno with given
+ NUM in array ARR. */
+#define COSTS_OF_ALLOCNO(arr, num) \
+ ((struct costs *) ((char *) (arr) + (num) * struct_costs_size))
+
+/* Record register class preferences of each allocno. Null value
+ means no preferences. It happens on the 1st iteration of the cost
+ calculation. */
+static enum reg_class *allocno_pref;
+
+/* Allocated buffers for allocno_pref. */
+static enum reg_class *allocno_pref_buffer;
+
+/* Execution frequency of the current insn. */
+static int frequency;
+
+
+
+/* Compute the cost of loading X into (if TO_P is TRUE) or from (if
+ TO_P is FALSE) a register of class RCLASS in mode MODE. X must not
+ be a pseudo register. */
+static int
+copy_cost (rtx x, enum machine_mode mode, enum reg_class rclass, bool to_p,
+ secondary_reload_info *prev_sri)
+{
+ secondary_reload_info sri;
+ enum reg_class secondary_class = NO_REGS;
+
+ /* If X is a SCRATCH, there is actually nothing to move since we are
+ assuming optimal allocation. */
+ if (GET_CODE (x) == SCRATCH)
+ return 0;
+
+ /* Get the class we will actually use for a reload. */
+ rclass = PREFERRED_RELOAD_CLASS (x, rclass);
+
+ /* If we need a secondary reload for an intermediate, the cost is
+ that to load the input into the intermediate register, then to
+ copy it. */
+ sri.prev_sri = prev_sri;
+ sri.extra_cost = 0;
+ secondary_class = targetm.secondary_reload (to_p, x, rclass, mode, &sri);
+
+ if (ira_register_move_cost[mode] == NULL)
+ ira_init_register_move_cost (mode);
+
+ if (secondary_class != NO_REGS)
+ return (move_cost[mode][secondary_class][rclass] + sri.extra_cost
+ + copy_cost (x, mode, secondary_class, to_p, &sri));
+
+ /* For memory, use the memory move cost, for (hard) registers, use
+ the cost to move between the register classes, and use 2 for
+ everything else (constants). */
+ if (MEM_P (x) || rclass == NO_REGS)
+ return sri.extra_cost + ira_memory_move_cost[mode][rclass][to_p != 0];
+ else if (REG_P (x))
+ return
+ (sri.extra_cost + move_cost[mode][REGNO_REG_CLASS (REGNO (x))][rclass]);
+ else
+ /* If this is a constant, we may eventually want to call rtx_cost
+ here. */
+ return sri.extra_cost + COSTS_N_INSNS (1);
+}
+
+
+
+/* Record the cost of using memory or hard registers of various
+ classes for the operands in INSN.
+
+ N_ALTS is the number of alternatives.
+ N_OPS is the number of operands.
+ OPS is an array of the operands.
+ MODES are the modes of the operands, in case any are VOIDmode.
+ CONSTRAINTS are the constraints to use for the operands. This array
+ is modified by this procedure.
+
+ This procedure works alternative by alternative. For each
+ alternative we assume that we will be able to allocate all allocnos
+ to their ideal register class and calculate the cost of using that
+ alternative. Then we compute, for each operand that is a
+ pseudo-register, the cost of having the allocno allocated to each
+ register class and using it in that alternative. To this cost is
+ added the cost of the alternative.
+
+ The cost of each class for this insn is its lowest cost among all
+ the alternatives. */
+static void
+record_reg_classes (int n_alts, int n_ops, rtx *ops,
+ enum machine_mode *modes, const char **constraints,
+ rtx insn, struct costs **op_costs,
+ enum reg_class *allocno_pref)
+{
+ int alt;
+ int i, j, k;
+ rtx set;
+
+ /* Process each alternative, each time minimizing an operand's cost
+ with the cost for each operand in that alternative. */
+ for (alt = 0; alt < n_alts; alt++)
+ {
+ enum reg_class classes[MAX_RECOG_OPERANDS];
+ int allows_mem[MAX_RECOG_OPERANDS];
+ int rclass;
+ int alt_fail = 0;
+ int alt_cost = 0, op_cost_add;
+
+ for (i = 0; i < n_ops; i++)
+ {
+ unsigned char c;
+ const char *p = constraints[i];
+ rtx op = ops[i];
+ enum machine_mode mode = modes[i];
+ int allows_addr = 0;
+ int win = 0;
+
+ /* Initially show we know nothing about the register class. */
+ classes[i] = NO_REGS;
+ allows_mem[i] = 0;
+
+ /* If this operand has no constraints at all, we can
+ conclude nothing about it since anything is valid. */
+ if (*p == 0)
+ {
+ if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ memset (this_op_costs[i], 0, struct_costs_size);
+ continue;
+ }
+
+ /* If this alternative is only relevant when this operand
+ matches a previous operand, we do different things
+ depending on whether this operand is a allocno-reg or not.
+ We must process any modifiers for the operand before we
+ can make this test. */
+ while (*p == '%' || *p == '=' || *p == '+' || *p == '&')
+ p++;
+
+ if (p[0] >= '0' && p[0] <= '0' + i && (p[1] == ',' || p[1] == 0))
+ {
+ /* Copy class and whether memory is allowed from the
+ matching alternative. Then perform any needed cost
+ computations and/or adjustments. */
+ j = p[0] - '0';
+ classes[i] = classes[j];
+ allows_mem[i] = allows_mem[j];
+
+ if (! REG_P (op) || REGNO (op) < FIRST_PSEUDO_REGISTER)
+ {
+ /* If this matches the other operand, we have no
+ added cost and we win. */
+ if (rtx_equal_p (ops[j], op))
+ win = 1;
+ /* If we can put the other operand into a register,
+ add to the cost of this alternative the cost to
+ copy this operand to the register used for the
+ other operand. */
+ else if (classes[j] != NO_REGS)
+ {
+ alt_cost += copy_cost (op, mode, classes[j], 1, NULL);
+ win = 1;
+ }
+ }
+ else if (! REG_P (ops[j])
+ || REGNO (ops[j]) < FIRST_PSEUDO_REGISTER)
+ {
+ /* This op is an allocno but the one it matches is
+ not. */
+
+ /* If we can't put the other operand into a
+ register, this alternative can't be used. */
+
+ if (classes[j] == NO_REGS)
+ alt_fail = 1;
+ /* Otherwise, add to the cost of this alternative
+ the cost to copy the other operand to the hard
+ register used for this operand. */
+ else
+ alt_cost += copy_cost (ops[j], mode, classes[j], 1, NULL);
+ }
+ else
+ {
+ /* The costs of this operand are not the same as the
+ other operand since move costs are not symmetric.
+ Moreover, if we cannot tie them, this alternative
+ needs to do a copy, which is one insn. */
+ struct costs *pp = this_op_costs[i];
+
+ if (ira_register_move_cost[mode] == NULL)
+ ira_init_register_move_cost (mode);
+
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ pp->cost[k]
+ = ((recog_data.operand_type[i] != OP_OUT
+ ? ira_may_move_in_cost[mode][rclass]
+ [classes[i]] * frequency : 0)
+ + (recog_data.operand_type[i] != OP_IN
+ ? ira_may_move_out_cost[mode][classes[i]]
+ [rclass] * frequency : 0));
+ }
+
+ /* If the alternative actually allows memory, make
+ things a bit cheaper since we won't need an extra
+ insn to load it. */
+ pp->mem_cost
+ = ((recog_data.operand_type[i] != OP_IN
+ ? ira_memory_move_cost[mode][classes[i]][0] : 0)
+ + (recog_data.operand_type[i] != OP_OUT
+ ? ira_memory_move_cost[mode][classes[i]][1] : 0)
+ - allows_mem[i]) * frequency;
+ /* If we have assigned a class to this allocno in our
+ first pass, add a cost to this alternative
+ corresponding to what we would add if this allocno
+ were not in the appropriate class. We could use
+ cover class here but it is less accurate
+ approximation. */
+ if (allocno_pref)
+ {
+ enum reg_class pref_class
+ = allocno_pref[ALLOCNO_NUM
+ (ira_curr_regno_allocno_map
+ [REGNO (op)])];
+
+ if (pref_class == NO_REGS)
+ alt_cost
+ += ((recog_data.operand_type[i] != OP_IN
+ ? ira_memory_move_cost[mode][classes[i]][0]
+ : 0)
+ + (recog_data.operand_type[i] != OP_OUT
+ ? ira_memory_move_cost[mode][classes[i]][1]
+ : 0));
+ else if (ira_reg_class_intersect
+ [pref_class][classes[i]] == NO_REGS)
+ alt_cost += (ira_register_move_cost
+ [mode][pref_class][classes[i]]);
+ }
+ if (REGNO (ops[i]) != REGNO (ops[j])
+ && ! find_reg_note (insn, REG_DEAD, op))
+ alt_cost += 2;
+
+ /* This is in place of ordinary cost computation for
+ this operand, so skip to the end of the
+ alternative (should be just one character). */
+ while (*p && *p++ != ',')
+ ;
+
+ constraints[i] = p;
+ continue;
+ }
+ }
+
+ /* Scan all the constraint letters. See if the operand
+ matches any of the constraints. Collect the valid
+ register classes and see if this operand accepts
+ memory. */
+ while ((c = *p))
+ {
+ switch (c)
+ {
+ case ',':
+ break;
+ case '*':
+ /* Ignore the next letter for this pass. */
+ c = *++p;
+ break;
+
+ case '?':
+ alt_cost += 2;
+ case '!': case '#': case '&':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ break;
+
+ case 'p':
+ allows_addr = 1;
+ win = address_operand (op, GET_MODE (op));
+ /* We know this operand is an address, so we want it
+ to be allocated to a register that can be the
+ base of an address, i.e. BASE_REG_CLASS. */
+ classes[i]
+ = ira_reg_class_union[classes[i]]
+ [base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+ break;
+
+ case 'm': case 'o': case 'V':
+ /* It doesn't seem worth distinguishing between
+ offsettable and non-offsettable addresses
+ here. */
+ allows_mem[i] = 1;
+ if (MEM_P (op))
+ win = 1;
+ break;
+
+ case '<':
+ if (MEM_P (op)
+ && (GET_CODE (XEXP (op, 0)) == PRE_DEC
+ || GET_CODE (XEXP (op, 0)) == POST_DEC))
+ win = 1;
+ break;
+
+ case '>':
+ if (MEM_P (op)
+ && (GET_CODE (XEXP (op, 0)) == PRE_INC
+ || GET_CODE (XEXP (op, 0)) == POST_INC))
+ win = 1;
+ 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)))
+ win = 1;
+ break;
+
+ case 'G':
+ case 'H':
+ if (GET_CODE (op) == CONST_DOUBLE
+ && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, c, p))
+ win = 1;
+ break;
+
+ case 's':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ break;
+
+ case 'i':
+ if (CONSTANT_P (op)
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)))
+ win = 1;
+ break;
+
+ case 'n':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ win = 1;
+ 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, p))
+ win = 1;
+ break;
+
+ case 'X':
+ win = 1;
+ break;
+
+ case 'g':
+ if (MEM_P (op)
+ || (CONSTANT_P (op)
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))))
+ win = 1;
+ allows_mem[i] = 1;
+ case 'r':
+ classes[i] = ira_reg_class_union[classes[i]][GENERAL_REGS];
+ break;
+
+ default:
+ if (REG_CLASS_FROM_CONSTRAINT (c, p) != NO_REGS)
+ classes[i] = ira_reg_class_union[classes[i]]
+ [REG_CLASS_FROM_CONSTRAINT (c, p)];
+#ifdef EXTRA_CONSTRAINT_STR
+ else if (EXTRA_CONSTRAINT_STR (op, c, p))
+ win = 1;
+
+ if (EXTRA_MEMORY_CONSTRAINT (c, p))
+ {
+ /* Every MEM can be reloaded to fit. */
+ allows_mem[i] = 1;
+ if (MEM_P (op))
+ win = 1;
+ }
+ if (EXTRA_ADDRESS_CONSTRAINT (c, p))
+ {
+ /* Every address can be reloaded to fit. */
+ allows_addr = 1;
+ if (address_operand (op, GET_MODE (op)))
+ win = 1;
+ /* We know this operand is an address, so we
+ want it to be allocated to a hard register
+ that can be the base of an address,
+ i.e. BASE_REG_CLASS. */
+ classes[i]
+ = ira_reg_class_union[classes[i]]
+ [base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+ }
+#endif
+ break;
+ }
+ p += CONSTRAINT_LEN (c, p);
+ if (c == ',')
+ break;
+ }
+
+ constraints[i] = p;
+
+ /* How we account for this operand now depends on whether it
+ is a pseudo register or not. If it is, we first check if
+ any register classes are valid. If not, we ignore this
+ alternative, since we want to assume that all allocnos get
+ allocated for register preferencing. If some register
+ class is valid, compute the costs of moving the allocno
+ into that class. */
+ if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ {
+ if (classes[i] == NO_REGS)
+ {
+ /* We must always fail if the operand is a REG, but
+ we did not find a suitable class.
+
+ Otherwise we may perform an uninitialized read
+ from this_op_costs after the `continue' statement
+ below. */
+ alt_fail = 1;
+ }
+ else
+ {
+ struct costs *pp = this_op_costs[i];
+
+ if (ira_register_move_cost[mode] == NULL)
+ ira_init_register_move_cost (mode);
+
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ pp->cost[k]
+ = ((recog_data.operand_type[i] != OP_OUT
+ ? ira_may_move_in_cost[mode][rclass]
+ [classes[i]] * frequency : 0)
+ + (recog_data.operand_type[i] != OP_IN
+ ? ira_may_move_out_cost[mode][classes[i]]
+ [rclass] * frequency : 0));
+ }
+
+ /* If the alternative actually allows memory, make
+ things a bit cheaper since we won't need an extra
+ insn to load it. */
+ pp->mem_cost
+ = ((recog_data.operand_type[i] != OP_IN
+ ? ira_memory_move_cost[mode][classes[i]][0] : 0)
+ + (recog_data.operand_type[i] != OP_OUT
+ ? ira_memory_move_cost[mode][classes[i]][1] : 0)
+ - allows_mem[i]) * frequency;
+ /* If we have assigned a class to this allocno in our
+ first pass, add a cost to this alternative
+ corresponding to what we would add if this allocno
+ were not in the appropriate class. We could use
+ cover class here but it is less accurate
+ approximation. */
+ if (allocno_pref)
+ {
+ enum reg_class pref_class
+ = allocno_pref[ALLOCNO_NUM
+ (ira_curr_regno_allocno_map
+ [REGNO (op)])];
+
+ if (pref_class == NO_REGS)
+ alt_cost
+ += ((recog_data.operand_type[i] != OP_IN
+ ? ira_memory_move_cost[mode][classes[i]][0]
+ : 0)
+ + (recog_data.operand_type[i] != OP_OUT
+ ? ira_memory_move_cost[mode][classes[i]][1]
+ : 0));
+ else if (ira_reg_class_intersect[pref_class][classes[i]]
+ == NO_REGS)
+ alt_cost += (ira_register_move_cost
+ [mode][pref_class][classes[i]]);
+ }
+ }
+ }
+
+ /* Otherwise, if this alternative wins, either because we
+ have already determined that or if we have a hard
+ register of the proper class, there is no cost for this
+ alternative. */
+ else if (win || (REG_P (op)
+ && reg_fits_class_p (op, classes[i],
+ 0, GET_MODE (op))))
+ ;
+
+ /* If registers are valid, the cost of this alternative
+ includes copying the object to and/or from a
+ register. */
+ else if (classes[i] != NO_REGS)
+ {
+ if (recog_data.operand_type[i] != OP_OUT)
+ alt_cost += copy_cost (op, mode, classes[i], 1, NULL);
+
+ if (recog_data.operand_type[i] != OP_IN)
+ alt_cost += copy_cost (op, mode, classes[i], 0, NULL);
+ }
+ /* The only other way this alternative can be used is if
+ this is a constant that could be placed into memory. */
+ else if (CONSTANT_P (op) && (allows_addr || allows_mem[i]))
+ alt_cost += ira_memory_move_cost[mode][classes[i]][1];
+ else
+ alt_fail = 1;
+ }
+
+ if (alt_fail)
+ continue;
+
+ op_cost_add = alt_cost * frequency;
+ /* Finally, update the costs with the information we've
+ calculated about this alternative. */
+ for (i = 0; i < n_ops; i++)
+ if (REG_P (ops[i]) && REGNO (ops[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ struct costs *pp = op_costs[i], *qq = this_op_costs[i];
+ int scale = 1 + (recog_data.operand_type[i] == OP_INOUT);
+
+ pp->mem_cost = MIN (pp->mem_cost,
+ (qq->mem_cost + op_cost_add) * scale);
+
+ for (k = 0; k < cost_classes_num; k++)
+ pp->cost[k]
+ = MIN (pp->cost[k], (qq->cost[k] + op_cost_add) * scale);
+ }
+ }
+
+ /* If this insn is a single set copying operand 1 to operand 0 and
+ one operand is an allocno with the other a hard reg or an allocno
+ that prefers a hard register that is in its own register class
+ then we may want to adjust the cost of that register class to -1.
+
+ Avoid the adjustment if the source does not die to avoid
+ stressing of register allocator by preferrencing two colliding
+ registers into single class.
+
+ Also avoid the adjustment if a copy between hard registers of the
+ class is expensive (ten times the cost of a default copy is
+ considered arbitrarily expensive). This avoids losing when the
+ preferred class is very expensive as the source of a copy
+ instruction. */
+ if ((set = single_set (insn)) != 0
+ && ops[0] == SET_DEST (set) && ops[1] == SET_SRC (set)
+ && REG_P (ops[0]) && REG_P (ops[1])
+ && find_regno_note (insn, REG_DEAD, REGNO (ops[1])))
+ for (i = 0; i <= 1; i++)
+ if (REGNO (ops[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ unsigned int regno = REGNO (ops[!i]);
+ enum machine_mode mode = GET_MODE (ops[!i]);
+ int rclass;
+ unsigned int nr;
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ if (TEST_HARD_REG_BIT (reg_class_contents[rclass], regno)
+ && (reg_class_size[rclass]
+ == (unsigned) CLASS_MAX_NREGS (rclass, mode)))
+ {
+ if (reg_class_size[rclass] == 1)
+ op_costs[i]->cost[k] = -frequency;
+ else
+ {
+ for (nr = 0;
+ nr < (unsigned) hard_regno_nregs[regno][mode];
+ nr++)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[rclass],
+ regno + nr))
+ break;
+
+ if (nr == (unsigned) hard_regno_nregs[regno][mode])
+ op_costs[i]->cost[k] = -frequency;
+ }
+ }
+ }
+ }
+}
+
+
+
+/* Wrapper around REGNO_OK_FOR_INDEX_P, to allow pseudo registers. */
+static inline bool
+ok_for_index_p_nonstrict (rtx reg)
+{
+ unsigned regno = REGNO (reg);
+
+ return regno >= FIRST_PSEUDO_REGISTER || REGNO_OK_FOR_INDEX_P (regno);
+}
+
+/* A version of regno_ok_for_base_p for use here, when all
+ pseudo-registers should count as OK. Arguments as for
+ regno_ok_for_base_p. */
+static inline bool
+ok_for_base_p_nonstrict (rtx reg, enum machine_mode mode,
+ enum rtx_code outer_code, enum rtx_code index_code)
+{
+ unsigned regno = REGNO (reg);
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return true;
+ return ok_for_base_p_1 (regno, mode, outer_code, index_code);
+}
+
+/* Record the pseudo registers we must reload into hard registers in a
+ subexpression of a memory address, X.
+
+ If CONTEXT is 0, we are looking at the base part of an address,
+ otherwise we are looking at the index part.
+
+ MODE is the mode of the memory reference; OUTER_CODE and INDEX_CODE
+ give the context that the rtx appears in. These three arguments
+ are passed down to base_reg_class.
+
+ SCALE is twice the amount to multiply the cost by (it is twice so
+ we can represent half-cost adjustments). */
+static void
+record_address_regs (enum machine_mode mode, rtx x, int context,
+ enum rtx_code outer_code, enum rtx_code index_code,
+ int scale)
+{
+ enum rtx_code code = GET_CODE (x);
+ enum reg_class rclass;
+
+ if (context == 1)
+ rclass = INDEX_REG_CLASS;
+ else
+ rclass = base_reg_class (mode, outer_code, index_code);
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST:
+ case CC0:
+ case PC:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return;
+
+ case PLUS:
+ /* When we have an address that is a sum, we must determine
+ whether registers are "base" or "index" regs. If there is a
+ sum of two registers, we must choose one to be the "base".
+ Luckily, we can use the REG_POINTER to make a good choice
+ most of the time. We only need to do this on machines that
+ can have two registers in an address and where the base and
+ index register classes are different.
+
+ ??? This code used to set REGNO_POINTER_FLAG in some cases,
+ but that seems bogus since it should only be set when we are
+ sure the register is being used as a pointer. */
+ {
+ rtx arg0 = XEXP (x, 0);
+ rtx arg1 = XEXP (x, 1);
+ enum rtx_code code0 = GET_CODE (arg0);
+ enum rtx_code code1 = GET_CODE (arg1);
+
+ /* Look inside subregs. */
+ if (code0 == SUBREG)
+ arg0 = SUBREG_REG (arg0), code0 = GET_CODE (arg0);
+ if (code1 == SUBREG)
+ arg1 = SUBREG_REG (arg1), code1 = GET_CODE (arg1);
+
+ /* If this machine only allows one register per address, it
+ must be in the first operand. */
+ if (MAX_REGS_PER_ADDRESS == 1)
+ record_address_regs (mode, arg0, 0, PLUS, code1, scale);
+
+ /* If index and base registers are the same on this machine,
+ just record registers in any non-constant operands. We
+ assume here, as well as in the tests below, that all
+ addresses are in canonical form. */
+ else if (INDEX_REG_CLASS == base_reg_class (VOIDmode, PLUS, SCRATCH))
+ {
+ record_address_regs (mode, arg0, context, PLUS, code1, scale);
+ if (! CONSTANT_P (arg1))
+ record_address_regs (mode, arg1, context, PLUS, code0, scale);
+ }
+
+ /* If the second operand is a constant integer, it doesn't
+ change what class the first operand must be. */
+ else if (code1 == CONST_INT || code1 == CONST_DOUBLE)
+ record_address_regs (mode, arg0, context, PLUS, code1, scale);
+ /* If the second operand is a symbolic constant, the first
+ operand must be an index register. */
+ else if (code1 == SYMBOL_REF || code1 == CONST || code1 == LABEL_REF)
+ record_address_regs (mode, arg0, 1, PLUS, code1, scale);
+ /* If both operands are registers but one is already a hard
+ register of index or reg-base class, give the other the
+ class that the hard register is not. */
+ else if (code0 == REG && code1 == REG
+ && REGNO (arg0) < FIRST_PSEUDO_REGISTER
+ && (ok_for_base_p_nonstrict (arg0, mode, PLUS, REG)
+ || ok_for_index_p_nonstrict (arg0)))
+ record_address_regs (mode, arg1,
+ ok_for_base_p_nonstrict (arg0, mode, PLUS, REG)
+ ? 1 : 0,
+ PLUS, REG, scale);
+ else if (code0 == REG && code1 == REG
+ && REGNO (arg1) < FIRST_PSEUDO_REGISTER
+ && (ok_for_base_p_nonstrict (arg1, mode, PLUS, REG)
+ || ok_for_index_p_nonstrict (arg1)))
+ record_address_regs (mode, arg0,
+ ok_for_base_p_nonstrict (arg1, mode, PLUS, REG)
+ ? 1 : 0,
+ PLUS, REG, scale);
+ /* If one operand is known to be a pointer, it must be the
+ base with the other operand the index. Likewise if the
+ other operand is a MULT. */
+ else if ((code0 == REG && REG_POINTER (arg0)) || code1 == MULT)
+ {
+ record_address_regs (mode, arg0, 0, PLUS, code1, scale);
+ record_address_regs (mode, arg1, 1, PLUS, code0, scale);
+ }
+ else if ((code1 == REG && REG_POINTER (arg1)) || code0 == MULT)
+ {
+ record_address_regs (mode, arg0, 1, PLUS, code1, scale);
+ record_address_regs (mode, arg1, 0, PLUS, code0, scale);
+ }
+ /* Otherwise, count equal chances that each might be a base or
+ index register. This case should be rare. */
+ else
+ {
+ record_address_regs (mode, arg0, 0, PLUS, code1, scale / 2);
+ record_address_regs (mode, arg0, 1, PLUS, code1, scale / 2);
+ record_address_regs (mode, arg1, 0, PLUS, code0, scale / 2);
+ record_address_regs (mode, arg1, 1, PLUS, code0, scale / 2);
+ }
+ }
+ break;
+
+ /* Double the importance of an allocno that is incremented or
+ decremented, since it would take two extra insns if it ends
+ up in the wrong place. */
+ case POST_MODIFY:
+ case PRE_MODIFY:
+ record_address_regs (mode, XEXP (x, 0), 0, code,
+ GET_CODE (XEXP (XEXP (x, 1), 1)), 2 * scale);
+ if (REG_P (XEXP (XEXP (x, 1), 1)))
+ record_address_regs (mode, XEXP (XEXP (x, 1), 1), 1, code, REG,
+ 2 * scale);
+ break;
+
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ /* Double the importance of an allocno that is incremented or
+ decremented, since it would take two extra insns if it ends
+ up in the wrong place. If the operand is a pseudo-register,
+ show it is being used in an INC_DEC context. */
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ if (REG_P (XEXP (x, 0))
+ && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER)
+ in_inc_dec[ALLOCNO_NUM (ira_curr_regno_allocno_map
+ [REGNO (XEXP (x, 0))])] = true;
+#endif
+ record_address_regs (mode, XEXP (x, 0), 0, code, SCRATCH, 2 * scale);
+ break;
+
+ case REG:
+ {
+ struct costs *pp;
+ int i, k;
+
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+ break;
+
+ pp = COSTS_OF_ALLOCNO (allocno_costs,
+ ALLOCNO_NUM (ira_curr_regno_allocno_map
+ [REGNO (x)]));
+ pp->mem_cost += (ira_memory_move_cost[Pmode][rclass][1] * scale) / 2;
+ if (ira_register_move_cost[Pmode] == NULL)
+ ira_init_register_move_cost (Pmode);
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ i = cost_classes[k];
+ pp->cost[k]
+ += (ira_may_move_in_cost[Pmode][i][rclass] * scale) / 2;
+ }
+ }
+ break;
+
+ default:
+ {
+ const char *fmt = GET_RTX_FORMAT (code);
+ int i;
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ record_address_regs (mode, XEXP (x, i), context, code, SCRATCH,
+ scale);
+ }
+ }
+}
+
+
+
+/* Calculate the costs of insn operands. */
+static void
+record_operand_costs (rtx insn, struct costs **op_costs,
+ enum reg_class *allocno_pref)
+{
+ const char *constraints[MAX_RECOG_OPERANDS];
+ enum machine_mode modes[MAX_RECOG_OPERANDS];
+ int i;
+
+ for (i = 0; i < recog_data.n_operands; i++)
+ {
+ constraints[i] = recog_data.constraints[i];
+ modes[i] = recog_data.operand_mode[i];
+ }
+
+ /* If we get here, we are set up to record the costs of all the
+ operands for this insn. Start by initializing the costs. Then
+ handle any address registers. Finally record the desired classes
+ for any allocnos, doing it twice if some pair of operands are
+ commutative. */
+ for (i = 0; i < recog_data.n_operands; i++)
+ {
+ memcpy (op_costs[i], init_cost, struct_costs_size);
+
+ if (GET_CODE (recog_data.operand[i]) == SUBREG)
+ recog_data.operand[i] = SUBREG_REG (recog_data.operand[i]);
+
+ if (MEM_P (recog_data.operand[i]))
+ record_address_regs (GET_MODE (recog_data.operand[i]),
+ XEXP (recog_data.operand[i], 0),
+ 0, MEM, SCRATCH, frequency * 2);
+ else if (constraints[i][0] == 'p'
+ || EXTRA_ADDRESS_CONSTRAINT (constraints[i][0],
+ constraints[i]))
+ record_address_regs (VOIDmode, recog_data.operand[i], 0, ADDRESS,
+ SCRATCH, frequency * 2);
+ }
+
+ /* Check for commutative in a separate loop so everything will have
+ been initialized. We must do this even if one operand is a
+ constant--see addsi3 in m68k.md. */
+ for (i = 0; i < (int) recog_data.n_operands - 1; i++)
+ if (constraints[i][0] == '%')
+ {
+ const char *xconstraints[MAX_RECOG_OPERANDS];
+ int j;
+
+ /* Handle commutative operands by swapping the constraints.
+ We assume the modes are the same. */
+ for (j = 0; j < recog_data.n_operands; j++)
+ xconstraints[j] = constraints[j];
+
+ xconstraints[i] = constraints[i+1];
+ xconstraints[i+1] = constraints[i];
+ record_reg_classes (recog_data.n_alternatives, recog_data.n_operands,
+ recog_data.operand, modes,
+ xconstraints, insn, op_costs, allocno_pref);
+ }
+ record_reg_classes (recog_data.n_alternatives, recog_data.n_operands,
+ recog_data.operand, modes,
+ constraints, insn, op_costs, allocno_pref);
+}
+
+
+
+/* Process one insn INSN. Scan it and record each time it would save
+ code to put a certain allocnos in a certain class. Return the last
+ insn processed, so that the scan can be continued from there. */
+static rtx
+scan_one_insn (rtx insn)
+{
+ enum rtx_code pat_code;
+ rtx set, note;
+ int i, k;
+
+ if (!INSN_P (insn))
+ return insn;
+
+ pat_code = GET_CODE (PATTERN (insn));
+ if (pat_code == USE || pat_code == CLOBBER || pat_code == ASM_INPUT
+ || pat_code == ADDR_VEC || pat_code == ADDR_DIFF_VEC)
+ return insn;
+
+ set = single_set (insn);
+ extract_insn (insn);
+
+ /* If this insn loads a parameter from its stack slot, then it
+ represents a savings, rather than a cost, if the parameter is
+ stored in memory. Record this fact. */
+ if (set != 0 && REG_P (SET_DEST (set)) && MEM_P (SET_SRC (set))
+ && (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != NULL_RTX
+ && MEM_P (XEXP (note, 0)))
+ {
+ COSTS_OF_ALLOCNO (allocno_costs,
+ ALLOCNO_NUM (ira_curr_regno_allocno_map
+ [REGNO (SET_DEST (set))]))->mem_cost
+ -= (ira_memory_move_cost[GET_MODE (SET_DEST (set))][GENERAL_REGS][1]
+ * frequency);
+ record_address_regs (GET_MODE (SET_SRC (set)), XEXP (SET_SRC (set), 0),
+ 0, MEM, SCRATCH, frequency * 2);
+ }
+
+ record_operand_costs (insn, op_costs, allocno_pref);
+
+ /* Now add the cost for each operand to the total costs for its
+ allocno. */
+ for (i = 0; i < recog_data.n_operands; i++)
+ if (REG_P (recog_data.operand[i])
+ && REGNO (recog_data.operand[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (recog_data.operand[i]);
+ struct costs *p
+ = COSTS_OF_ALLOCNO (allocno_costs,
+ ALLOCNO_NUM (ira_curr_regno_allocno_map[regno]));
+ struct costs *q = op_costs[i];
+
+ p->mem_cost += q->mem_cost;
+ for (k = 0; k < cost_classes_num; k++)
+ p->cost[k] += q->cost[k];
+ }
+
+ return insn;
+}
+
+
+
+/* Print allocnos costs to file F. */
+static void
+print_costs (FILE *f)
+{
+ int k;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ fprintf (f, "\n");
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ int i, rclass;
+ basic_block bb;
+ int regno = ALLOCNO_REGNO (a);
+
+ i = ALLOCNO_NUM (a);
+ fprintf (f, " a%d(r%d,", i, regno);
+ if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
+ fprintf (f, "b%d", bb->index);
+ else
+ fprintf (f, "l%d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num);
+ fprintf (f, ") costs:");
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ if (contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (regno)]
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ && (! in_inc_dec[i] || ! forbidden_inc_dec_class[rclass])
+#endif
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ && ! invalid_mode_change_p (regno, (enum reg_class) rclass,
+ PSEUDO_REGNO_MODE (regno))
+#endif
+ )
+ {
+ fprintf (f, " %s:%d", reg_class_names[rclass],
+ COSTS_OF_ALLOCNO (allocno_costs, i)->cost[k]);
+ if (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
+ || flag_ira_algorithm == IRA_ALGORITHM_MIXED)
+ fprintf (f, ",%d", COSTS_OF_ALLOCNO (total_costs, i)->cost[k]);
+ }
+ }
+ fprintf (f, " MEM:%i\n", COSTS_OF_ALLOCNO (allocno_costs, i)->mem_cost);
+ }
+}
+
+/* Traverse the BB represented by LOOP_TREE_NODE to update the allocno
+ costs. */
+static void
+process_bb_node_for_costs (ira_loop_tree_node_t loop_tree_node)
+{
+ basic_block bb;
+ rtx insn;
+
+ bb = loop_tree_node->bb;
+ if (bb == NULL)
+ return;
+ frequency = REG_FREQ_FROM_BB (bb);
+ if (frequency == 0)
+ frequency = 1;
+ FOR_BB_INSNS (bb, insn)
+ insn = scan_one_insn (insn);
+}
+
+/* Find costs of register classes and memory for allocnos and their
+ best costs. */
+static void
+find_allocno_class_costs (void)
+{
+ int i, k;
+ int pass;
+ basic_block bb;
+
+ init_recog ();
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ in_inc_dec = ira_allocate (sizeof (bool) * ira_allocnos_num);
+#endif /* FORBIDDEN_INC_DEC_CLASSES */
+ allocno_pref = NULL;
+ /* Normally we scan the insns once and determine the best class to
+ use for each allocno. However, if -fexpensive-optimizations are
+ on, we do so twice, the second time using the tentative best
+ classes to guide the selection. */
+ for (pass = 0; pass <= flag_expensive_optimizations; pass++)
+ {
+ if (internal_flag_ira_verbose > 0 && ira_dump_file)
+ fprintf (ira_dump_file, "\nPass %i for finding allocno costs\n\n",
+ pass);
+ /* We could use only cover classes. Unfortunately it does not
+ work well for some targets where some subclass of cover class
+ is costly and wrong cover class is chosen. */
+ for (cost_classes_num = 0;
+ cost_classes_num < ira_important_classes_num;
+ cost_classes_num++)
+ {
+ cost_classes[cost_classes_num]
+ = ira_important_classes[cost_classes_num];
+ cost_class_nums[cost_classes[cost_classes_num]]
+ = cost_classes_num;
+ }
+ struct_costs_size
+ = sizeof (struct costs) + sizeof (int) * (cost_classes_num - 1);
+ /* Zero out our accumulation of the cost of each class for each
+ allocno. */
+ memset (allocno_costs, 0, ira_allocnos_num * struct_costs_size);
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ memset (in_inc_dec, 0, ira_allocnos_num * sizeof (bool));
+#endif
+
+ /* Scan the instructions and record each time it would save code
+ to put a certain allocno in a certain class. */
+ ira_traverse_loop_tree (true, ira_loop_tree_root,
+ process_bb_node_for_costs, NULL);
+
+ memcpy (total_costs, allocno_costs,
+ max_struct_costs_size * ira_allocnos_num);
+ if (pass == 0)
+ allocno_pref = allocno_pref_buffer;
+
+ /* Now for each allocno look at how desirable each class is and
+ find which class is preferred. */
+ for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
+ {
+ ira_allocno_t a, parent_a;
+ int rclass, a_num, parent_a_num;
+ ira_loop_tree_node_t parent;
+ int best_cost;
+ enum reg_class best, alt_class, common_class;
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ int inc_dec_p = false;
+#endif
+
+ if (ira_regno_allocno_map[i] == NULL)
+ continue;
+ memset (temp_costs, 0, struct_costs_size);
+ /* Find cost of all allocnos with the same regno. */
+ for (a = ira_regno_allocno_map[i];
+ a != NULL;
+ a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
+ {
+ a_num = ALLOCNO_NUM (a);
+ if ((flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
+ || flag_ira_algorithm == IRA_ALGORITHM_MIXED)
+ && (parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) != NULL
+ && (parent_a = parent->regno_allocno_map[i]) != NULL
+ /* There are no caps yet. */
+ && bitmap_bit_p (ALLOCNO_LOOP_TREE_NODE (a)->border_allocnos,
+ ALLOCNO_NUM (a)))
+ {
+ /* Propagate costs to upper levels in the region
+ tree. */
+ parent_a_num = ALLOCNO_NUM (parent_a);
+ for (k = 0; k < cost_classes_num; k++)
+ COSTS_OF_ALLOCNO (total_costs, parent_a_num)->cost[k]
+ += COSTS_OF_ALLOCNO (total_costs, a_num)->cost[k];
+ COSTS_OF_ALLOCNO (total_costs, parent_a_num)->mem_cost
+ += COSTS_OF_ALLOCNO (total_costs, a_num)->mem_cost;
+ }
+ for (k = 0; k < cost_classes_num; k++)
+ temp_costs->cost[k]
+ += COSTS_OF_ALLOCNO (allocno_costs, a_num)->cost[k];
+ temp_costs->mem_cost
+ += COSTS_OF_ALLOCNO (allocno_costs, a_num)->mem_cost;
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ if (in_inc_dec[a_num])
+ inc_dec_p = true;
+#endif
+ }
+ best_cost = (1 << (HOST_BITS_PER_INT - 2)) - 1;
+ best = ALL_REGS;
+ alt_class = NO_REGS;
+ /* Find best common class for all allocnos with the same
+ regno. */
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ /* Ignore classes that are too small for this operand or
+ invalid for an operand that was auto-incremented. */
+ if (! contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (i)]
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ || (inc_dec_p && forbidden_inc_dec_class[rclass])
+#endif
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ || invalid_mode_change_p (i, (enum reg_class) rclass,
+ PSEUDO_REGNO_MODE (i))
+#endif
+ )
+ continue;
+ if (temp_costs->cost[k] < best_cost)
+ {
+ best_cost = temp_costs->cost[k];
+ best = (enum reg_class) rclass;
+ }
+ else if (temp_costs->cost[k] == best_cost)
+ best = ira_reg_class_union[best][rclass];
+ if (pass == flag_expensive_optimizations
+ && temp_costs->cost[k] < temp_costs->mem_cost
+ && (reg_class_size[reg_class_subunion[alt_class][rclass]]
+ > reg_class_size[alt_class]))
+ alt_class = reg_class_subunion[alt_class][rclass];
+ }
+ if (pass == flag_expensive_optimizations)
+ {
+ if (best_cost > temp_costs->mem_cost)
+ best = alt_class = NO_REGS;
+ else if (best == alt_class)
+ alt_class = NO_REGS;
+ setup_reg_classes (i, best, alt_class);
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " r%d: preferred %s, alternative %s\n",
+ i, reg_class_names[best], reg_class_names[alt_class]);
+ }
+ if (best_cost > temp_costs->mem_cost)
+ common_class = NO_REGS;
+ else
+ /* Make the common class a cover class. Remember all
+ allocnos with the same regno should have the same cover
+ class. */
+ common_class = ira_class_translate[best];
+ for (a = ira_regno_allocno_map[i];
+ a != NULL;
+ a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
+ {
+ a_num = ALLOCNO_NUM (a);
+ if (common_class == NO_REGS)
+ best = NO_REGS;
+ else
+ {
+ /* Finding best class which is subset of the common
+ class. */
+ best_cost = (1 << (HOST_BITS_PER_INT - 2)) - 1;
+ best = ALL_REGS;
+ for (k = 0; k < cost_classes_num; k++)
+ {
+ rclass = cost_classes[k];
+ if (! ira_class_subset_p[rclass][common_class])
+ continue;
+ /* Ignore classes that are too small for this
+ operand or invalid for an operand that was
+ auto-incremented. */
+ if (! contains_reg_of_mode[rclass][PSEUDO_REGNO_MODE (i)]
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ || (inc_dec_p && forbidden_inc_dec_class[rclass])
+#endif
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ || invalid_mode_change_p (i, (enum reg_class) rclass,
+ PSEUDO_REGNO_MODE (i))
+#endif
+ )
+ ;
+ else if (COSTS_OF_ALLOCNO (total_costs, a_num)->cost[k]
+ < best_cost)
+ {
+ best_cost
+ = COSTS_OF_ALLOCNO (total_costs, a_num)->cost[k];
+ best = (enum reg_class) rclass;
+ }
+ else if (COSTS_OF_ALLOCNO (total_costs, a_num)->cost[k]
+ == best_cost)
+ best = ira_reg_class_union[best][rclass];
+ }
+ }
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL
+ && (pass == 0 || allocno_pref[a_num] != best))
+ {
+ fprintf (ira_dump_file, " a%d (r%d,", a_num, i);
+ if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
+ fprintf (ira_dump_file, "b%d", bb->index);
+ else
+ fprintf (ira_dump_file, "l%d",
+ ALLOCNO_LOOP_TREE_NODE (a)->loop->num);
+ fprintf (ira_dump_file, ") best %s, cover %s\n",
+ reg_class_names[best],
+ reg_class_names[ira_class_translate[best]]);
+ }
+ allocno_pref[a_num] = best;
+ }
+ }
+
+ if (internal_flag_ira_verbose > 4 && ira_dump_file)
+ {
+ print_costs (ira_dump_file);
+ fprintf (ira_dump_file,"\n");
+ }
+ }
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ ira_free (in_inc_dec);
+#endif
+}
+
+
+
+/* Process moves involving hard regs to modify allocno hard register
+ costs. We can do this only after determining allocno cover class.
+ If a hard register forms a register class, than moves with the hard
+ register are already taken into account in class costs for the
+ allocno. */
+static void
+process_bb_node_for_hard_reg_moves (ira_loop_tree_node_t loop_tree_node)
+{
+ int i, freq, cost, src_regno, dst_regno, hard_regno;
+ bool to_p;
+ ira_allocno_t a;
+ enum reg_class rclass, hard_reg_class;
+ enum machine_mode mode;
+ basic_block bb;
+ rtx insn, set, src, dst;
+
+ bb = loop_tree_node->bb;
+ if (bb == NULL)
+ return;
+ freq = REG_FREQ_FROM_BB (bb);
+ if (freq == 0)
+ freq = 1;
+ FOR_BB_INSNS (bb, insn)
+ {
+ if (! INSN_P (insn))
+ continue;
+ set = single_set (insn);
+ if (set == NULL_RTX)
+ continue;
+ dst = SET_DEST (set);
+ src = SET_SRC (set);
+ if (! REG_P (dst) || ! REG_P (src))
+ continue;
+ dst_regno = REGNO (dst);
+ src_regno = REGNO (src);
+ if (dst_regno >= FIRST_PSEUDO_REGISTER
+ && src_regno < FIRST_PSEUDO_REGISTER)
+ {
+ hard_regno = src_regno;
+ to_p = true;
+ a = ira_curr_regno_allocno_map[dst_regno];
+ }
+ else if (src_regno >= FIRST_PSEUDO_REGISTER
+ && dst_regno < FIRST_PSEUDO_REGISTER)
+ {
+ hard_regno = dst_regno;
+ to_p = false;
+ a = ira_curr_regno_allocno_map[src_regno];
+ }
+ else
+ continue;
+ rclass = ALLOCNO_COVER_CLASS (a);
+ if (! TEST_HARD_REG_BIT (reg_class_contents[rclass], hard_regno))
+ continue;
+ i = ira_class_hard_reg_index[rclass][hard_regno];
+ if (i < 0)
+ continue;
+ mode = ALLOCNO_MODE (a);
+ hard_reg_class = REGNO_REG_CLASS (hard_regno);
+ cost = (to_p ? ira_register_move_cost[mode][hard_reg_class][rclass]
+ : ira_register_move_cost[mode][rclass][hard_reg_class]) * freq;
+ ira_allocate_and_set_costs (&ALLOCNO_HARD_REG_COSTS (a), rclass,
+ ALLOCNO_COVER_CLASS_COST (a));
+ ira_allocate_and_set_costs (&ALLOCNO_CONFLICT_HARD_REG_COSTS (a),
+ rclass, 0);
+ ALLOCNO_HARD_REG_COSTS (a)[i] -= cost;
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (a)[i] -= cost;
+ ALLOCNO_COVER_CLASS_COST (a) = MIN (ALLOCNO_COVER_CLASS_COST (a),
+ ALLOCNO_HARD_REG_COSTS (a)[i]);
+ }
+}
+
+/* After we find hard register and memory costs for allocnos, define
+ its cover class and modify hard register cost because insns moving
+ allocno to/from hard registers. */
+static void
+setup_allocno_cover_class_and_costs (void)
+{
+ int i, j, n, regno;
+ int *reg_costs;
+ enum reg_class cover_class, rclass;
+ enum machine_mode mode;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ i = ALLOCNO_NUM (a);
+ mode = ALLOCNO_MODE (a);
+ cover_class = ira_class_translate[allocno_pref[i]];
+ ira_assert (allocno_pref[i] == NO_REGS || cover_class != NO_REGS);
+ ALLOCNO_MEMORY_COST (a) = ALLOCNO_UPDATED_MEMORY_COST (a)
+ = COSTS_OF_ALLOCNO (allocno_costs, i)->mem_cost;
+ ira_set_allocno_cover_class (a, cover_class);
+ if (cover_class == NO_REGS)
+ continue;
+ ALLOCNO_AVAILABLE_REGS_NUM (a) = ira_available_class_regs[cover_class];
+ ALLOCNO_COVER_CLASS_COST (a)
+ = (COSTS_OF_ALLOCNO (allocno_costs, i)
+ ->cost[cost_class_nums[allocno_pref[i]]]);
+ if (optimize && ALLOCNO_COVER_CLASS (a) != allocno_pref[i])
+ {
+ n = ira_class_hard_regs_num[cover_class];
+ ALLOCNO_HARD_REG_COSTS (a)
+ = reg_costs = ira_allocate_cost_vector (cover_class);
+ for (j = n - 1; j >= 0; j--)
+ {
+ regno = ira_class_hard_regs[cover_class][j];
+ rclass = REGNO_REG_CLASS (regno);
+ reg_costs[j] = (COSTS_OF_ALLOCNO (allocno_costs, i)
+ ->cost[cost_class_nums[rclass]]);
+ }
+ }
+ }
+ if (optimize)
+ ira_traverse_loop_tree (true, ira_loop_tree_root,
+ process_bb_node_for_hard_reg_moves, NULL);
+}
+
+
+
+/* Function called once during compiler work. */
+void
+ira_init_costs_once (void)
+{
+ int i;
+
+ init_cost = NULL;
+ for (i = 0; i < MAX_RECOG_OPERANDS; i++)
+ {
+ op_costs[i] = NULL;
+ this_op_costs[i] = NULL;
+ }
+ temp_costs = NULL;
+ cost_classes = NULL;
+}
+
+/* Free allocated temporary cost vectors. */
+static void
+free_ira_costs (void)
+{
+ int i;
+
+ if (init_cost != NULL)
+ free (init_cost);
+ init_cost = NULL;
+ for (i = 0; i < MAX_RECOG_OPERANDS; i++)
+ {
+ if (op_costs[i] != NULL)
+ free (op_costs[i]);
+ if (this_op_costs[i] != NULL)
+ free (this_op_costs[i]);
+ op_costs[i] = this_op_costs[i] = NULL;
+ }
+ if (temp_costs != NULL)
+ free (temp_costs);
+ temp_costs = NULL;
+ if (cost_classes != NULL)
+ free (cost_classes);
+ cost_classes = NULL;
+}
+
+/* This is called each time register related information is
+ changed. */
+void
+ira_init_costs (void)
+{
+ int i;
+
+ free_ira_costs ();
+ max_struct_costs_size
+ = sizeof (struct costs) + sizeof (int) * (ira_important_classes_num - 1);
+ /* Don't use ira_allocate because vectors live through several IRA calls. */
+ init_cost = (struct costs *) xmalloc (max_struct_costs_size);
+ init_cost->mem_cost = 1000000;
+ for (i = 0; i < ira_important_classes_num; i++)
+ init_cost->cost[i] = 1000000;
+ for (i = 0; i < MAX_RECOG_OPERANDS; i++)
+ {
+ op_costs[i] = (struct costs *) xmalloc (max_struct_costs_size);
+ this_op_costs[i] = (struct costs *) xmalloc (max_struct_costs_size);
+ }
+ temp_costs = (struct costs *) xmalloc (max_struct_costs_size);
+ cost_classes = (enum reg_class *) xmalloc (sizeof (enum reg_class)
+ * ira_important_classes_num);
+}
+
+/* Function called once at the end of compiler work. */
+void
+ira_finish_costs_once (void)
+{
+ free_ira_costs ();
+}
+
+
+
+/* Entry function which defines cover class, memory and hard register
+ costs for each allocno. */
+void
+ira_costs (void)
+{
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ allocno_costs = (struct costs *) ira_allocate (max_struct_costs_size
+ * ira_allocnos_num);
+ total_costs = (struct costs *) ira_allocate (max_struct_costs_size
+ * ira_allocnos_num);
+ allocno_pref_buffer
+ = (enum reg_class *) ira_allocate (sizeof (enum reg_class)
+ * ira_allocnos_num);
+ find_allocno_class_costs ();
+ setup_allocno_cover_class_and_costs ();
+ /* Because we could process operands only as subregs, check mode of
+ the registers themselves too. */
+ FOR_EACH_ALLOCNO (a, ai)
+ if (ira_register_move_cost[ALLOCNO_MODE (a)] == NULL
+ && have_regs_of_mode[ALLOCNO_MODE (a)])
+ ira_init_register_move_cost (ALLOCNO_MODE (a));
+ ira_free (allocno_pref_buffer);
+ ira_free (total_costs);
+ ira_free (allocno_costs);
+}
+
+
+
+/* Change hard register costs for allocnos which lives through
+ function calls. This is called only when we found all intersected
+ calls during building allocno live ranges. */
+void
+ira_tune_allocno_costs_and_cover_classes (void)
+{
+ int j, n, regno;
+ int cost, min_cost, *reg_costs;
+ enum reg_class cover_class, rclass;
+ enum machine_mode mode;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ cover_class = ALLOCNO_COVER_CLASS (a);
+ if (cover_class == NO_REGS)
+ continue;
+ mode = ALLOCNO_MODE (a);
+ n = ira_class_hard_regs_num[cover_class];
+ min_cost = INT_MAX;
+ if (ALLOCNO_CALLS_CROSSED_NUM (a) != 0)
+ {
+ ira_allocate_and_set_costs
+ (&ALLOCNO_HARD_REG_COSTS (a), cover_class,
+ ALLOCNO_COVER_CLASS_COST (a));
+ reg_costs = ALLOCNO_HARD_REG_COSTS (a);
+ for (j = n - 1; j >= 0; j--)
+ {
+ regno = ira_class_hard_regs[cover_class][j];
+ rclass = REGNO_REG_CLASS (regno);
+ cost = 0;
+ /* ??? If only part is call clobbered. */
+ if (! ira_hard_reg_not_in_set_p (regno, mode, call_used_reg_set))
+ cost += (ALLOCNO_CALL_FREQ (a)
+ * (ira_memory_move_cost[mode][rclass][0]
+ + ira_memory_move_cost[mode][rclass][1]));
+#ifdef IRA_HARD_REGNO_ADD_COST_MULTIPLIER
+ cost += ((ira_memory_move_cost[mode][rclass][0]
+ + ira_memory_move_cost[mode][rclass][1])
+ * ALLOCNO_FREQ (a)
+ * IRA_HARD_REGNO_ADD_COST_MULTIPLIER (regno) / 2);
+#endif
+ reg_costs[j] += cost;
+ if (min_cost > reg_costs[j])
+ min_cost = reg_costs[j];
+ }
+ }
+ if (min_cost != INT_MAX)
+ ALLOCNO_COVER_CLASS_COST (a) = min_cost;
+ }
+}
diff --git a/gcc/ira-emit.c b/gcc/ira-emit.c
new file mode 100644
index 0000000..d18be80
--- /dev/null
+++ b/gcc/ira-emit.c
@@ -0,0 +1,1019 @@
+/* Integrated Register Allocator. Changing code and generating moves.
+ 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 "obstack.h"
+#include "bitmap.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "expr.h"
+#include "recog.h"
+#include "params.h"
+#include "timevar.h"
+#include "tree-pass.h"
+#include "output.h"
+#include "reload.h"
+#include "errors.h"
+#include "df.h"
+#include "ira-int.h"
+
+
+typedef struct move *move_t;
+
+/* The structure represents an allocno move. Both allocnos have the
+ same origional regno but different allocation. */
+struct move
+{
+ /* The allocnos involved in the move. */
+ ira_allocno_t from, to;
+ /* The next move in the move sequence. */
+ move_t next;
+ /* Used for finding dependencies. */
+ bool visited_p;
+ /* The size of the following array. */
+ int deps_num;
+ /* Moves on which given move depends on. Dependency can be cyclic.
+ It means we need a temporary to generates the moves. Sequence
+ A1->A2, B1->B2 where A1 and B2 are assigned to reg R1 and A2 and
+ B1 are assigned to reg R2 is an example of the cyclic
+ dependencies. */
+ move_t *deps;
+ /* First insn generated for the move. */
+ rtx insn;
+};
+
+/* Array of moves (indexed by BB index) which should be put at the
+ start/end of the corresponding basic blocks. */
+static move_t *at_bb_start, *at_bb_end;
+
+/* Max regno before renaming some pseudo-registers. For example, the
+ same pseudo-register can be renamed in a loop if its allocation is
+ different outside the loop. */
+static int max_regno_before_changing;
+
+/* Return new move of allocnos TO and FROM. */
+static move_t
+create_move (ira_allocno_t to, ira_allocno_t from)
+{
+ move_t move;
+
+ move = (move_t) ira_allocate (sizeof (struct move));
+ move->deps = NULL;
+ move->deps_num = 0;
+ move->to = to;
+ move->from = from;
+ move->next = NULL;
+ move->insn = NULL_RTX;
+ move->visited_p = false;
+ return move;
+}
+
+/* Free memory for MOVE and its dependencies. */
+static void
+free_move (move_t move)
+{
+ if (move->deps != NULL)
+ ira_free (move->deps);
+ ira_free (move);
+}
+
+/* Free memory for list of the moves given by its HEAD. */
+static void
+free_move_list (move_t head)
+{
+ move_t next;
+
+ for (; head != NULL; head = next)
+ {
+ next = head->next;
+ free_move (head);
+ }
+}
+
+/* Return TRUE if the the move list LIST1 and LIST2 are equal (two
+ moves are equal if they involve the same allocnos). */
+static bool
+eq_move_lists_p (move_t list1, move_t list2)
+{
+ for (; list1 != NULL && list2 != NULL;
+ list1 = list1->next, list2 = list2->next)
+ if (list1->from != list2->from || list1->to != list2->to)
+ return false;
+ return list1 == list2;
+}
+
+/* This recursive function changes pseudo-registers in *LOC if it is
+ necessary. The function returns TRUE if a change was done. */
+static bool
+change_regs (rtx *loc)
+{
+ int i, regno, result = false;
+ const char *fmt;
+ enum rtx_code code;
+
+ if (*loc == NULL_RTX)
+ return false;
+ code = GET_CODE (*loc);
+ if (code == REG)
+ {
+ regno = REGNO (*loc);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ return false;
+ if (regno >= max_regno_before_changing)
+ /* It is a shared register which was changed already. */
+ return false;
+ if (ira_curr_regno_allocno_map[regno] == NULL)
+ return false;
+ *loc = ALLOCNO_REG (ira_curr_regno_allocno_map[regno]);
+ return true;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ result = change_regs (&XEXP (*loc, i)) || result;
+ else if (fmt[i] == 'E')
+ {
+ int j;
+
+ for (j = XVECLEN (*loc, i) - 1; j >= 0; j--)
+ result = change_regs (&XVECEXP (*loc, i, j)) || result;
+ }
+ }
+ return result;
+}
+
+/* Attach MOVE to the edge E. The move is attached to the head of the
+ list if HEAD_P is TRUE. */
+static void
+add_to_edge_list (edge e, move_t move, bool head_p)
+{
+ move_t last;
+
+ if (head_p || e->aux == NULL)
+ {
+ move->next = (move_t) e->aux;
+ e->aux = move;
+ }
+ else
+ {
+ for (last = (move_t) e->aux; last->next != NULL; last = last->next)
+ ;
+ last->next = move;
+ move->next = NULL;
+ }
+}
+
+/* Create and return new pseudo-register with the same attributes as
+ ORIGINAL_REG. */
+static rtx
+create_new_reg (rtx original_reg)
+{
+ rtx new_reg;
+
+ new_reg = gen_reg_rtx (GET_MODE (original_reg));
+ ORIGINAL_REGNO (new_reg) = ORIGINAL_REGNO (original_reg);
+ REG_USERVAR_P (new_reg) = REG_USERVAR_P (original_reg);
+ REG_POINTER (new_reg) = REG_POINTER (original_reg);
+ REG_ATTRS (new_reg) = REG_ATTRS (original_reg);
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Creating newreg=%i from oldreg=%i\n",
+ REGNO (new_reg), REGNO (original_reg));
+ return new_reg;
+}
+
+/* Return TRUE if loop given by SUBNODE inside the loop given by
+ NODE. */
+static bool
+subloop_tree_node_p (ira_loop_tree_node_t subnode, ira_loop_tree_node_t node)
+{
+ for (; subnode != NULL; subnode = subnode->parent)
+ if (subnode == node)
+ return true;
+ return false;
+}
+
+/* Set up member `reg' to REG for allocnos which has the same regno as
+ ALLOCNO and which are inside the loop corresponding to ALLOCNO. */
+static void
+set_allocno_reg (ira_allocno_t allocno, rtx reg)
+{
+ int regno;
+ ira_allocno_t a;
+ ira_loop_tree_node_t node;
+
+ node = ALLOCNO_LOOP_TREE_NODE (allocno);
+ for (a = ira_regno_allocno_map[ALLOCNO_REGNO (allocno)];
+ a != NULL;
+ a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
+ if (subloop_tree_node_p (ALLOCNO_LOOP_TREE_NODE (a), node))
+ ALLOCNO_REG (a) = reg;
+ for (a = ALLOCNO_CAP (allocno); a != NULL; a = ALLOCNO_CAP (a))
+ ALLOCNO_REG (a) = reg;
+ regno = ALLOCNO_REGNO (allocno);
+ for (a = allocno;;)
+ {
+ if (a == NULL || (a = ALLOCNO_CAP (a)) == NULL)
+ {
+ node = node->parent;
+ if (node == NULL)
+ break;
+ a = node->regno_allocno_map[regno];
+ }
+ if (a == NULL)
+ continue;
+ if (ALLOCNO_CHILD_RENAMED_P (a))
+ break;
+ ALLOCNO_CHILD_RENAMED_P (a) = true;
+ }
+}
+
+/* Return TRUE if move of SRC_ALLOCNO to DEST_ALLOCNO does not change
+ value of the destination. One possible reason for this is the
+ situation when SRC_ALLOCNO is not modified in the corresponding
+ loop. */
+static bool
+not_modified_p (ira_allocno_t src_allocno, ira_allocno_t dest_allocno)
+{
+ int regno, orig_regno;
+ ira_allocno_t a;
+ ira_loop_tree_node_t node;
+
+ ira_assert (ALLOCNO_CAP_MEMBER (src_allocno) == NULL
+ && ALLOCNO_CAP_MEMBER (dest_allocno) == NULL);
+ orig_regno = ALLOCNO_REGNO (src_allocno);
+ regno = REGNO (ALLOCNO_REG (dest_allocno));
+ for (node = ALLOCNO_LOOP_TREE_NODE (src_allocno);
+ node != NULL;
+ node = node->parent)
+ if ((a = node->regno_allocno_map[orig_regno]) == NULL)
+ break;
+ else if (REGNO (ALLOCNO_REG (a)) == (unsigned) regno)
+ return true;
+ else if (bitmap_bit_p (node->modified_regnos, orig_regno))
+ return false;
+ return node != NULL;
+}
+
+/* Generate and attach moves to the edge E. This looks at the final
+ regnos of allocnos living on the edge with the same original regno
+ to figure out when moves should be generated. */
+static void
+generate_edge_moves (edge e)
+{
+ ira_loop_tree_node_t src_loop_node, dest_loop_node;
+ unsigned int regno;
+ bitmap_iterator bi;
+ ira_allocno_t src_allocno, dest_allocno, *src_map, *dest_map;
+ move_t move;
+
+ src_loop_node = IRA_BB_NODE (e->src)->parent;
+ dest_loop_node = IRA_BB_NODE (e->dest)->parent;
+ e->aux = NULL;
+ if (src_loop_node == dest_loop_node)
+ return;
+ src_map = src_loop_node->regno_allocno_map;
+ dest_map = dest_loop_node->regno_allocno_map;
+ EXECUTE_IF_SET_IN_REG_SET (DF_LR_IN (e->dest),
+ FIRST_PSEUDO_REGISTER, regno, bi)
+ if (bitmap_bit_p (DF_LR_OUT (e->src), regno))
+ {
+ src_allocno = src_map[regno];
+ dest_allocno = dest_map[regno];
+ if (REGNO (ALLOCNO_REG (src_allocno))
+ == REGNO (ALLOCNO_REG (dest_allocno)))
+ continue;
+ /* Actually it is not a optimization we need this code because
+ the memory (remember about equivalent memory) might be ROM
+ (or placed in read only section). */
+ if (ALLOCNO_HARD_REGNO (dest_allocno) < 0
+ && ALLOCNO_HARD_REGNO (src_allocno) >= 0
+ && not_modified_p (src_allocno, dest_allocno))
+ {
+ ALLOCNO_MEM_OPTIMIZED_DEST (src_allocno) = dest_allocno;
+ ALLOCNO_MEM_OPTIMIZED_DEST_P (dest_allocno) = true;
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Remove r%d:a%d->a%d(mem)\n",
+ regno, ALLOCNO_NUM (src_allocno),
+ ALLOCNO_NUM (dest_allocno));
+ continue;
+ }
+ move = create_move (dest_allocno, src_allocno);
+ add_to_edge_list (e, move, true);
+ }
+}
+
+/* Bitmap of allocnos local for the current loop. */
+static bitmap local_allocno_bitmap;
+
+/* This bitmap is used to find that we need to generate and to use a
+ new pseudo-register when processing allocnos with the same original
+ regno. */
+static bitmap used_regno_bitmap;
+
+/* This bitmap contains regnos of allocnos which were renamed locally
+ because the allocnos correspond to disjoint live ranges in loops
+ with a common parent. */
+static bitmap renamed_regno_bitmap;
+
+/* Change (if necessary) pseudo-registers inside loop given by loop
+ tree node NODE. */
+static void
+change_loop (ira_loop_tree_node_t node)
+{
+ bitmap_iterator bi;
+ unsigned int i;
+ int regno;
+ bool used_p;
+ ira_allocno_t allocno, parent_allocno, *map;
+ rtx insn, original_reg;
+ enum reg_class cover_class;
+ ira_loop_tree_node_t parent;
+
+ if (node != ira_loop_tree_root)
+ {
+
+ if (node->bb != NULL)
+ {
+ FOR_BB_INSNS (node->bb, insn)
+ if (INSN_P (insn) && change_regs (&insn))
+ {
+ df_insn_rescan (insn);
+ df_notes_rescan (insn);
+ }
+ return;
+ }
+
+ if (internal_flag_ira_verbose > 3 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Changing RTL for loop %d (header bb%d)\n",
+ node->loop->num, node->loop->header->index);
+
+ parent = ira_curr_loop_tree_node->parent;
+ map = parent->regno_allocno_map;
+ EXECUTE_IF_SET_IN_REG_SET (ira_curr_loop_tree_node->border_allocnos,
+ 0, i, bi)
+ {
+ allocno = ira_allocnos[i];
+ regno = ALLOCNO_REGNO (allocno);
+ cover_class = ALLOCNO_COVER_CLASS (allocno);
+ parent_allocno = map[regno];
+ ira_assert (regno < ira_reg_equiv_len);
+ /* We generate the same hard register move because the
+ reload pass can put an allocno into memory in this case
+ we will have live range splitting. If it does not happen
+ such the same hard register moves will be removed. The
+ worst case when the both allocnos are put into memory by
+ the reload is very rare. */
+ if (parent_allocno != NULL
+ && (ALLOCNO_HARD_REGNO (allocno)
+ == ALLOCNO_HARD_REGNO (parent_allocno))
+ && (ALLOCNO_HARD_REGNO (allocno) < 0
+ || (parent->reg_pressure[cover_class] + 1
+ <= ira_available_class_regs[cover_class])
+ || TEST_HARD_REG_BIT (ira_prohibited_mode_move_regs
+ [ALLOCNO_MODE (allocno)],
+ ALLOCNO_HARD_REGNO (allocno))
+ /* don't create copies because reload can spill an
+ allocno set by copy although the allocno will not
+ get memory slot. */
+ || ira_reg_equiv_invariant_p[regno]
+ || ira_reg_equiv_const[regno] != NULL_RTX))
+ continue;
+ original_reg = ALLOCNO_REG (allocno);
+ if (parent_allocno == NULL
+ || REGNO (ALLOCNO_REG (parent_allocno)) == REGNO (original_reg))
+ {
+ if (internal_flag_ira_verbose > 3 && ira_dump_file)
+ fprintf (ira_dump_file, " %i vs parent %i:",
+ ALLOCNO_HARD_REGNO (allocno),
+ ALLOCNO_HARD_REGNO (parent_allocno));
+ set_allocno_reg (allocno, create_new_reg (original_reg));
+ }
+ }
+ }
+ /* Rename locals: Local allocnos with same regno in different loops
+ might get the different hard register. So we need to change
+ ALLOCNO_REG. */
+ bitmap_and_compl (local_allocno_bitmap,
+ ira_curr_loop_tree_node->mentioned_allocnos,
+ ira_curr_loop_tree_node->border_allocnos);
+ EXECUTE_IF_SET_IN_REG_SET (local_allocno_bitmap, 0, i, bi)
+ {
+ allocno = ira_allocnos[i];
+ regno = ALLOCNO_REGNO (allocno);
+ if (ALLOCNO_CAP_MEMBER (allocno) != NULL)
+ continue;
+ used_p = bitmap_bit_p (used_regno_bitmap, regno);
+ bitmap_set_bit (used_regno_bitmap, regno);
+ ALLOCNO_SOMEWHERE_RENAMED_P (allocno) = true;
+ if (! used_p)
+ continue;
+ bitmap_set_bit (renamed_regno_bitmap, regno);
+ set_allocno_reg (allocno, create_new_reg (ALLOCNO_REG (allocno)));
+ }
+}
+
+/* Process to set up flag somewhere_renamed_p. */
+static void
+set_allocno_somewhere_renamed_p (void)
+{
+ unsigned int regno;
+ ira_allocno_t allocno;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (allocno, ai)
+ {
+ regno = ALLOCNO_REGNO (allocno);
+ if (bitmap_bit_p (renamed_regno_bitmap, regno)
+ && REGNO (ALLOCNO_REG (allocno)) == regno)
+ ALLOCNO_SOMEWHERE_RENAMED_P (allocno) = true;
+ }
+}
+
+/* Return TRUE if move lists on all edges given in vector VEC are
+ equal. */
+static bool
+eq_edge_move_lists_p (VEC(edge,gc) *vec)
+{
+ move_t list;
+ int i;
+
+ list = (move_t) EDGE_I (vec, 0)->aux;
+ for (i = EDGE_COUNT (vec) - 1; i > 0; i--)
+ if (! eq_move_lists_p (list, (move_t) EDGE_I (vec, i)->aux))
+ return false;
+ return true;
+}
+
+/* Look at all entry edges (if START_P) or exit edges of basic block
+ BB and put move lists at the BB start or end if it is possible. In
+ other words, this decreases code duplication of allocno moves. */
+static void
+unify_moves (basic_block bb, bool start_p)
+{
+ int i;
+ edge e;
+ move_t list;
+ VEC(edge,gc) *vec;
+
+ vec = (start_p ? bb->preds : bb->succs);
+ if (EDGE_COUNT (vec) == 0 || ! eq_edge_move_lists_p (vec))
+ return;
+ e = EDGE_I (vec, 0);
+ list = (move_t) e->aux;
+ if (! start_p && control_flow_insn_p (BB_END (bb)))
+ return;
+ e->aux = NULL;
+ for (i = EDGE_COUNT (vec) - 1; i > 0; i--)
+ {
+ e = EDGE_I (vec, i);
+ free_move_list ((move_t) e->aux);
+ e->aux = NULL;
+ }
+ if (start_p)
+ at_bb_start[bb->index] = list;
+ else
+ at_bb_end[bb->index] = list;
+}
+
+/* Last move (in move sequence being processed) setting up the
+ corresponding hard register. */
+static move_t hard_regno_last_set[FIRST_PSEUDO_REGISTER];
+
+/* If the element value is equal to CURR_TICK then the corresponding
+ element in `hard_regno_last_set' is defined and correct. */
+static int hard_regno_last_set_check[FIRST_PSEUDO_REGISTER];
+
+/* Last move (in move sequence being processed) setting up the
+ corresponding allocno. */
+static move_t *allocno_last_set;
+
+/* If the element value is equal to CURR_TICK then the corresponding
+ element in . `allocno_last_set' is defined and correct. */
+static int *allocno_last_set_check;
+
+/* Definition of vector of moves. */
+DEF_VEC_P(move_t);
+DEF_VEC_ALLOC_P(move_t, heap);
+
+/* This vec contains moves sorted topologically (depth-first) on their
+ dependency graph. */
+static VEC(move_t,heap) *move_vec;
+
+/* The variable value is used to check correctness of values of
+ elements of arrays `hard_regno_last_set' and
+ `allocno_last_set_check'. */
+static int curr_tick;
+
+/* This recursive function traverses dependencies of MOVE and produces
+ topological sorting (in depth-first order). */
+static void
+traverse_moves (move_t move)
+{
+ int i;
+
+ if (move->visited_p)
+ return;
+ move->visited_p = true;
+ for (i = move->deps_num - 1; i >= 0; i--)
+ traverse_moves (move->deps[i]);
+ VEC_safe_push (move_t, heap, move_vec, move);
+}
+
+/* Remove unnecessary moves in the LIST, makes topological sorting,
+ and removes cycles on hard reg dependencies by introducing new
+ allocnos assigned to memory and additional moves. It returns the
+ result move list. */
+static move_t
+modify_move_list (move_t list)
+{
+ int i, n, nregs, hard_regno;
+ ira_allocno_t to, from, new_allocno;
+ move_t move, new_move, set_move, first, last;
+
+ if (list == NULL)
+ return NULL;
+ /* Creat move deps. */
+ curr_tick++;
+ for (move = list; move != NULL; move = move->next)
+ {
+ to = move->to;
+ if ((hard_regno = ALLOCNO_HARD_REGNO (to)) < 0)
+ continue;
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (to)];
+ for (i = 0; i < nregs; i++)
+ {
+ hard_regno_last_set[hard_regno + i] = move;
+ hard_regno_last_set_check[hard_regno + i] = curr_tick;
+ }
+ }
+ for (move = list; move != NULL; move = move->next)
+ {
+ from = move->from;
+ to = move->to;
+ if ((hard_regno = ALLOCNO_HARD_REGNO (from)) >= 0)
+ {
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (from)];
+ for (n = i = 0; i < nregs; i++)
+ if (hard_regno_last_set_check[hard_regno + i] == curr_tick
+ && (ALLOCNO_REGNO (hard_regno_last_set[hard_regno + i]->to)
+ != ALLOCNO_REGNO (from)))
+ n++;
+ move->deps = (move_t *) ira_allocate (n * sizeof (move_t));
+ for (n = i = 0; i < nregs; i++)
+ if (hard_regno_last_set_check[hard_regno + i] == curr_tick
+ && (ALLOCNO_REGNO (hard_regno_last_set[hard_regno + i]->to)
+ != ALLOCNO_REGNO (from)))
+ move->deps[n++] = hard_regno_last_set[hard_regno + i];
+ move->deps_num = n;
+ }
+ }
+ /* Toplogical sorting: */
+ VEC_truncate (move_t, move_vec, 0);
+ for (move = list; move != NULL; move = move->next)
+ traverse_moves (move);
+ last = NULL;
+ for (i = (int) VEC_length (move_t, move_vec) - 1; i >= 0; i--)
+ {
+ move = VEC_index (move_t, move_vec, i);
+ move->next = NULL;
+ if (last != NULL)
+ last->next = move;
+ last = move;
+ }
+ first = VEC_last (move_t, move_vec);
+ /* Removing cycles: */
+ curr_tick++;
+ VEC_truncate (move_t, move_vec, 0);
+ for (move = first; move != NULL; move = move->next)
+ {
+ from = move->from;
+ to = move->to;
+ if ((hard_regno = ALLOCNO_HARD_REGNO (from)) >= 0)
+ {
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (from)];
+ for (i = 0; i < nregs; i++)
+ if (hard_regno_last_set_check[hard_regno + i] == curr_tick
+ && ALLOCNO_HARD_REGNO
+ (hard_regno_last_set[hard_regno + i]->to) >= 0)
+ {
+ set_move = hard_regno_last_set[hard_regno + i];
+ /* It does not matter what loop_tree_node (of TO or
+ FROM) to use for the new allocno because of
+ subsequent IRA internal representation
+ flattening. */
+ new_allocno
+ = ira_create_allocno (ALLOCNO_REGNO (set_move->to), false,
+ ALLOCNO_LOOP_TREE_NODE (set_move->to));
+ ALLOCNO_MODE (new_allocno) = ALLOCNO_MODE (set_move->to);
+ ira_set_allocno_cover_class
+ (new_allocno, ALLOCNO_COVER_CLASS (set_move->to));
+ ALLOCNO_ASSIGNED_P (new_allocno) = true;
+ ALLOCNO_HARD_REGNO (new_allocno) = -1;
+ ALLOCNO_REG (new_allocno)
+ = create_new_reg (ALLOCNO_REG (set_move->to));
+ ALLOCNO_CONFLICT_ID (new_allocno) = ALLOCNO_NUM (new_allocno);
+ /* Make it possibly conflicting with all earlier
+ created allocnos. Cases where temporary allocnos
+ created to remove the cycles are quite rare. */
+ ALLOCNO_MIN (new_allocno) = 0;
+ ALLOCNO_MAX (new_allocno) = ira_allocnos_num - 1;
+ new_move = create_move (set_move->to, new_allocno);
+ set_move->to = new_allocno;
+ VEC_safe_push (move_t, heap, move_vec, new_move);
+ ira_move_loops_num++;
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Creating temporary allocno a%dr%d\n",
+ ALLOCNO_NUM (new_allocno),
+ REGNO (ALLOCNO_REG (new_allocno)));
+ }
+ }
+ if ((hard_regno = ALLOCNO_HARD_REGNO (to)) < 0)
+ continue;
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (to)];
+ for (i = 0; i < nregs; i++)
+ {
+ hard_regno_last_set[hard_regno + i] = move;
+ hard_regno_last_set_check[hard_regno + i] = curr_tick;
+ }
+ }
+ for (i = (int) VEC_length (move_t, move_vec) - 1; i >= 0; i--)
+ {
+ move = VEC_index (move_t, move_vec, i);
+ move->next = NULL;
+ last->next = move;
+ last = move;
+ }
+ return first;
+}
+
+/* Generate RTX move insns from the move list LIST. This updates
+ allocation cost using move execution frequency FREQ. */
+static rtx
+emit_move_list (move_t list, int freq)
+{
+ int cost;
+ rtx result, insn;
+ enum machine_mode mode;
+ enum reg_class cover_class;
+
+ start_sequence ();
+ for (; list != NULL; list = list->next)
+ {
+ start_sequence ();
+ emit_move_insn (ALLOCNO_REG (list->to), ALLOCNO_REG (list->from));
+ list->insn = get_insns ();
+ end_sequence ();
+ /* The reload needs to have set up insn codes. If the reload
+ sets up insn codes by itself, it may fail because insns will
+ have hard registers instead of pseudos and there may be no
+ machine insn with given hard registers. */
+ for (insn = list->insn; insn != NULL_RTX; insn = NEXT_INSN (insn))
+ recog_memoized (insn);
+ emit_insn (list->insn);
+ mode = ALLOCNO_MODE (list->to);
+ cover_class = ALLOCNO_COVER_CLASS (list->to);
+ cost = 0;
+ if (ALLOCNO_HARD_REGNO (list->to) < 0)
+ {
+ if (ALLOCNO_HARD_REGNO (list->from) >= 0)
+ {
+ cost = ira_memory_move_cost[mode][cover_class][0] * freq;
+ ira_store_cost += cost;
+ }
+ }
+ else if (ALLOCNO_HARD_REGNO (list->from) < 0)
+ {
+ if (ALLOCNO_HARD_REGNO (list->to) >= 0)
+ {
+ cost = ira_memory_move_cost[mode][cover_class][0] * freq;
+ ira_load_cost += cost;
+ }
+ }
+ else
+ {
+ cost = ira_register_move_cost[mode][cover_class][cover_class] * freq;
+ ira_shuffle_cost += cost;
+ }
+ ira_overall_cost += cost;
+ }
+ result = get_insns ();
+ end_sequence ();
+ return result;
+}
+
+/* Generate RTX move insns from move lists attached to basic blocks
+ and edges. */
+static void
+emit_moves (void)
+{
+ basic_block bb;
+ edge_iterator ei;
+ edge e;
+ rtx insns, tmp;
+
+ FOR_EACH_BB (bb)
+ {
+ if (at_bb_start[bb->index] != NULL)
+ {
+ at_bb_start[bb->index] = modify_move_list (at_bb_start[bb->index]);
+ insns = emit_move_list (at_bb_start[bb->index],
+ REG_FREQ_FROM_BB (bb));
+ tmp = BB_HEAD (bb);
+ if (LABEL_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (NOTE_INSN_BASIC_BLOCK_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (tmp == BB_HEAD (bb))
+ emit_insn_before (insns, tmp);
+ else if (tmp != NULL_RTX)
+ emit_insn_after (insns, PREV_INSN (tmp));
+ else
+ emit_insn_after (insns, get_last_insn ());
+ }
+
+ if (at_bb_end[bb->index] != NULL)
+ {
+ at_bb_end[bb->index] = modify_move_list (at_bb_end[bb->index]);
+ insns = emit_move_list (at_bb_end[bb->index], REG_FREQ_FROM_BB (bb));
+ ira_assert (! control_flow_insn_p (BB_END (bb)));
+ emit_insn_after (insns, BB_END (bb));
+ }
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ if (e->aux == NULL)
+ continue;
+ ira_assert ((e->flags & EDGE_ABNORMAL) == 0
+ || ! EDGE_CRITICAL_P (e));
+ e->aux = modify_move_list ((move_t) e->aux);
+ insert_insn_on_edge
+ (emit_move_list ((move_t) e->aux,
+ REG_FREQ_FROM_EDGE_FREQ (EDGE_FREQUENCY (e))),
+ e);
+ if (e->src->next_bb != e->dest)
+ ira_additional_jumps_num++;
+ }
+ }
+}
+
+/* Update costs of A and corresponding allocnos on upper levels on the
+ loop tree from reading (if READ_P) or writing A on an execution
+ path with FREQ. */
+static void
+update_costs (ira_allocno_t a, bool read_p, int freq)
+{
+ ira_loop_tree_node_t parent;
+
+ for (;;)
+ {
+ ALLOCNO_NREFS (a)++;
+ ALLOCNO_FREQ (a) += freq;
+ ALLOCNO_MEMORY_COST (a)
+ += (ira_memory_move_cost[ALLOCNO_MODE (a)][ALLOCNO_COVER_CLASS (a)]
+ [read_p ? 1 : 0] * freq);
+ if (ALLOCNO_CAP (a) != NULL)
+ a = ALLOCNO_CAP (a);
+ else if ((parent = ALLOCNO_LOOP_TREE_NODE (a)->parent) == NULL
+ || (a = parent->regno_allocno_map[ALLOCNO_REGNO (a)]) == NULL)
+ break;
+ }
+}
+
+/* Process moves from LIST with execution FREQ to add ranges, copies,
+ and modify costs for allocnos involved in the moves. All regnos
+ living through the list is in LIVE_THROUGH, and the loop tree node
+ used to find corresponding allocnos is NODE. */
+static void
+add_range_and_copies_from_move_list (move_t list, ira_loop_tree_node_t node,
+ bitmap live_through, int freq)
+{
+ int start, n;
+ unsigned int regno;
+ move_t move;
+ ira_allocno_t to, from, a;
+ ira_copy_t cp;
+ allocno_live_range_t r;
+ bitmap_iterator bi;
+ HARD_REG_SET hard_regs_live;
+
+ if (list == NULL)
+ return;
+ n = 0;
+ EXECUTE_IF_SET_IN_BITMAP (live_through, FIRST_PSEUDO_REGISTER, regno, bi)
+ n++;
+ REG_SET_TO_HARD_REG_SET (hard_regs_live, live_through);
+ /* This is a trick to guarantee that new ranges is not merged with
+ the old ones. */
+ ira_max_point++;
+ start = ira_max_point;
+ for (move = list; move != NULL; move = move->next)
+ {
+ from = move->from;
+ to = move->to;
+ if (ALLOCNO_CONFLICT_ALLOCNO_ARRAY (to) == NULL)
+ {
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Allocate conflicts for a%dr%d\n",
+ ALLOCNO_NUM (to), REGNO (ALLOCNO_REG (to)));
+ ira_allocate_allocno_conflicts (to, n);
+ }
+ bitmap_clear_bit (live_through, ALLOCNO_REGNO (from));
+ bitmap_clear_bit (live_through, ALLOCNO_REGNO (to));
+ IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (from), hard_regs_live);
+ IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (to), hard_regs_live);
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (from),
+ hard_regs_live);
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (to), hard_regs_live);
+ update_costs (from, true, freq);
+ update_costs (to, false, freq);
+ cp = ira_add_allocno_copy (from, to, freq, move->insn, NULL);
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, " Adding cp%d:a%dr%d-a%dr%d\n",
+ cp->num, ALLOCNO_NUM (cp->first),
+ REGNO (ALLOCNO_REG (cp->first)), ALLOCNO_NUM (cp->second),
+ REGNO (ALLOCNO_REG (cp->second)));
+ r = ALLOCNO_LIVE_RANGES (from);
+ if (r == NULL || r->finish >= 0)
+ {
+ ALLOCNO_LIVE_RANGES (from)
+ = ira_create_allocno_live_range (from, start, ira_max_point, r);
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Adding range [%d..%d] to allocno a%dr%d\n",
+ start, ira_max_point, ALLOCNO_NUM (from),
+ REGNO (ALLOCNO_REG (from)));
+ }
+ else
+ r->finish = ira_max_point;
+ ira_max_point++;
+ ALLOCNO_LIVE_RANGES (to)
+ = ira_create_allocno_live_range (to, ira_max_point, -1,
+ ALLOCNO_LIVE_RANGES (to));
+ ira_max_point++;
+ }
+ for (move = list; move != NULL; move = move->next)
+ {
+ r = ALLOCNO_LIVE_RANGES (move->to);
+ if (r->finish < 0)
+ {
+ r->finish = ira_max_point - 1;
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " Adding range [%d..%d] to allocno a%dr%d\n",
+ r->start, r->finish, ALLOCNO_NUM (move->to),
+ REGNO (ALLOCNO_REG (move->to)));
+ }
+ }
+ EXECUTE_IF_SET_IN_BITMAP (live_through, FIRST_PSEUDO_REGISTER, regno, bi)
+ {
+ a = node->regno_allocno_map[regno];
+ if (ALLOCNO_MEM_OPTIMIZED_DEST (a) == NULL)
+ {
+ ALLOCNO_LIVE_RANGES (a)
+ = ira_create_allocno_live_range (a, start, ira_max_point - 1,
+ ALLOCNO_LIVE_RANGES (a));
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf
+ (ira_dump_file,
+ " Adding range [%d..%d] to live through allocno a%dr%d\n",
+ start, ira_max_point - 1, ALLOCNO_NUM (a),
+ REGNO (ALLOCNO_REG (a)));
+ }
+ }
+}
+
+/* Process all move list to add ranges, conflicts, copies, and modify
+ costs for allocnos involved in the moves. */
+static void
+add_ranges_and_copies (void)
+{
+ basic_block bb;
+ edge_iterator ei;
+ edge e;
+ ira_loop_tree_node_t node;
+ bitmap live_through;
+
+ live_through = ira_allocate_bitmap ();
+ FOR_EACH_BB (bb)
+ {
+ /* It does not matter what loop_tree_node (of source or
+ destination block) to use for searching allocnos by their
+ regnos because of subsequent IR flattening. */
+ node = IRA_BB_NODE (bb)->parent;
+ bitmap_copy (live_through, DF_LR_IN (bb));
+ add_range_and_copies_from_move_list
+ (at_bb_start[bb->index], node, live_through, REG_FREQ_FROM_BB (bb));
+ bitmap_copy (live_through, DF_LR_OUT (bb));
+ add_range_and_copies_from_move_list
+ (at_bb_end[bb->index], node, live_through, REG_FREQ_FROM_BB (bb));
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ bitmap_and (live_through, DF_LR_IN (e->dest), DF_LR_OUT (bb));
+ add_range_and_copies_from_move_list
+ ((move_t) e->aux, node, live_through,
+ REG_FREQ_FROM_EDGE_FREQ (EDGE_FREQUENCY (e)));
+ }
+ }
+ ira_free_bitmap (live_through);
+}
+
+/* The entry function changes code and generates shuffling allocnos on
+ region borders for the regional (LOOPS_P is TRUE in this case)
+ register allocation. */
+void
+ira_emit (bool loops_p)
+{
+ basic_block bb;
+ edge_iterator ei;
+ edge e;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ ALLOCNO_REG (a) = regno_reg_rtx[ALLOCNO_REGNO (a)];
+ if (! loops_p)
+ return;
+ at_bb_start = (move_t *) ira_allocate (sizeof (move_t) * last_basic_block);
+ memset (at_bb_start, 0, sizeof (move_t) * last_basic_block);
+ at_bb_end = (move_t *) ira_allocate (sizeof (move_t) * last_basic_block);
+ memset (at_bb_end, 0, sizeof (move_t) * last_basic_block);
+ local_allocno_bitmap = ira_allocate_bitmap ();
+ used_regno_bitmap = ira_allocate_bitmap ();
+ renamed_regno_bitmap = ira_allocate_bitmap ();
+ max_regno_before_changing = max_reg_num ();
+ ira_traverse_loop_tree (true, ira_loop_tree_root, change_loop, NULL);
+ set_allocno_somewhere_renamed_p ();
+ ira_free_bitmap (used_regno_bitmap);
+ ira_free_bitmap (renamed_regno_bitmap);
+ ira_free_bitmap (local_allocno_bitmap);
+ FOR_EACH_BB (bb)
+ {
+ at_bb_start[bb->index] = NULL;
+ at_bb_end[bb->index] = NULL;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->dest != EXIT_BLOCK_PTR)
+ generate_edge_moves (e);
+ }
+ allocno_last_set
+ = (move_t *) ira_allocate (sizeof (move_t) * max_reg_num ());
+ allocno_last_set_check
+ = (int *) ira_allocate (sizeof (int) * max_reg_num ());
+ memset (allocno_last_set_check, 0, sizeof (int) * max_reg_num ());
+ memset (hard_regno_last_set_check, 0, sizeof (hard_regno_last_set_check));
+ curr_tick = 0;
+ FOR_EACH_BB (bb)
+ unify_moves (bb, true);
+ FOR_EACH_BB (bb)
+ unify_moves (bb, false);
+ move_vec = VEC_alloc (move_t, heap, ira_allocnos_num);
+ emit_moves ();
+ add_ranges_and_copies ();
+ /* Clean up: */
+ FOR_EACH_BB (bb)
+ {
+ free_move_list (at_bb_start[bb->index]);
+ free_move_list (at_bb_end[bb->index]);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ free_move_list ((move_t) e->aux);
+ e->aux = NULL;
+ }
+ }
+ VEC_free (move_t, heap, move_vec);
+ ira_free (allocno_last_set_check);
+ ira_free (allocno_last_set);
+ commit_edge_insertions ();
+ ira_free (at_bb_end);
+ ira_free (at_bb_start);
+}
diff --git a/gcc/ira-int.h b/gcc/ira-int.h
new file mode 100644
index 0000000..f656cf1
--- /dev/null
+++ b/gcc/ira-int.h
@@ -0,0 +1,1200 @@
+/* Integrated Register Allocator (IRA) intercommunication header file.
+ 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 "cfgloop.h"
+#include "ira.h"
+#include "alloc-pool.h"
+
+/* To provide consistency in naming, all IRA external variables,
+ functions, common typedefs start with prefix ira_. */
+
+#ifdef ENABLE_CHECKING
+#define ENABLE_IRA_CHECKING
+#endif
+
+#ifdef ENABLE_IRA_CHECKING
+#define ira_assert(c) gcc_assert (c)
+#else
+#define ira_assert(c)
+#endif
+
+/* Compute register frequency from edge frequency FREQ. It is
+ analogous to REG_FREQ_FROM_BB. When optimizing for size, or
+ profile driven feedback is available and the function is never
+ executed, frequency is always equivalent. Otherwise rescale the
+ edge frequency. */
+#define REG_FREQ_FROM_EDGE_FREQ(freq) \
+ (optimize_size || (flag_branch_probabilities && !ENTRY_BLOCK_PTR->count) \
+ ? REG_FREQ_MAX : (freq * REG_FREQ_MAX / BB_FREQ_MAX) \
+ ? (freq * REG_FREQ_MAX / BB_FREQ_MAX) : 1)
+
+/* All natural loops. */
+extern struct loops ira_loops;
+
+/* A modified value of flag `-fira-verbose' used internally. */
+extern int internal_flag_ira_verbose;
+
+/* Dump file of the allocator if it is not NULL. */
+extern FILE *ira_dump_file;
+
+/* Typedefs for pointers to allocno live range, allocno, and copy of
+ allocnos. */
+typedef struct ira_allocno_live_range *allocno_live_range_t;
+typedef struct ira_allocno *ira_allocno_t;
+typedef struct ira_allocno_copy *ira_copy_t;
+
+/* Definition of vector of allocnos and copies. */
+DEF_VEC_P(ira_allocno_t);
+DEF_VEC_ALLOC_P(ira_allocno_t, heap);
+DEF_VEC_P(ira_copy_t);
+DEF_VEC_ALLOC_P(ira_copy_t, heap);
+
+/* Typedef for pointer to the subsequent structure. */
+typedef struct ira_loop_tree_node *ira_loop_tree_node_t;
+
+/* In general case, IRA is a regional allocator. The regions are
+ nested and form a tree. Currently regions are natural loops. The
+ following structure describes loop tree node (representing basic
+ block or loop). We need such tree because the loop tree from
+ cfgloop.h is not convenient for the optimization: basic blocks are
+ not a part of the tree from cfgloop.h. We also use the nodes for
+ storing additional information about basic blocks/loops for the
+ register allocation purposes. */
+struct ira_loop_tree_node
+{
+ /* The node represents basic block if children == NULL. */
+ basic_block bb; /* NULL for loop. */
+ struct loop *loop; /* NULL for BB. */
+ /* The next (loop) node of with the same parent. SUBLOOP_NEXT is
+ always NULL for BBs. */
+ ira_loop_tree_node_t subloop_next, next;
+ /* The first (loop) node immediately inside the node. SUBLOOPS is
+ always NULL for BBs. */
+ ira_loop_tree_node_t subloops, children;
+ /* The node immediately containing given node. */
+ ira_loop_tree_node_t parent;
+
+ /* Loop level in range [0, ira_loop_tree_height). */
+ int level;
+
+ /* All the following members are defined only for nodes representing
+ loops. */
+
+ /* Allocnos in the loop corresponding to their regnos. If it is
+ NULL the loop does not form a separate register allocation region
+ (e.g. because it has abnormal enter/exit edges and we can not put
+ code for register shuffling on the edges if a different
+ allocation is used for a pseudo-register on different sides of
+ the edges). Caps are not in the map (remember we can have more
+ one cap with the same regno in a region). */
+ ira_allocno_t *regno_allocno_map;
+
+ /* Maximal register pressure inside loop for given register class
+ (defined only for the cover classes). */
+ int reg_pressure[N_REG_CLASSES];
+
+ /* Numbers of allocnos referred in the loop node. */
+ bitmap mentioned_allocnos;
+
+ /* Regnos of pseudos modified in the loop node (including its
+ subloops). */
+ bitmap modified_regnos;
+
+ /* Numbers of allocnos living at the loop borders. */
+ bitmap border_allocnos;
+
+ /* Numbers of copies referred in the corresponding loop. */
+ bitmap local_copies;
+};
+
+/* The root of the loop tree corresponding to the all function. */
+extern ira_loop_tree_node_t ira_loop_tree_root;
+
+/* Height of the loop tree. */
+extern int ira_loop_tree_height;
+
+/* All nodes representing basic blocks are referred through the
+ following array. We can not use basic block member `aux' for this
+ because it is used for insertion of insns on edges. */
+extern ira_loop_tree_node_t ira_bb_nodes;
+
+/* Two access macros to the nodes representing basic blocks. */
+#if defined ENABLE_IRA_CHECKING && (GCC_VERSION >= 2007)
+#define IRA_BB_NODE_BY_INDEX(index) __extension__ \
+(({ ira_loop_tree_node_t _node = (&ira_bb_nodes[index]); \
+ if (_node->children != NULL || _node->loop != NULL || _node->bb == NULL)\
+ { \
+ fprintf (stderr, \
+ "\n%s: %d: error in %s: it is not a block node\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ gcc_unreachable (); \
+ } \
+ _node; }))
+#else
+#define IRA_BB_NODE_BY_INDEX(index) (&ira_bb_nodes[index])
+#endif
+
+#define IRA_BB_NODE(bb) IRA_BB_NODE_BY_INDEX ((bb)->index)
+
+/* All nodes representing loops are referred through the following
+ array. */
+extern ira_loop_tree_node_t ira_loop_nodes;
+
+/* Two access macros to the nodes representing loops. */
+#if defined ENABLE_IRA_CHECKING && (GCC_VERSION >= 2007)
+#define IRA_LOOP_NODE_BY_INDEX(index) __extension__ \
+(({ ira_loop_tree_node_t const _node = (&ira_loop_nodes[index]);\
+ if (_node->children == NULL || _node->bb != NULL || _node->loop == NULL)\
+ { \
+ fprintf (stderr, \
+ "\n%s: %d: error in %s: it is not a loop node\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ gcc_unreachable (); \
+ } \
+ _node; }))
+#else
+#define IRA_LOOP_NODE_BY_INDEX(index) (&ira_loop_nodes[index])
+#endif
+
+#define IRA_LOOP_NODE(loop) IRA_LOOP_NODE_BY_INDEX ((loop)->num)
+
+
+
+/* The structure describes program points where a given allocno lives.
+ To save memory we store allocno conflicts only for the same cover
+ class allocnos which is enough to assign hard registers. To find
+ conflicts for other allocnos (e.g. to assign stack memory slot) we
+ use the live ranges. If the live ranges of two allocnos are
+ intersected, the allocnos are in conflict. */
+struct ira_allocno_live_range
+{
+ /* Allocno whose live range is described by given structure. */
+ ira_allocno_t allocno;
+ /* Program point range. */
+ int start, finish;
+ /* Next structure describing program points where the allocno
+ lives. */
+ allocno_live_range_t next;
+ /* Pointer to structures with the same start/finish. */
+ allocno_live_range_t start_next, finish_next;
+};
+
+/* 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. */
+extern int ira_max_point;
+
+/* Arrays of size IRA_MAX_POINT mapping a program point to the allocno
+ live ranges with given start/finish point. */
+extern allocno_live_range_t *ira_start_point_ranges, *ira_finish_point_ranges;
+
+/* A structure representing an allocno (allocation entity). Allocno
+ represents a pseudo-register in an allocation region. If
+ pseudo-register does not live in a region but it lives in the
+ nested regions, it is represented in the region by special allocno
+ called *cap*. There may be more one cap representing the same
+ pseudo-register in region. It means that the corresponding
+ pseudo-register lives in more one non-intersected subregion. */
+struct ira_allocno
+{
+ /* The allocno order number starting with 0. Each allocno has an
+ unique number and the number is never changed for the
+ allocno. */
+ int num;
+ /* Regno for allocno or cap. */
+ int regno;
+ /* Mode of the allocno which is the mode of the corresponding
+ pseudo-register. */
+ enum machine_mode mode;
+ /* Final rtx representation of the allocno. */
+ rtx reg;
+ /* Hard register assigned to given allocno. Negative value means
+ that memory was allocated to the allocno. During the reload,
+ spilled allocno has value equal to the corresponding stack slot
+ number (0, ...) - 2. Value -1 is used for allocnos spilled by the
+ reload (at this point pseudo-register has only one allocno) which
+ did not get stack slot yet. */
+ int hard_regno;
+ /* Allocnos with the same regno are linked by the following member.
+ Allocnos corresponding to inner loops are first in the list (it
+ corresponds to depth-first traverse of the loops). */
+ ira_allocno_t next_regno_allocno;
+ /* There may be different allocnos with the same regno in different
+ regions. Allocnos are bound to the corresponding loop tree node.
+ Pseudo-register may have only one regular allocno with given loop
+ tree node but more than one cap (see comments above). */
+ ira_loop_tree_node_t loop_tree_node;
+ /* Accumulated usage references of the allocno. Here and below,
+ word 'accumulated' means info for given region and all nested
+ subregions. In this case, 'accumulated' means sum of references
+ of the corresponding pseudo-register in this region and in all
+ nested subregions recursively. */
+ int nrefs;
+ /* Accumulated frequency of usage of the allocno. */
+ int freq;
+ /* Register class which should be used for allocation for given
+ allocno. NO_REGS means that we should use memory. */
+ enum reg_class cover_class;
+ /* Minimal accumulated cost of usage register of the cover class for
+ the allocno. */
+ int cover_class_cost;
+ /* Minimal accumulated, and updated costs of memory for the allocno.
+ At the allocation start, the original and updated costs are
+ equal. The updated cost may be changed after finishing
+ allocation in a region and starting allocation in a subregion.
+ The change reflects the cost of spill/restore code on the
+ subregion border if we assign memory to the pseudo in the
+ subregion. */
+ int memory_cost, updated_memory_cost;
+ /* Accumulated number of points where the allocno lives and there is
+ excess pressure for its class. 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. */
+ int excess_pressure_points_num;
+ /* Copies to other non-conflicting allocnos. The copies can
+ represent move insn or potential move insn usually because of two
+ operand insn constraints. */
+ ira_copy_t allocno_copies;
+ /* It is a allocno (cap) representing given allocno on upper loop tree
+ level. */
+ ira_allocno_t cap;
+ /* It is a link to allocno (cap) on lower loop level represented by
+ given cap. Null if given allocno is not a cap. */
+ ira_allocno_t cap_member;
+ /* Coalesced allocnos form a cyclic list. One allocno given by
+ FIRST_COALESCED_ALLOCNO represents all coalesced allocnos. The
+ list is chained by NEXT_COALESCED_ALLOCNO. */
+ ira_allocno_t first_coalesced_allocno;
+ ira_allocno_t next_coalesced_allocno;
+ /* Pointer to structures describing at what program point the
+ allocno lives. We always maintain the list in such way that *the
+ ranges in the list are not intersected and ordered by decreasing
+ their program points*. */
+ allocno_live_range_t live_ranges;
+ /* Before building conflicts the two member values are
+ correspondingly minimal and maximal points of the accumulated
+ allocno live ranges. After building conflicts the values are
+ correspondingly minimal and maximal conflict ids of allocnos with
+ which given allocno can conflict. */
+ int min, max;
+ /* The unique member value represents given allocno in conflict bit
+ vectors. */
+ int conflict_id;
+ /* Vector of accumulated conflicting allocnos with NULL end marker
+ (if CONFLICT_VEC_P is true) or conflict bit vector otherwise.
+ Only allocnos with the same cover class are in the vector or in
+ the bit vector. */
+ void *conflict_allocno_array;
+ /* Allocated size of the previous array. */
+ unsigned int conflict_allocno_array_size;
+ /* Number of accumulated conflicts in the vector of conflicting
+ allocnos. */
+ int conflict_allocnos_num;
+ /* Initial and accumulated hard registers conflicting with this
+ allocno and as a consequences can not be assigned to the allocno.
+ All non-allocatable hard regs and hard regs of cover classes
+ different from given allocno one are included in the sets. */
+ HARD_REG_SET conflict_hard_regs, total_conflict_hard_regs;
+ /* Accumulated frequency of calls which given allocno
+ intersects. */
+ int call_freq;
+ /* Length of the previous array (number of the intersected calls). */
+ int calls_crossed_num;
+ /* Non NULL if we remove restoring value from given allocno to
+ MEM_OPTIMIZED_DEST at loop exit (see ira-emit.c) because the
+ allocno value is not changed inside the loop. */
+ ira_allocno_t mem_optimized_dest;
+ /* TRUE if the allocno assigned to memory was a destination of
+ removed move (see ira-emit.c) at loop exit because the value of
+ the corresponding pseudo-register is not changed inside the
+ loop. */
+ unsigned int mem_optimized_dest_p : 1;
+ /* TRUE if the corresponding pseudo-register has disjoint live
+ ranges and the other allocnos of the pseudo-register except this
+ one changed REG. */
+ unsigned int somewhere_renamed_p : 1;
+ /* TRUE if allocno with the same REGNO in a subregion has been
+ renamed, in other words, got a new pseudo-register. */
+ unsigned int child_renamed_p : 1;
+ /* During the reload, value TRUE means that we should not reassign a
+ hard register to the allocno got memory earlier. It is set up
+ when we removed memory-memory move insn before each iteration of
+ the reload. */
+ unsigned int dont_reassign_p : 1;
+#ifdef STACK_REGS
+ /* Set to TRUE if allocno can't be assigned to the stack hard
+ register correspondingly in this region and area including the
+ region and all its subregions recursively. */
+ unsigned int no_stack_reg_p : 1, total_no_stack_reg_p : 1;
+#endif
+ /* TRUE value means that the allocno was not removed yet from the
+ conflicting graph during colouring. */
+ unsigned int in_graph_p : 1;
+ /* TRUE if a hard register or memory has been assigned to the
+ allocno. */
+ unsigned int assigned_p : 1;
+ /* TRUE if it is put on the stack to make other allocnos
+ colorable. */
+ unsigned int may_be_spilled_p : 1;
+ /* TRUE if the allocno was removed from the splay tree used to
+ choose allocn for spilling (see ira-color.c::. */
+ unsigned int splay_removed_p : 1;
+ /* TRUE if conflicts for given allocno are represented by vector of
+ pointers to the conflicting allocnos. Otherwise, we use a bit
+ vector where a bit with given index represents allocno with the
+ same number. */
+ unsigned int conflict_vec_p : 1;
+ /* Array of usage costs (accumulated and the one updated during
+ coloring) for each hard register of the allocno cover class. The
+ member value can be NULL if all costs are the same and equal to
+ COVER_CLASS_COST. For example, the costs of two different hard
+ registers can be different if one hard register is callee-saved
+ and another one is callee-used and the allocno lives through
+ calls. Another example can be case when for some insn the
+ corresponding pseudo-register value should be put in specific
+ register class (e.g. AREG for x86) which is a strict subset of
+ the allocno cover class (GENERAL_REGS for x86). We have updated
+ costs to reflect the situation when the usage cost of a hard
+ register is decreased because the allocno is connected to another
+ allocno by a copy and the another allocno has been assigned to
+ the hard register. */
+ int *hard_reg_costs, *updated_hard_reg_costs;
+ /* Array of decreasing costs (accumulated and the one updated during
+ coloring) for allocnos conflicting with given allocno for hard
+ regno of the allocno cover class. The member value can be NULL
+ if all costs are the same. These costs are used to reflect
+ preferences of other allocnos not assigned yet during assigning
+ to given allocno. */
+ int *conflict_hard_reg_costs, *updated_conflict_hard_reg_costs;
+ /* Number of the same cover class allocnos with TRUE in_graph_p
+ value and conflicting with given allocno during each point of
+ graph coloring. */
+ int left_conflicts_num;
+ /* Number of hard registers of the allocno cover class really
+ available for the allocno allocation. */
+ int available_regs_num;
+ /* Allocnos in a bucket (used in coloring) chained by the following
+ two members. */
+ ira_allocno_t next_bucket_allocno;
+ ira_allocno_t prev_bucket_allocno;
+ /* Used for temporary purposes. */
+ int temp;
+};
+
+/* All members of the allocno structures should be accessed only
+ through the following macros. */
+#define ALLOCNO_NUM(A) ((A)->num)
+#define ALLOCNO_REGNO(A) ((A)->regno)
+#define ALLOCNO_REG(A) ((A)->reg)
+#define ALLOCNO_NEXT_REGNO_ALLOCNO(A) ((A)->next_regno_allocno)
+#define ALLOCNO_LOOP_TREE_NODE(A) ((A)->loop_tree_node)
+#define ALLOCNO_CAP(A) ((A)->cap)
+#define ALLOCNO_CAP_MEMBER(A) ((A)->cap_member)
+#define ALLOCNO_CONFLICT_ALLOCNO_ARRAY(A) ((A)->conflict_allocno_array)
+#define ALLOCNO_CONFLICT_ALLOCNO_ARRAY_SIZE(A) \
+ ((A)->conflict_allocno_array_size)
+#define ALLOCNO_CONFLICT_ALLOCNOS_NUM(A) \
+ ((A)->conflict_allocnos_num)
+#define ALLOCNO_CONFLICT_HARD_REGS(A) ((A)->conflict_hard_regs)
+#define ALLOCNO_TOTAL_CONFLICT_HARD_REGS(A) ((A)->total_conflict_hard_regs)
+#define ALLOCNO_NREFS(A) ((A)->nrefs)
+#define ALLOCNO_FREQ(A) ((A)->freq)
+#define ALLOCNO_HARD_REGNO(A) ((A)->hard_regno)
+#define ALLOCNO_CALL_FREQ(A) ((A)->call_freq)
+#define ALLOCNO_CALLS_CROSSED_NUM(A) ((A)->calls_crossed_num)
+#define ALLOCNO_MEM_OPTIMIZED_DEST(A) ((A)->mem_optimized_dest)
+#define ALLOCNO_MEM_OPTIMIZED_DEST_P(A) ((A)->mem_optimized_dest_p)
+#define ALLOCNO_SOMEWHERE_RENAMED_P(A) ((A)->somewhere_renamed_p)
+#define ALLOCNO_CHILD_RENAMED_P(A) ((A)->child_renamed_p)
+#define ALLOCNO_DONT_REASSIGN_P(A) ((A)->dont_reassign_p)
+#ifdef STACK_REGS
+#define ALLOCNO_NO_STACK_REG_P(A) ((A)->no_stack_reg_p)
+#define ALLOCNO_TOTAL_NO_STACK_REG_P(A) ((A)->total_no_stack_reg_p)
+#endif
+#define ALLOCNO_IN_GRAPH_P(A) ((A)->in_graph_p)
+#define ALLOCNO_ASSIGNED_P(A) ((A)->assigned_p)
+#define ALLOCNO_MAY_BE_SPILLED_P(A) ((A)->may_be_spilled_p)
+#define ALLOCNO_SPLAY_REMOVED_P(A) ((A)->splay_removed_p)
+#define ALLOCNO_CONFLICT_VEC_P(A) ((A)->conflict_vec_p)
+#define ALLOCNO_MODE(A) ((A)->mode)
+#define ALLOCNO_COPIES(A) ((A)->allocno_copies)
+#define ALLOCNO_HARD_REG_COSTS(A) ((A)->hard_reg_costs)
+#define ALLOCNO_UPDATED_HARD_REG_COSTS(A) ((A)->updated_hard_reg_costs)
+#define ALLOCNO_CONFLICT_HARD_REG_COSTS(A) \
+ ((A)->conflict_hard_reg_costs)
+#define ALLOCNO_UPDATED_CONFLICT_HARD_REG_COSTS(A) \
+ ((A)->updated_conflict_hard_reg_costs)
+#define ALLOCNO_LEFT_CONFLICTS_NUM(A) ((A)->left_conflicts_num)
+#define ALLOCNO_COVER_CLASS(A) ((A)->cover_class)
+#define ALLOCNO_COVER_CLASS_COST(A) ((A)->cover_class_cost)
+#define ALLOCNO_MEMORY_COST(A) ((A)->memory_cost)
+#define ALLOCNO_UPDATED_MEMORY_COST(A) ((A)->updated_memory_cost)
+#define ALLOCNO_EXCESS_PRESSURE_POINTS_NUM(A) ((A)->excess_pressure_points_num)
+#define ALLOCNO_AVAILABLE_REGS_NUM(A) ((A)->available_regs_num)
+#define ALLOCNO_NEXT_BUCKET_ALLOCNO(A) ((A)->next_bucket_allocno)
+#define ALLOCNO_PREV_BUCKET_ALLOCNO(A) ((A)->prev_bucket_allocno)
+#define IRA_ALLOCNO_TEMP(A) ((A)->temp)
+#define ALLOCNO_FIRST_COALESCED_ALLOCNO(A) ((A)->first_coalesced_allocno)
+#define ALLOCNO_NEXT_COALESCED_ALLOCNO(A) ((A)->next_coalesced_allocno)
+#define ALLOCNO_LIVE_RANGES(A) ((A)->live_ranges)
+#define ALLOCNO_MIN(A) ((A)->min)
+#define ALLOCNO_MAX(A) ((A)->max)
+#define ALLOCNO_CONFLICT_ID(A) ((A)->conflict_id)
+
+/* Map regno -> allocnos with given regno (see comments for
+ allocno member `next_regno_allocno'). */
+extern ira_allocno_t *ira_regno_allocno_map;
+
+/* Array of references to all allocnos. The order number of the
+ allocno corresponds to the index in the array. Removed allocnos
+ have NULL element value. */
+extern ira_allocno_t *ira_allocnos;
+
+/* Sizes of the previous array. */
+extern int ira_allocnos_num;
+
+/* Map conflict id -> allocno with given conflict id (see comments for
+ allocno member `conflict_id'). */
+extern ira_allocno_t *ira_conflict_id_allocno_map;
+
+/* The following structure represents a copy of two allocnos. The
+ copies represent move insns or potential move insns usually because
+ of two operand insn constraints. To remove register shuffle, we
+ also create copies between allocno which is output of an insn and
+ allocno becoming dead in the insn. */
+struct ira_allocno_copy
+{
+ /* The unique order number of the copy node starting with 0. */
+ int num;
+ /* Allocnos connected by the copy. The first allocno should have
+ smaller order number than the second one. */
+ ira_allocno_t first, second;
+ /* Execution frequency of the copy. */
+ int freq;
+ /* It is a move insn which is an origin of the copy. The member
+ value for the copy representing two operand insn constraints or
+ for the copy created to remove register shuffle is NULL. In last
+ case the copy frequency is smaller than the corresponding insn
+ execution frequency. */
+ rtx insn;
+ /* All copies with the same allocno as FIRST are linked by the two
+ following members. */
+ ira_copy_t prev_first_allocno_copy, next_first_allocno_copy;
+ /* All copies with the same allocno as SECOND are linked by the two
+ following members. */
+ ira_copy_t prev_second_allocno_copy, next_second_allocno_copy;
+ /* Region from which given copy is originated. */
+ ira_loop_tree_node_t loop_tree_node;
+};
+
+/* Array of references to all copies. The order number of the copy
+ corresponds to the index in the array. Removed copies have NULL
+ element value. */
+extern ira_copy_t *ira_copies;
+
+/* Size of the previous array. */
+extern int ira_copies_num;
+
+/* The following structure describes a stack slot used for spilled
+ pseudo-registers. */
+struct ira_spilled_reg_stack_slot
+{
+ /* pseudo-registers assigned to the stack slot. */
+ regset_head spilled_regs;
+ /* RTL representation of the stack slot. */
+ rtx mem;
+ /* Size of the stack slot. */
+ unsigned int width;
+};
+
+/* The number of elements in the following array. */
+extern int ira_spilled_reg_stack_slots_num;
+
+/* The following array contains info about spilled pseudo-registers
+ stack slots used in current function so far. */
+extern struct ira_spilled_reg_stack_slot *ira_spilled_reg_stack_slots;
+
+/* Correspondingly overall cost of the allocation, cost of the
+ allocnos assigned to hard-registers, cost of the allocnos assigned
+ to memory, cost of loads, stores and register move insns generated
+ for pseudo-register live range splitting (see ira-emit.c). */
+extern int ira_overall_cost;
+extern int ira_reg_cost, ira_mem_cost;
+extern int ira_load_cost, ira_store_cost, ira_shuffle_cost;
+extern int ira_move_loops_num, ira_additional_jumps_num;
+
+/* Map: register class x machine mode -> number of hard registers of
+ given class needed to store value of given mode. If the number for
+ some hard-registers of the register class is different, the size
+ will be negative. */
+extern int ira_reg_class_nregs[N_REG_CLASSES][MAX_MACHINE_MODE];
+
+/* Maximal value of the previous array elements. */
+extern int ira_max_nregs;
+
+/* The number of bits in each element of array used to implement a bit
+ vector of allocnos and what type that element has. We use the
+ largest integer format on the host machine. */
+#define IRA_INT_BITS HOST_BITS_PER_WIDE_INT
+#define IRA_INT_TYPE HOST_WIDE_INT
+
+/* Set, clear or test bit number I in R, a bit vector of elements with
+ minimal index and maximal index equal correspondingly to MIN and
+ MAX. */
+#if defined ENABLE_IRA_CHECKING && (GCC_VERSION >= 2007)
+
+#define SET_ALLOCNO_SET_BIT(R, I, MIN, MAX) __extension__ \
+ (({ int _min = (MIN), _max = (MAX), _i = (I); \
+ if (_i < _min || _i > _max) \
+ { \
+ fprintf (stderr, \
+ "\n%s: %d: error in %s: %d not in range [%d,%d]\n", \
+ __FILE__, __LINE__, __FUNCTION__, _i, _min, _max); \
+ gcc_unreachable (); \
+ } \
+ ((R)[(unsigned) (_i - _min) / IRA_INT_BITS] \
+ |= ((IRA_INT_TYPE) 1 << ((unsigned) (_i - _min) % IRA_INT_BITS))); }))
+
+
+#define CLEAR_ALLOCNO_SET_BIT(R, I, MIN, MAX) __extension__ \
+ (({ int _min = (MIN), _max = (MAX), _i = (I); \
+ if (_i < _min || _i > _max) \
+ { \
+ fprintf (stderr, \
+ "\n%s: %d: error in %s: %d not in range [%d,%d]\n", \
+ __FILE__, __LINE__, __FUNCTION__, _i, _min, _max); \
+ gcc_unreachable (); \
+ } \
+ ((R)[(unsigned) (_i - _min) / IRA_INT_BITS] \
+ &= ~((IRA_INT_TYPE) 1 << ((unsigned) (_i - _min) % IRA_INT_BITS))); }))
+
+#define TEST_ALLOCNO_SET_BIT(R, I, MIN, MAX) __extension__ \
+ (({ int _min = (MIN), _max = (MAX), _i = (I); \
+ if (_i < _min || _i > _max) \
+ { \
+ fprintf (stderr, \
+ "\n%s: %d: error in %s: %d not in range [%d,%d]\n", \
+ __FILE__, __LINE__, __FUNCTION__, _i, _min, _max); \
+ gcc_unreachable (); \
+ } \
+ ((R)[(unsigned) (_i - _min) / IRA_INT_BITS] \
+ & ((IRA_INT_TYPE) 1 << ((unsigned) (_i - _min) % IRA_INT_BITS))); }))
+
+#else
+
+#define SET_ALLOCNO_SET_BIT(R, I, MIN, MAX) \
+ ((R)[(unsigned) ((I) - (MIN)) / IRA_INT_BITS] \
+ |= ((IRA_INT_TYPE) 1 << ((unsigned) ((I) - (MIN)) % IRA_INT_BITS)))
+
+#define CLEAR_ALLOCNO_SET_BIT(R, I, MIN, MAX) \
+ ((R)[(unsigned) ((I) - (MIN)) / IRA_INT_BITS] \
+ &= ~((IRA_INT_TYPE) 1 << ((unsigned) ((I) - (MIN)) % IRA_INT_BITS)))
+
+#define TEST_ALLOCNO_SET_BIT(R, I, MIN, MAX) \
+ ((R)[(unsigned) ((I) - (MIN)) / IRA_INT_BITS] \
+ & ((IRA_INT_TYPE) 1 << ((unsigned) ((I) - (MIN)) % IRA_INT_BITS)))
+
+#endif
+
+/* The iterator for allocno set implemented ed as allocno bit
+ vector. */
+typedef struct {
+
+ /* Array containing the allocno bit vector. */
+ IRA_INT_TYPE *vec;
+
+ /* The number of the current element in the vector. */
+ unsigned int word_num;
+
+ /* The number of bits in the bit vector. */
+ unsigned int nel;
+
+ /* The current bit index of the bit vector. */
+ unsigned int bit_num;
+
+ /* Index corresponding to the 1st bit of the bit vector. */
+ int start_val;
+
+ /* The word of the bit vector currently visited. */
+ unsigned IRA_INT_TYPE word;
+} ira_allocno_set_iterator;
+
+/* Initialize the iterator I for allocnos bit vector VEC containing
+ minimal and maximal values MIN and MAX. */
+static inline void
+ira_allocno_set_iter_init (ira_allocno_set_iterator *i,
+ IRA_INT_TYPE *vec, int min, int max)
+{
+ i->vec = vec;
+ i->word_num = 0;
+ i->nel = max < min ? 0 : max - min + 1;
+ i->start_val = min;
+ i->bit_num = 0;
+ i->word = i->nel == 0 ? 0 : vec[0];
+}
+
+/* Return TRUE if we have more allocnos to visit, in which case *N is
+ set to the allocno number to be visited. Otherwise, return
+ FALSE. */
+static inline bool
+ira_allocno_set_iter_cond (ira_allocno_set_iterator *i, int *n)
+{
+ /* Skip words that are zeros. */
+ for (; i->word == 0; i->word = i->vec[i->word_num])
+ {
+ i->word_num++;
+ i->bit_num = i->word_num * IRA_INT_BITS;
+
+ /* If we have reached the end, break. */
+ if (i->bit_num >= i->nel)
+ return false;
+ }
+
+ /* Skip bits that are zero. */
+ for (; (i->word & 1) == 0; i->word >>= 1)
+ i->bit_num++;
+
+ *n = (int) i->bit_num + i->start_val;
+
+ return true;
+}
+
+/* Advance to the next allocno in the set. */
+static inline void
+ira_allocno_set_iter_next (ira_allocno_set_iterator *i)
+{
+ i->word >>= 1;
+ i->bit_num++;
+}
+
+/* Loop over all elements of allocno set given by bit vector VEC and
+ their minimal and maximal values MIN and MAX. In each iteration, N
+ is set to the number of next allocno. ITER is an instance of
+ ira_allocno_set_iterator used to iterate the allocnos in the set. */
+#define FOR_EACH_ALLOCNO_IN_SET(VEC, MIN, MAX, N, ITER) \
+ for (ira_allocno_set_iter_init (&(ITER), (VEC), (MIN), (MAX)); \
+ ira_allocno_set_iter_cond (&(ITER), &(N)); \
+ ira_allocno_set_iter_next (&(ITER)))
+
+/* ira.c: */
+
+/* Hard regsets whose all bits are correspondingly zero or one. */
+extern HARD_REG_SET ira_zero_hard_reg_set;
+extern HARD_REG_SET ira_one_hard_reg_set;
+
+/* Map: hard regs X modes -> set of hard registers for storing value
+ of given mode starting with given hard register. */
+extern HARD_REG_SET ira_reg_mode_hard_regset
+ [FIRST_PSEUDO_REGISTER][NUM_MACHINE_MODES];
+
+/* Arrays analogous to macros MEMORY_MOVE_COST and
+ REGISTER_MOVE_COST. */
+extern short ira_memory_move_cost[MAX_MACHINE_MODE][N_REG_CLASSES][2];
+extern move_table *ira_register_move_cost[MAX_MACHINE_MODE];
+
+/* Similar to may_move_in_cost but it is calculated in IRA instead of
+ regclass. Another difference we take only available hard registers
+ into account to figure out that one register class is a subset of
+ the another one. */
+extern move_table *ira_may_move_in_cost[MAX_MACHINE_MODE];
+
+/* Similar to may_move_out_cost but it is calculated in IRA instead of
+ regclass. Another difference we take only available hard registers
+ into account to figure out that one register class is a subset of
+ the another one. */
+extern move_table *ira_may_move_out_cost[MAX_MACHINE_MODE];
+
+/* Register class subset relation: TRUE if the first class is a subset
+ of the second one considering only hard registers available for the
+ allocation. */
+extern int ira_class_subset_p[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Array of number of hard registers of given class which are
+ available for the allocation. The order is defined by the
+ allocation order. */
+extern short ira_class_hard_regs[N_REG_CLASSES][FIRST_PSEUDO_REGISTER];
+
+/* The number of elements of the above array for given register
+ class. */
+extern int ira_class_hard_regs_num[N_REG_CLASSES];
+
+/* Index (in ira_class_hard_regs) for given register class and hard
+ register (in general case a hard register can belong to several
+ register classes). The index is negative for hard registers
+ unavailable for the allocation. */
+extern short ira_class_hard_reg_index[N_REG_CLASSES][FIRST_PSEUDO_REGISTER];
+
+/* Function specific hard registers can not be used for the register
+ allocation. */
+extern HARD_REG_SET ira_no_alloc_regs;
+
+/* Number of given class hard registers available for the register
+ allocation for given classes. */
+extern int ira_available_class_regs[N_REG_CLASSES];
+
+/* Array whose values are hard regset of hard registers available for
+ the allocation of given register class whose HARD_REGNO_MODE_OK
+ values for given mode are zero. */
+extern HARD_REG_SET prohibited_class_mode_regs
+ [N_REG_CLASSES][NUM_MACHINE_MODES];
+
+/* Array whose values are hard regset of hard registers for which
+ move of the hard register in given mode into itself is
+ prohibited. */
+extern HARD_REG_SET ira_prohibited_mode_move_regs[NUM_MACHINE_MODES];
+
+/* Number of cover classes. Cover classes is non-intersected register
+ classes containing all hard-registers available for the
+ allocation. */
+extern int ira_reg_class_cover_size;
+
+/* The array containing cover classes (see also comments for macro
+ IRA_COVER_CLASSES). Only first IRA_REG_CLASS_COVER_SIZE elements are
+ used for this. */
+extern enum reg_class ira_reg_class_cover[N_REG_CLASSES];
+
+/* The value is number of elements in the subsequent array. */
+extern int ira_important_classes_num;
+
+/* The array containing non-empty classes (including non-empty cover
+ classes) which are subclasses of cover classes. Such classes is
+ important for calculation of the hard register usage costs. */
+extern enum reg_class ira_important_classes[N_REG_CLASSES];
+
+/* The array containing indexes of important classes in the previous
+ array. The array elements are defined only for important
+ classes. */
+extern int ira_important_class_nums[N_REG_CLASSES];
+
+/* Map of all register classes to corresponding cover class containing
+ the given class. If given class is not a subset of a cover class,
+ we translate it into the cheapest cover class. */
+extern enum reg_class ira_class_translate[N_REG_CLASSES];
+
+/* The biggest important class inside of intersection of the two
+ classes (that is calculated taking only hard registers available
+ for allocation into account). If the both classes contain no hard
+ registers available for allocation, the value is calculated with
+ taking all hard-registers including fixed ones into account. */
+extern enum reg_class ira_reg_class_intersect[N_REG_CLASSES][N_REG_CLASSES];
+
+/* The biggest important class inside of union of the two classes
+ (that is calculated taking only hard registers available for
+ allocation into account). If the both classes contain no hard
+ registers available for allocation, the value is calculated with
+ taking all hard-registers including fixed ones into account. In
+ other words, the value is the corresponding reg_class_subunion
+ value. */
+extern enum reg_class ira_reg_class_union[N_REG_CLASSES][N_REG_CLASSES];
+
+extern void *ira_allocate (size_t);
+extern void *ira_reallocate (void *, size_t);
+extern void ira_free (void *addr);
+extern bitmap ira_allocate_bitmap (void);
+extern void ira_free_bitmap (bitmap);
+extern void ira_print_disposition (FILE *);
+extern void ira_debug_disposition (void);
+extern void ira_debug_class_cover (void);
+extern void ira_init_register_move_cost (enum machine_mode);
+
+/* The length of the two following arrays. */
+extern int ira_reg_equiv_len;
+
+/* The element value is TRUE if the corresponding regno value is
+ invariant. */
+extern bool *ira_reg_equiv_invariant_p;
+
+/* The element value is equiv constant of given pseudo-register or
+ NULL_RTX. */
+extern rtx *ira_reg_equiv_const;
+
+/* ira-build.c */
+
+/* The current loop tree node and its regno allocno map. */
+extern ira_loop_tree_node_t ira_curr_loop_tree_node;
+extern ira_allocno_t *ira_curr_regno_allocno_map;
+
+extern void ira_debug_allocno_copies (ira_allocno_t);
+
+extern void ira_traverse_loop_tree (bool, ira_loop_tree_node_t,
+ void (*) (ira_loop_tree_node_t),
+ void (*) (ira_loop_tree_node_t));
+extern ira_allocno_t ira_create_allocno (int, bool, ira_loop_tree_node_t);
+extern void ira_set_allocno_cover_class (ira_allocno_t, enum reg_class);
+extern bool ira_conflict_vector_profitable_p (ira_allocno_t, int);
+extern void ira_allocate_allocno_conflict_vec (ira_allocno_t, int);
+extern void ira_allocate_allocno_conflicts (ira_allocno_t, int);
+extern void ira_add_allocno_conflict (ira_allocno_t, ira_allocno_t);
+extern void ira_print_expanded_allocno (ira_allocno_t);
+extern allocno_live_range_t ira_create_allocno_live_range
+ (ira_allocno_t, int, int, allocno_live_range_t);
+extern void ira_finish_allocno_live_range (allocno_live_range_t);
+extern void ira_free_allocno_updated_costs (ira_allocno_t);
+extern ira_copy_t ira_create_copy (ira_allocno_t, ira_allocno_t,
+ int, rtx, ira_loop_tree_node_t);
+extern void ira_add_allocno_copy_to_list (ira_copy_t);
+extern void ira_swap_allocno_copy_ends_if_necessary (ira_copy_t);
+extern void ira_remove_allocno_copy_from_list (ira_copy_t);
+extern ira_copy_t ira_add_allocno_copy (ira_allocno_t, ira_allocno_t, int, rtx,
+ ira_loop_tree_node_t);
+
+extern int *ira_allocate_cost_vector (enum reg_class);
+extern void ira_free_cost_vector (int *, enum reg_class);
+
+extern void ira_flattening (int, int);
+extern bool ira_build (bool);
+extern void ira_destroy (void);
+
+/* ira-costs.c */
+extern void ira_init_costs_once (void);
+extern void ira_init_costs (void);
+extern void ira_finish_costs_once (void);
+extern void ira_costs (void);
+extern void ira_tune_allocno_costs_and_cover_classes (void);
+
+/* ira-lives.c */
+
+extern void ira_rebuild_start_finish_chains (void);
+extern void ira_print_live_range_list (FILE *, allocno_live_range_t);
+extern void ira_debug_live_range_list (allocno_live_range_t);
+extern void ira_debug_allocno_live_ranges (ira_allocno_t);
+extern void ira_debug_live_ranges (void);
+extern void ira_create_allocno_live_ranges (void);
+extern void ira_finish_allocno_live_ranges (void);
+
+/* ira-conflicts.c */
+extern bool ira_allocno_live_ranges_intersect_p (ira_allocno_t, ira_allocno_t);
+extern bool ira_pseudo_live_ranges_intersect_p (int, int);
+extern void ira_debug_conflicts (bool);
+extern void ira_build_conflicts (void);
+
+/* ira-color.c */
+extern int ira_loop_edge_freq (ira_loop_tree_node_t, int, bool);
+extern void ira_reassign_conflict_allocnos (int);
+extern void ira_initiate_assign (void);
+extern void ira_finish_assign (void);
+extern void ira_color (void);
+extern void ira_fast_allocation (void);
+
+/* ira-emit.c */
+extern void ira_emit (bool);
+
+
+
+/* The iterator for all allocnos. */
+typedef struct {
+ /* The number of the current element in IRA_ALLOCNOS. */
+ int n;
+} ira_allocno_iterator;
+
+/* Initialize the iterator I. */
+static inline void
+ira_allocno_iter_init (ira_allocno_iterator *i)
+{
+ i->n = 0;
+}
+
+/* Return TRUE if we have more allocnos to visit, in which case *A is
+ set to the allocno to be visited. Otherwise, return FALSE. */
+static inline bool
+ira_allocno_iter_cond (ira_allocno_iterator *i, ira_allocno_t *a)
+{
+ int n;
+
+ for (n = i->n; n < ira_allocnos_num; n++)
+ if (ira_allocnos[n] != NULL)
+ {
+ *a = ira_allocnos[n];
+ i->n = n + 1;
+ return true;
+ }
+ return false;
+}
+
+/* Loop over all allocnos. In each iteration, A is set to the next
+ allocno. ITER is an instance of ira_allocno_iterator used to iterate
+ the allocnos. */
+#define FOR_EACH_ALLOCNO(A, ITER) \
+ for (ira_allocno_iter_init (&(ITER)); \
+ ira_allocno_iter_cond (&(ITER), &(A));)
+
+
+
+
+/* The iterator for copies. */
+typedef struct {
+ /* The number of the current element in IRA_COPIES. */
+ int n;
+} ira_copy_iterator;
+
+/* Initialize the iterator I. */
+static inline void
+ira_copy_iter_init (ira_copy_iterator *i)
+{
+ i->n = 0;
+}
+
+/* Return TRUE if we have more copies to visit, in which case *CP is
+ set to the copy to be visited. Otherwise, return FALSE. */
+static inline bool
+ira_copy_iter_cond (ira_copy_iterator *i, ira_copy_t *cp)
+{
+ int n;
+
+ for (n = i->n; n < ira_copies_num; n++)
+ if (ira_copies[n] != NULL)
+ {
+ *cp = ira_copies[n];
+ i->n = n + 1;
+ return true;
+ }
+ return false;
+}
+
+/* Loop over all copies. In each iteration, C is set to the next
+ copy. ITER is an instance of ira_copy_iterator used to iterate
+ the copies. */
+#define FOR_EACH_COPY(C, ITER) \
+ for (ira_copy_iter_init (&(ITER)); \
+ ira_copy_iter_cond (&(ITER), &(C));)
+
+
+
+
+/* The iterator for allocno conflicts. */
+typedef struct {
+
+ /* TRUE if the conflicts are represented by vector of allocnos. */
+ bool allocno_conflict_vec_p;
+
+ /* The conflict vector or conflict bit vector. */
+ void *vec;
+
+ /* The number of the current element in the vector (of type
+ ira_allocno_t or IRA_INT_TYPE). */
+ unsigned int word_num;
+
+ /* The bit vector size. It is defined only if
+ ALLOCNO_CONFLICT_VEC_P is FALSE. */
+ unsigned int size;
+
+ /* The current bit index of bit vector. It is defined only if
+ ALLOCNO_CONFLICT_VEC_P is FALSE. */
+ unsigned int bit_num;
+
+ /* Allocno conflict id corresponding to the 1st bit of the bit
+ vector. It is defined only if ALLOCNO_CONFLICT_VEC_P is
+ FALSE. */
+ int base_conflict_id;
+
+ /* The word of bit vector currently visited. It is defined only if
+ ALLOCNO_CONFLICT_VEC_P is FALSE. */
+ unsigned IRA_INT_TYPE word;
+} ira_allocno_conflict_iterator;
+
+/* Initialize the iterator I with ALLOCNO conflicts. */
+static inline void
+ira_allocno_conflict_iter_init (ira_allocno_conflict_iterator *i,
+ ira_allocno_t allocno)
+{
+ i->allocno_conflict_vec_p = ALLOCNO_CONFLICT_VEC_P (allocno);
+ i->vec = ALLOCNO_CONFLICT_ALLOCNO_ARRAY (allocno);
+ i->word_num = 0;
+ if (i->allocno_conflict_vec_p)
+ i->size = i->bit_num = i->base_conflict_id = i->word = 0;
+ else
+ {
+ if (ALLOCNO_MIN (allocno) > ALLOCNO_MAX (allocno))
+ i->size = 0;
+ else
+ i->size = ((ALLOCNO_MAX (allocno) - ALLOCNO_MIN (allocno)
+ + IRA_INT_BITS)
+ / IRA_INT_BITS) * sizeof (IRA_INT_TYPE);
+ i->bit_num = 0;
+ i->base_conflict_id = ALLOCNO_MIN (allocno);
+ i->word = (i->size == 0 ? 0 : ((IRA_INT_TYPE *) i->vec)[0]);
+ }
+}
+
+/* Return TRUE if we have more conflicting allocnos to visit, in which
+ case *A is set to the allocno to be visited. Otherwise, return
+ FALSE. */
+static inline bool
+ira_allocno_conflict_iter_cond (ira_allocno_conflict_iterator *i,
+ ira_allocno_t *a)
+{
+ ira_allocno_t conflict_allocno;
+
+ if (i->allocno_conflict_vec_p)
+ {
+ conflict_allocno = ((ira_allocno_t *) i->vec)[i->word_num];
+ if (conflict_allocno == NULL)
+ return false;
+ *a = conflict_allocno;
+ return true;
+ }
+ else
+ {
+ /* Skip words that are zeros. */
+ for (; i->word == 0; i->word = ((IRA_INT_TYPE *) i->vec)[i->word_num])
+ {
+ i->word_num++;
+
+ /* If we have reached the end, break. */
+ if (i->word_num * sizeof (IRA_INT_TYPE) >= i->size)
+ return false;
+
+ i->bit_num = i->word_num * IRA_INT_BITS;
+ }
+
+ /* Skip bits that are zero. */
+ for (; (i->word & 1) == 0; i->word >>= 1)
+ i->bit_num++;
+
+ *a = ira_conflict_id_allocno_map[i->bit_num + i->base_conflict_id];
+
+ return true;
+ }
+}
+
+/* Advance to the next conflicting allocno. */
+static inline void
+ira_allocno_conflict_iter_next (ira_allocno_conflict_iterator *i)
+{
+ if (i->allocno_conflict_vec_p)
+ i->word_num++;
+ else
+ {
+ i->word >>= 1;
+ i->bit_num++;
+ }
+}
+
+/* Loop over all allocnos conflicting with ALLOCNO. In each
+ iteration, A is set to the next conflicting allocno. ITER is an
+ instance of ira_allocno_conflict_iterator used to iterate the
+ conflicts. */
+#define FOR_EACH_ALLOCNO_CONFLICT(ALLOCNO, A, ITER) \
+ for (ira_allocno_conflict_iter_init (&(ITER), (ALLOCNO)); \
+ ira_allocno_conflict_iter_cond (&(ITER), &(A)); \
+ ira_allocno_conflict_iter_next (&(ITER)))
+
+
+
+/* The function returns TRUE if hard registers starting with
+ HARD_REGNO and containing value of MODE are not in set
+ HARD_REGSET. */
+static inline bool
+ira_hard_reg_not_in_set_p (int hard_regno, enum machine_mode mode,
+ HARD_REG_SET hard_regset)
+{
+ int i;
+
+ ira_assert (hard_regno >= 0);
+ for (i = hard_regno_nregs[hard_regno][mode] - 1; i >= 0; i--)
+ if (TEST_HARD_REG_BIT (hard_regset, hard_regno + i))
+ return false;
+ return true;
+}
+
+
+
+/* To save memory we use a lazy approach for allocation and
+ initialization of the cost vectors. We do this only when it is
+ really necessary. */
+
+/* Allocate cost vector *VEC for hard registers of COVER_CLASS and
+ initialize the elements by VAL if it is necessary */
+static inline void
+ira_allocate_and_set_costs (int **vec, enum reg_class cover_class, int val)
+{
+ int i, *reg_costs;
+ int len;
+
+ if (*vec != NULL)
+ return;
+ *vec = reg_costs = ira_allocate_cost_vector (cover_class);
+ len = ira_class_hard_regs_num[cover_class];
+ for (i = 0; i < len; i++)
+ reg_costs[i] = val;
+}
+
+/* Allocate cost vector *VEC for hard registers of COVER_CLASS and
+ copy values of vector SRC into the vector if it is necessary */
+static inline void
+ira_allocate_and_copy_costs (int **vec, enum reg_class cover_class, int *src)
+{
+ int len;
+
+ if (*vec != NULL || src == NULL)
+ return;
+ *vec = ira_allocate_cost_vector (cover_class);
+ len = ira_class_hard_regs_num[cover_class];
+ memcpy (*vec, src, sizeof (int) * len);
+}
+
+/* Allocate cost vector *VEC for hard registers of COVER_CLASS and
+ add values of vector SRC into the vector if it is necessary */
+static inline void
+ira_allocate_and_accumulate_costs (int **vec, enum reg_class cover_class,
+ int *src)
+{
+ int i, len;
+
+ if (src == NULL)
+ return;
+ len = ira_class_hard_regs_num[cover_class];
+ if (*vec == NULL)
+ {
+ *vec = ira_allocate_cost_vector (cover_class);
+ memset (*vec, 0, sizeof (int) * len);
+ }
+ for (i = 0; i < len; i++)
+ (*vec)[i] += src[i];
+}
+
+/* Allocate cost vector *VEC for hard registers of COVER_CLASS and
+ copy values of vector SRC into the vector or initialize it by VAL
+ (if SRC is null). */
+static inline void
+ira_allocate_and_set_or_copy_costs (int **vec, enum reg_class cover_class,
+ int val, int *src)
+{
+ int i, *reg_costs;
+ int len;
+
+ if (*vec != NULL)
+ return;
+ *vec = reg_costs = ira_allocate_cost_vector (cover_class);
+ len = ira_class_hard_regs_num[cover_class];
+ if (src != NULL)
+ memcpy (reg_costs, src, sizeof (int) * len);
+ else
+ {
+ for (i = 0; i < len; i++)
+ reg_costs[i] = val;
+ }
+}
diff --git a/gcc/ira-lives.c b/gcc/ira-lives.c
new file mode 100644
index 0000000..7d6b29f
--- /dev/null
+++ b/gcc/ira-lives.c
@@ -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);
+}
diff --git a/gcc/ira.c b/gcc/ira.c
new file mode 100644
index 0000000..c98f0a05
--- /dev/null
+++ b/gcc/ira.c
@@ -0,0 +1,2064 @@
+/* Integrated Register Allocator (IRA) entry point.
+ 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/>. */
+
+/* The integrated register allocator (IRA) is a
+ regional register allocator performing graph coloring on a top-down
+ traversal of nested regions. Graph coloring in a region is based
+ on Chaitin-Briggs algorithm. It is called integrated because
+ register coalescing, register live range splitting, and choosing a
+ better hard register are done on-the-fly during coloring. Register
+ coalescing and choosing a cheaper hard register is done by hard
+ register preferencing during hard register assigning. The live
+ range splitting is a byproduct of the regional register allocation.
+
+ Major IRA notions are:
+
+ o *Region* is a part of CFG where graph coloring based on
+ Chaitin-Briggs algorithm is done. IRA can work on any set of
+ nested CFG regions forming a tree. Currently the regions are
+ the entire function for the root region and natural loops for
+ the other regions. Therefore data structure representing a
+ region is called loop_tree_node.
+
+ o *Cover class* is a register class belonging to a set of
+ non-intersecting register classes containing all of the
+ hard-registers available for register allocation. The set of
+ all cover classes for a target is defined in the corresponding
+ machine-description file according some criteria. Such notion
+ is needed because Chaitin-Briggs algorithm works on
+ non-intersected register classes.
+
+ o *Allocno* represents the live range of a pseudo-register in a
+ region. Besides the obvious attributes like the corresponding
+ pseudo-register number, cover class, conflicting allocnos and
+ conflicting hard-registers, there are a few allocno attributes
+ which are important for understanding the allocation algorithm:
+
+ - *Live ranges*. This is a list of ranges of *program
+ points* where the allocno lives. Program points represent
+ places where a pseudo can be born or become dead (there are
+ approximately two times more program points than the insns)
+ and they are represented by integers starting with 0. The
+ live ranges are used to find conflicts between allocnos of
+ different cover classes. They also play very important role
+ for the transformation of the IRA internal representation of
+ several regions into a one region representation. The later is
+ used during the reload pass work because each allocno
+ represents all of the corresponding pseudo-registers.
+
+ - *Hard-register costs*. This is a vector of size equal to the
+ number of available hard-registers of the allocno's cover
+ class. The cost of a callee-clobbered hard-register for an
+ allocno is increased by the cost of save/restore code around
+ the calls through the given allocno's life. If the allocno
+ is a move instruction operand and another operand is a
+ hard-register of the allocno's cover class, the cost of the
+ hard-register is decreased by the move cost.
+
+ When an allocno is assigned, the hard-register with minimal
+ full cost is used. Initially, a hard-register's full cost is
+ the corresponding value from the hard-register's cost vector.
+ If the allocno is connected by a *copy* (see below) to
+ another allocno which has just received a hard-register, the
+ cost of the hard-register is decreased. Before choosing a
+ hard-register for an allocno, the allocno's current costs of
+ the hard-registers are modified by the conflict hard-register
+ costs of all of the conflicting allocnos which are not
+ assigned yet.
+
+ - *Conflict hard-register costs*. This is a vector of the same
+ size as the hard-register costs vector. To permit an
+ unassigned allocno to get a better hard-register, IRA uses
+ this vector to calculate the final full cost of the
+ available hard-registers. Conflict hard-register costs of an
+ unassigned allocno are also changed with a change of the
+ hard-register cost of the allocno when a copy involving the
+ allocno is processed as described above. This is done to
+ show other unassigned allocnos that a given allocno prefers
+ some hard-registers in order to remove the move instruction
+ corresponding to the copy.
+
+ o *Cap*. If a pseudo-register does not live in a region but
+ lives in a nested region, IRA creates a special allocno called
+ a cap in the outer region. A region cap is also created for a
+ subregion cap.
+
+ o *Copy*. Allocnos can be connected by copies. Copies are used
+ to modify hard-register costs for allocnos during coloring.
+ Such modifications reflects a preference to use the same
+ hard-register for the allocnos connected by copies. Usually
+ copies are created for move insns (in this case it results in
+ register coalescing). But IRA also creates copies for operands
+ of an insn which should be assigned to the same hard-register
+ due to constraints in the machine description (it usually
+ results in removing a move generated in reload to satisfy
+ the constraints) and copies referring to the allocno which is
+ the output operand of an instruction and the allocno which is
+ an input operand dying in the instruction (creation of such
+ copies results in less register shuffling). IRA *does not*
+ create copies between the same register allocnos from different
+ regions because we use another technique for propagating
+ hard-register preference on the borders of regions.
+
+ Allocnos (including caps) for the upper region in the region tree
+ *accumulate* information important for coloring from allocnos with
+ the same pseudo-register from nested regions. This includes
+ hard-register and memory costs, conflicts with hard-registers,
+ allocno conflicts, allocno copies and more. *Thus, attributes for
+ allocnos in a region have the same values as if the region had no
+ subregions*. It means that attributes for allocnos in the
+ outermost region corresponding to the function have the same values
+ as though the allocation used only one region which is the entire
+ function. It also means that we can look at IRA work as if the
+ first IRA did allocation for all function then it improved the
+ allocation for loops then their subloops and so on.
+
+ IRA major passes are:
+
+ o Building IRA internal representation which consists of the
+ following subpasses:
+
+ * First, IRA builds regions and creates allocnos (file
+ ira-build.c) and initializes most of their attributes.
+
+ * Then IRA finds a cover class for each allocno and calculates
+ its initial (non-accumulated) cost of memory and each
+ hard-register of its cover class (file ira-cost.c).
+
+ * IRA creates live ranges of each allocno, calulates register
+ pressure for each cover class in each region, sets up
+ conflict hard registers for each allocno and info about calls
+ the allocno lives through (file ira-lives.c).
+
+ * IRA removes low register pressure loops from the regions
+ mostly to speed IRA up (file ira-build.c).
+
+ * IRA propagates accumulated allocno info from lower region
+ allocnos to corresponding upper region allocnos (file
+ ira-build.c).
+
+ * IRA creates all caps (file ira-build.c).
+
+ * Having live-ranges of allocnos and their cover classes, IRA
+ creates conflicting allocnos of the same cover class for each
+ allocno. Conflicting allocnos are stored as a bit vector or
+ array of pointers to the conflicting allocnos whatever is
+ more profitable (file ira-conflicts.c). At this point IRA
+ creates allocno copies.
+
+ o Coloring. Now IRA has all necessary info to start graph coloring
+ process. It is done in each region on top-down traverse of the
+ region tree (file ira-color.c). There are following subpasses:
+
+ * Optional aggressive coalescing of allocnos in the region.
+
+ * Putting allocnos onto the coloring stack. IRA uses Briggs
+ optimistic coloring which is a major improvement over
+ Chaitin's coloring. Therefore IRA does not spill allocnos at
+ this point. There is some freedom in the order of putting
+ allocnos on the stack which can affect the final result of
+ the allocation. IRA uses some heuristics to improve the order.
+
+ * Popping the allocnos from the stack and assigning them hard
+ registers. If IRA can not assign a hard register to an
+ allocno and the allocno is coalesced, IRA undoes the
+ coalescing and puts the uncoalesced allocnos onto the stack in
+ the hope that some such allocnos will get a hard register
+ separately. If IRA fails to assign hard register or memory
+ is more profitable for it, IRA spills the allocno. IRA
+ assigns the allocno the hard-register with minimal full
+ allocation cost which reflects the cost of usage of the
+ hard-register for the allocno and cost of usage of the
+ hard-register for allocnos conflicting with given allocno.
+
+ * After allono assigning in the region, IRA modifies the hard
+ register and memory costs for the corresponding allocnos in
+ the subregions to reflect the cost of possible loads, stores,
+ or moves on the border of the region and its subregions.
+ When default regional allocation algorithm is used
+ (-fira-algorithm=mixed), IRA just propagates the assignment
+ for allocnos if the register pressure in the region for the
+ corresponding cover class is less than number of available
+ hard registers for given cover class.
+
+ o Spill/restore code moving. When IRA performs an allocation
+ by traversing regions in top-down order, it does not know what
+ happens below in the region tree. Therefore, sometimes IRA
+ misses opportunities to perform a better allocation. A simple
+ optimization tries to improve allocation in a region having
+ subregions and containing in another region. If the
+ corresponding allocnos in the subregion are spilled, it spills
+ the region allocno if it is profitable. The optimization
+ implements a simple iterative algorithm performing profitable
+ transformations while they are still possible. It is fast in
+ practice, so there is no real need for a better time complexity
+ algorithm.
+
+ o Code change. After coloring, two allocnos representing the same
+ pseudo-register outside and inside a region respectively may be
+ assigned to different locations (hard-registers or memory). In
+ this case IRA creates and uses a new pseudo-register inside the
+ region and adds code to move allocno values on the region's
+ borders. This is done during top-down traversal of the regions
+ (file ira-emit.c). In some complicated cases IRA can create a
+ new allocno to move allocno values (e.g. when a swap of values
+ stored in two hard-registers is needed). At this stage, the
+ new allocno is marked as spilled. IRA still creates the
+ pseudo-register and the moves on the region borders even when
+ both allocnos were assigned to the same hard-register. If the
+ reload pass spills a pseudo-register for some reason, the
+ effect will be smaller because another allocno will still be in
+ the hard-register. In most cases, this is better then spilling
+ both allocnos. If reload does not change the allocation
+ for the two pseudo-registers, the trivial move will be removed
+ by post-reload optimizations. IRA does not generate moves for
+ allocnos assigned to the same hard register when the default
+ regional allocation algorithm is used and the register pressure
+ in the region for the corresponding allocno cover class is less
+ than number of available hard registers for given cover class.
+ IRA also does some optimizations to remove redundant stores and
+ to reduce code duplication on the region borders.
+
+ o Flattening internal representation. After changing code, IRA
+ transforms its internal representation for several regions into
+ one region representation (file ira-build.c). This process is
+ called IR flattening. Such process is more complicated than IR
+ rebuilding would be, but is much faster.
+
+ o After IR flattening, IRA tries to assign hard registers to all
+ spilled allocnos. This is impelemented by a simple and fast
+ priority coloring algorithm (see function
+ ira_reassign_conflict_allocnos::ira-color.c). Here new allocnos
+ created during the code change pass can be assigned to hard
+ registers.
+
+ o At the end IRA calls the reload pass. The reload pass
+ communicates with IRA through several functions in file
+ ira-color.c to improve its decisions in
+
+ * sharing stack slots for the spilled pseudos based on IRA info
+ about pseudo-register conflicts.
+
+ * reassigning hard-registers to all spilled pseudos at the end
+ of each reload iteration.
+
+ * choosing a better hard-register to spill based on IRA info
+ about pseudo-register live ranges and the register pressure
+ in places where the pseudo-register lives.
+
+ IRA uses a lot of data representing the target processors. These
+ data are initilized in file ira.c.
+
+ If function has no loops (or the loops are ignored when
+ -fira-algorithm=CB is used), we have classic Chaitin-Briggs
+ coloring (only instead of separate pass of coalescing, we use hard
+ register preferencing). In such case, IRA works much faster
+ because many things are not made (like IR flattening, the
+ spill/restore optimization, and the code change).
+
+ Literature is worth to read for better understanding the code:
+
+ o Preston Briggs, Keith D. Cooper, Linda Torczon. Improvements to
+ Graph Coloring Register Allocation.
+
+ o David Callahan, Brian Koblenz. Register allocation via
+ hierarchical graph coloring.
+
+ o Keith Cooper, Anshuman Dasgupta, Jason Eckhardt. Revisiting Graph
+ Coloring Register Allocation: A Study of the Chaitin-Briggs and
+ Callahan-Koblenz Algorithms.
+
+ o Guei-Yuan Lueh, Thomas Gross, and Ali-Reza Adl-Tabatabai. Global
+ Register Allocation Based on Graph Fusion.
+
+ o Vladimir Makarov. The Integrated Register Allocator for GCC.
+
+ o Vladimir Makarov. The top-down register allocator for irregular
+ register file architectures.
+
+*/
+
+
+#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 "obstack.h"
+#include "bitmap.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "expr.h"
+#include "recog.h"
+#include "params.h"
+#include "timevar.h"
+#include "tree-pass.h"
+#include "output.h"
+#include "reload.h"
+#include "errors.h"
+#include "integrate.h"
+#include "df.h"
+#include "ggc.h"
+#include "ira-int.h"
+
+
+/* A modified value of flag `-fira-verbose' used internally. */
+int internal_flag_ira_verbose;
+
+/* Dump file of the allocator if it is not NULL. */
+FILE *ira_dump_file;
+
+/* Pools for allocnos, copies, allocno live ranges. */
+alloc_pool allocno_pool, copy_pool, allocno_live_range_pool;
+
+/* The number of elements in the following array. */
+int ira_spilled_reg_stack_slots_num;
+
+/* The following array contains info about spilled pseudo-registers
+ stack slots used in current function so far. */
+struct ira_spilled_reg_stack_slot *ira_spilled_reg_stack_slots;
+
+/* Correspondingly overall cost of the allocation, cost of the
+ allocnos assigned to hard-registers, cost of the allocnos assigned
+ to memory, cost of loads, stores and register move insns generated
+ for pseudo-register live range splitting (see ira-emit.c). */
+int ira_overall_cost;
+int ira_reg_cost, ira_mem_cost;
+int ira_load_cost, ira_store_cost, ira_shuffle_cost;
+int ira_move_loops_num, ira_additional_jumps_num;
+
+/* Map: hard regs X modes -> set of hard registers for storing value
+ of given mode starting with given hard register. */
+HARD_REG_SET ira_reg_mode_hard_regset[FIRST_PSEUDO_REGISTER][NUM_MACHINE_MODES];
+
+/* The following two variables are array analogs of the macros
+ MEMORY_MOVE_COST and REGISTER_MOVE_COST. */
+short int ira_memory_move_cost[MAX_MACHINE_MODE][N_REG_CLASSES][2];
+move_table *ira_register_move_cost[MAX_MACHINE_MODE];
+
+/* Similar to may_move_in_cost but it is calculated in IRA instead of
+ regclass. Another difference is that we take only available hard
+ registers into account to figure out that one register class is a
+ subset of the another one. */
+move_table *ira_may_move_in_cost[MAX_MACHINE_MODE];
+
+/* Similar to may_move_out_cost but it is calculated in IRA instead of
+ regclass. Another difference is that we take only available hard
+ registers into account to figure out that one register class is a
+ subset of the another one. */
+move_table *ira_may_move_out_cost[MAX_MACHINE_MODE];
+
+/* Register class subset relation: TRUE if the first class is a subset
+ of the second one considering only hard registers available for the
+ allocation. */
+int ira_class_subset_p[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Temporary hard reg set used for a different calculation. */
+static HARD_REG_SET temp_hard_regset;
+
+
+
+/* The function sets up the map IRA_REG_MODE_HARD_REGSET. */
+static void
+setup_reg_mode_hard_regset (void)
+{
+ int i, m, hard_regno;
+
+ for (m = 0; m < NUM_MACHINE_MODES; m++)
+ for (hard_regno = 0; hard_regno < FIRST_PSEUDO_REGISTER; hard_regno++)
+ {
+ CLEAR_HARD_REG_SET (ira_reg_mode_hard_regset[hard_regno][m]);
+ for (i = hard_regno_nregs[hard_regno][m] - 1; i >= 0; i--)
+ if (hard_regno + i < FIRST_PSEUDO_REGISTER)
+ SET_HARD_REG_BIT (ira_reg_mode_hard_regset[hard_regno][m],
+ hard_regno + i);
+ }
+}
+
+
+
+/* Hard registers that can not be used for the register allocator for
+ all functions of the current compilation unit. */
+static HARD_REG_SET no_unit_alloc_regs;
+
+/* Array of the number of hard registers of given class which are
+ available for allocation. The order is defined by the
+ allocation order. */
+short ira_class_hard_regs[N_REG_CLASSES][FIRST_PSEUDO_REGISTER];
+
+/* The number of elements of the above array for given register
+ class. */
+int ira_class_hard_regs_num[N_REG_CLASSES];
+
+/* Index (in ira_class_hard_regs) for given register class and hard
+ register (in general case a hard register can belong to several
+ register classes). The index is negative for hard registers
+ unavailable for the allocation. */
+short ira_class_hard_reg_index[N_REG_CLASSES][FIRST_PSEUDO_REGISTER];
+
+/* The function sets up the three arrays declared above. */
+static void
+setup_class_hard_regs (void)
+{
+ int cl, i, hard_regno, n;
+ HARD_REG_SET processed_hard_reg_set;
+
+ ira_assert (SHRT_MAX >= FIRST_PSEUDO_REGISTER);
+ /* We could call ORDER_REGS_FOR_LOCAL_ALLOC here (it is usually
+ putting hard callee-used hard registers first). But our
+ heuristics work better. */
+ for (cl = (int) N_REG_CLASSES - 1; cl >= 0; cl--)
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ CLEAR_HARD_REG_SET (processed_hard_reg_set);
+ for (n = 0, i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+#ifdef REG_ALLOC_ORDER
+ hard_regno = reg_alloc_order[i];
+#else
+ hard_regno = i;
+#endif
+ if (TEST_HARD_REG_BIT (processed_hard_reg_set, hard_regno))
+ continue;
+ SET_HARD_REG_BIT (processed_hard_reg_set, hard_regno);
+ if (! TEST_HARD_REG_BIT (temp_hard_regset, hard_regno))
+ ira_class_hard_reg_index[cl][hard_regno] = -1;
+ else
+ {
+ ira_class_hard_reg_index[cl][hard_regno] = n;
+ ira_class_hard_regs[cl][n++] = hard_regno;
+ }
+ }
+ ira_class_hard_regs_num[cl] = n;
+ }
+}
+
+/* Number of given class hard registers available for the register
+ allocation for given classes. */
+int ira_available_class_regs[N_REG_CLASSES];
+
+/* Set up IRA_AVAILABLE_CLASS_REGS. */
+static void
+setup_available_class_regs (void)
+{
+ int i, j;
+
+ memset (ira_available_class_regs, 0, sizeof (ira_available_class_regs));
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[i]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (temp_hard_regset, j))
+ ira_available_class_regs[i]++;
+ }
+}
+
+/* Set up global variables defining info about hard registers for the
+ allocation. These depend on USE_HARD_FRAME_P whose TRUE value means
+ that we can use the hard frame pointer for the allocation. */
+static void
+setup_alloc_regs (bool use_hard_frame_p)
+{
+ COPY_HARD_REG_SET (no_unit_alloc_regs, fixed_reg_set);
+ if (! use_hard_frame_p)
+ SET_HARD_REG_BIT (no_unit_alloc_regs, HARD_FRAME_POINTER_REGNUM);
+ setup_class_hard_regs ();
+ setup_available_class_regs ();
+}
+
+
+
+/* Set up IRA_MEMORY_MOVE_COST, IRA_REGISTER_MOVE_COST. */
+static void
+setup_class_subset_and_memory_move_costs (void)
+{
+ int cl, cl2;
+ enum machine_mode mode;
+ HARD_REG_SET temp_hard_regset2;
+
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ ira_memory_move_cost[mode][NO_REGS][0]
+ = ira_memory_move_cost[mode][NO_REGS][1] = SHRT_MAX;
+ for (cl = (int) N_REG_CLASSES - 1; cl >= 0; cl--)
+ {
+ if (cl != (int) NO_REGS)
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ {
+ ira_memory_move_cost[mode][cl][0] = MEMORY_MOVE_COST (mode, cl, 0);
+ ira_memory_move_cost[mode][cl][1] = MEMORY_MOVE_COST (mode, cl, 1);
+ /* Costs for NO_REGS are used in cost calculation on the
+ 1st pass when the preferred register classes are not
+ known yet. In this case we take the best scenario. */
+ if (ira_memory_move_cost[mode][NO_REGS][0]
+ > ira_memory_move_cost[mode][cl][0])
+ ira_memory_move_cost[mode][NO_REGS][0]
+ = ira_memory_move_cost[mode][cl][0];
+ if (ira_memory_move_cost[mode][NO_REGS][1]
+ > ira_memory_move_cost[mode][cl][1])
+ ira_memory_move_cost[mode][NO_REGS][1]
+ = ira_memory_move_cost[mode][cl][1];
+ }
+ for (cl2 = (int) N_REG_CLASSES - 1; cl2 >= 0; cl2--)
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ COPY_HARD_REG_SET (temp_hard_regset2, reg_class_contents[cl2]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset2, no_unit_alloc_regs);
+ ira_class_subset_p[cl][cl2]
+ = hard_reg_set_subset_p (temp_hard_regset, temp_hard_regset2);
+ }
+ }
+}
+
+
+
+/* Define the following macro if allocation through malloc if
+ preferable. */
+#define IRA_NO_OBSTACK
+
+#ifndef IRA_NO_OBSTACK
+/* Obstack used for storing all dynamic data (except bitmaps) of the
+ IRA. */
+static struct obstack ira_obstack;
+#endif
+
+/* Obstack used for storing all bitmaps of the IRA. */
+static struct bitmap_obstack ira_bitmap_obstack;
+
+/* Allocate memory of size LEN for IRA data. */
+void *
+ira_allocate (size_t len)
+{
+ void *res;
+
+#ifndef IRA_NO_OBSTACK
+ res = obstack_alloc (&ira_obstack, len);
+#else
+ res = xmalloc (len);
+#endif
+ return res;
+}
+
+/* Reallocate memory PTR of size LEN for IRA data. */
+void *
+ira_reallocate (void *ptr, size_t len)
+{
+ void *res;
+
+#ifndef IRA_NO_OBSTACK
+ res = obstack_alloc (&ira_obstack, len);
+#else
+ res = xrealloc (ptr, len);
+#endif
+ return res;
+}
+
+/* Free memory ADDR allocated for IRA data. */
+void
+ira_free (void *addr ATTRIBUTE_UNUSED)
+{
+#ifndef IRA_NO_OBSTACK
+ /* do nothing */
+#else
+ free (addr);
+#endif
+}
+
+
+/* Allocate and returns bitmap for IRA. */
+bitmap
+ira_allocate_bitmap (void)
+{
+ return BITMAP_ALLOC (&ira_bitmap_obstack);
+}
+
+/* Free bitmap B allocated for IRA. */
+void
+ira_free_bitmap (bitmap b ATTRIBUTE_UNUSED)
+{
+ /* do nothing */
+}
+
+
+
+/* Output information about allocation of all allocnos (except for
+ caps) into file F. */
+void
+ira_print_disposition (FILE *f)
+{
+ int i, n, max_regno;
+ ira_allocno_t a;
+ basic_block bb;
+
+ fprintf (f, "Disposition:");
+ max_regno = max_reg_num ();
+ for (n = 0, i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ for (a = ira_regno_allocno_map[i];
+ a != NULL;
+ a = ALLOCNO_NEXT_REGNO_ALLOCNO (a))
+ {
+ if (n % 4 == 0)
+ fprintf (f, "\n");
+ n++;
+ fprintf (f, " %4d:r%-4d", ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
+ if ((bb = ALLOCNO_LOOP_TREE_NODE (a)->bb) != NULL)
+ fprintf (f, "b%-3d", bb->index);
+ else
+ fprintf (f, "l%-3d", ALLOCNO_LOOP_TREE_NODE (a)->loop->num);
+ if (ALLOCNO_HARD_REGNO (a) >= 0)
+ fprintf (f, " %3d", ALLOCNO_HARD_REGNO (a));
+ else
+ fprintf (f, " mem");
+ }
+ fprintf (f, "\n");
+}
+
+/* Outputs information about allocation of all allocnos into
+ stderr. */
+void
+ira_debug_disposition (void)
+{
+ ira_print_disposition (stderr);
+}
+
+
+
+/* For each reg class, table listing all the classes contained in it
+ (excluding the class itself. Non-allocatable registers are
+ excluded from the consideration). */
+static enum reg_class alloc_reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Initialize the table of subclasses of each reg class. */
+static void
+setup_reg_subclasses (void)
+{
+ int i, j;
+ HARD_REG_SET temp_hard_regset2;
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ for (j = 0; j < N_REG_CLASSES; j++)
+ alloc_reg_class_subclasses[i][j] = LIM_REG_CLASSES;
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ if (i == (int) NO_REGS)
+ continue;
+
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[i]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (hard_reg_set_equal_p (temp_hard_regset, ira_zero_hard_reg_set))
+ continue;
+ for (j = 0; j < N_REG_CLASSES; j++)
+ if (i != j)
+ {
+ enum reg_class *p;
+
+ COPY_HARD_REG_SET (temp_hard_regset2, reg_class_contents[j]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset2, no_unit_alloc_regs);
+ if (! hard_reg_set_subset_p (temp_hard_regset,
+ temp_hard_regset2))
+ continue;
+ p = &alloc_reg_class_subclasses[j][0];
+ while (*p != LIM_REG_CLASSES) p++;
+ *p = (enum reg_class) i;
+ }
+ }
+}
+
+
+
+/* Number of cover classes. Cover classes is non-intersected register
+ classes containing all hard-registers available for the
+ allocation. */
+int ira_reg_class_cover_size;
+
+/* The array containing cover classes (see also comments for macro
+ IRA_COVER_CLASSES). Only first IRA_REG_CLASS_COVER_SIZE elements are
+ used for this. */
+enum reg_class ira_reg_class_cover[N_REG_CLASSES];
+
+/* The number of elements in the subsequent array. */
+int ira_important_classes_num;
+
+/* The array containing non-empty classes (including non-empty cover
+ classes) which are subclasses of cover classes. Such classes is
+ important for calculation of the hard register usage costs. */
+enum reg_class ira_important_classes[N_REG_CLASSES];
+
+/* The array containing indexes of important classes in the previous
+ array. The array elements are defined only for important
+ classes. */
+int ira_important_class_nums[N_REG_CLASSES];
+
+#ifdef IRA_COVER_CLASSES
+
+/* Check IRA_COVER_CLASSES and sets the four global variables defined
+ above. */
+static void
+setup_cover_and_important_classes (void)
+{
+ int i, j;
+ enum reg_class cl;
+ static enum reg_class classes[] = IRA_COVER_CLASSES;
+ HARD_REG_SET temp_hard_regset2;
+
+ ira_reg_class_cover_size = 0;
+ for (i = 0; (cl = classes[i]) != LIM_REG_CLASSES; i++)
+ {
+ for (j = 0; j < i; j++)
+ if (reg_classes_intersect_p (cl, classes[j]))
+ gcc_unreachable ();
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (! hard_reg_set_equal_p (temp_hard_regset, ira_zero_hard_reg_set))
+ ira_reg_class_cover[ira_reg_class_cover_size++] = cl;
+ }
+ ira_important_classes_num = 0;
+ for (cl = 0; cl < N_REG_CLASSES; cl++)
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (! hard_reg_set_equal_p (temp_hard_regset, ira_zero_hard_reg_set))
+ for (j = 0; j < ira_reg_class_cover_size; j++)
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ COPY_HARD_REG_SET (temp_hard_regset2,
+ reg_class_contents[ira_reg_class_cover[j]]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset2, no_unit_alloc_regs);
+ if (cl == ira_reg_class_cover[j]
+ || (hard_reg_set_subset_p (temp_hard_regset, temp_hard_regset2)
+ && ! hard_reg_set_equal_p (temp_hard_regset,
+ temp_hard_regset2)))
+ {
+ ira_important_class_nums[cl] = ira_important_classes_num;
+ ira_important_classes[ira_important_classes_num++] = cl;
+ }
+ }
+ }
+}
+#endif
+
+/* Map of all register classes to corresponding cover class containing
+ the given class. If given class is not a subset of a cover class,
+ we translate it into the cheapest cover class. */
+enum reg_class ira_class_translate[N_REG_CLASSES];
+
+#ifdef IRA_COVER_CLASSES
+
+/* Set up array IRA_CLASS_TRANSLATE. */
+static void
+setup_class_translate (void)
+{
+ enum reg_class cl, cover_class, best_class, *cl_ptr;
+ enum machine_mode mode;
+ int i, cost, min_cost, best_cost;
+
+ for (cl = 0; cl < N_REG_CLASSES; cl++)
+ ira_class_translate[cl] = NO_REGS;
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ for (cl_ptr = &alloc_reg_class_subclasses[cover_class][0];
+ (cl = *cl_ptr) != LIM_REG_CLASSES;
+ cl_ptr++)
+ {
+ if (ira_class_translate[cl] == NO_REGS)
+ ira_class_translate[cl] = cover_class;
+#ifdef ENABLE_IRA_CHECKING
+ else
+ {
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (! hard_reg_set_subset_p (temp_hard_regset,
+ ira_zero_hard_reg_set))
+ gcc_unreachable ();
+ }
+#endif
+ }
+ ira_class_translate[cover_class] = cover_class;
+ }
+ /* For classes which are not fully covered by a cover class (in
+ other words covered by more one cover class), use the cheapest
+ cover class. */
+ for (cl = 0; cl < N_REG_CLASSES; cl++)
+ {
+ if (cl == NO_REGS || ira_class_translate[cl] != NO_REGS)
+ continue;
+ best_class = NO_REGS;
+ best_cost = INT_MAX;
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cover_class = ira_reg_class_cover[i];
+ COPY_HARD_REG_SET (temp_hard_regset,
+ reg_class_contents[cover_class]);
+ AND_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (! hard_reg_set_equal_p (temp_hard_regset, ira_zero_hard_reg_set))
+ {
+ min_cost = INT_MAX;
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ {
+ cost = (ira_memory_move_cost[mode][cl][0]
+ + ira_memory_move_cost[mode][cl][1]);
+ if (min_cost > cost)
+ min_cost = cost;
+ }
+ if (best_class == NO_REGS || best_cost > min_cost)
+ {
+ best_class = cover_class;
+ best_cost = min_cost;
+ }
+ }
+ }
+ ira_class_translate[cl] = best_class;
+ }
+}
+#endif
+
+/* The biggest important reg_class inside of intersection of the two
+ reg_classes (that is calculated taking only hard registers
+ available for allocation into account). If the both reg_classes
+ contain no hard registers available for allocation, the value is
+ calculated by taking all hard-registers including fixed ones into
+ account. */
+enum reg_class ira_reg_class_intersect[N_REG_CLASSES][N_REG_CLASSES];
+
+/* The biggest important reg_class inside of union of the two
+ reg_classes (that is calculated taking only hard registers
+ available for allocation into account). If the both reg_classes
+ contain no hard registers available for allocation, the value is
+ calculated by taking all hard-registers including fixed ones into
+ account. In other words, the value is the corresponding
+ reg_class_subunion value. */
+enum reg_class ira_reg_class_union[N_REG_CLASSES][N_REG_CLASSES];
+
+#ifdef IRA_COVER_CLASSES
+
+/* Set up IRA_REG_CLASS_INTERSECT and IRA_REG_CLASS_UNION. */
+static void
+setup_reg_class_intersect_union (void)
+{
+ int i, cl1, cl2, cl3;
+ HARD_REG_SET intersection_set, union_set, temp_set2;
+
+ for (cl1 = 0; cl1 < N_REG_CLASSES; cl1++)
+ {
+ for (cl2 = 0; cl2 < N_REG_CLASSES; cl2++)
+ {
+ ira_reg_class_intersect[cl1][cl2] = NO_REGS;
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl1]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ COPY_HARD_REG_SET (temp_set2, reg_class_contents[cl2]);
+ AND_COMPL_HARD_REG_SET (temp_set2, no_unit_alloc_regs);
+ if (hard_reg_set_equal_p (temp_hard_regset, ira_zero_hard_reg_set)
+ && hard_reg_set_equal_p (temp_set2, ira_zero_hard_reg_set))
+ {
+ for (i = 0;; i++)
+ {
+ cl3 = reg_class_subclasses[cl1][i];
+ if (cl3 == LIM_REG_CLASSES)
+ break;
+ if (reg_class_subset_p (ira_reg_class_intersect[cl1][cl2],
+ cl3))
+ ira_reg_class_intersect[cl1][cl2] = cl3;
+ }
+ ira_reg_class_union[cl1][cl2] = reg_class_subunion[cl1][cl2];
+ continue;
+ }
+ ira_reg_class_union[cl1][cl2] = NO_REGS;
+ COPY_HARD_REG_SET (intersection_set, reg_class_contents[cl1]);
+ AND_HARD_REG_SET (intersection_set, reg_class_contents[cl2]);
+ AND_COMPL_HARD_REG_SET (intersection_set, no_unit_alloc_regs);
+ COPY_HARD_REG_SET (union_set, reg_class_contents[cl1]);
+ IOR_HARD_REG_SET (union_set, reg_class_contents[cl2]);
+ AND_COMPL_HARD_REG_SET (union_set, no_unit_alloc_regs);
+ for (i = 0; i < ira_important_classes_num; i++)
+ {
+ cl3 = ira_important_classes[i];
+ COPY_HARD_REG_SET (temp_hard_regset, reg_class_contents[cl3]);
+ AND_COMPL_HARD_REG_SET (temp_hard_regset, no_unit_alloc_regs);
+ if (hard_reg_set_subset_p (temp_hard_regset, intersection_set))
+ {
+ COPY_HARD_REG_SET
+ (temp_set2,
+ reg_class_contents[(int)
+ ira_reg_class_intersect[cl1][cl2]]);
+ AND_COMPL_HARD_REG_SET (temp_set2, no_unit_alloc_regs);
+ if (! hard_reg_set_subset_p (temp_hard_regset, temp_set2)
+ /* Ignore unavailable hard registers and prefer
+ smallest class for debugging purposes. */
+ || (hard_reg_set_equal_p (temp_hard_regset, temp_set2)
+ && hard_reg_set_subset_p
+ (reg_class_contents[cl3],
+ reg_class_contents
+ [(int) ira_reg_class_intersect[cl1][cl2]])))
+ ira_reg_class_intersect[cl1][cl2] = (enum reg_class) cl3;
+ }
+ if (hard_reg_set_subset_p (temp_hard_regset, union_set))
+ {
+ COPY_HARD_REG_SET
+ (temp_set2,
+ reg_class_contents[(int) ira_reg_class_union[cl1][cl2]]);
+ AND_COMPL_HARD_REG_SET (temp_set2, no_unit_alloc_regs);
+ if (ira_reg_class_union[cl1][cl2] == NO_REGS
+ || (hard_reg_set_subset_p (temp_set2, temp_hard_regset)
+
+ && (! hard_reg_set_equal_p (temp_set2,
+ temp_hard_regset)
+ /* Ignore unavailable hard registers and
+ prefer smallest class for debugging
+ purposes. */
+ || hard_reg_set_subset_p
+ (reg_class_contents[cl3],
+ reg_class_contents
+ [(int) ira_reg_class_union[cl1][cl2]]))))
+ ira_reg_class_union[cl1][cl2] = (enum reg_class) cl3;
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+/* Output all cover classes and the translation map into file F. */
+static void
+print_class_cover (FILE *f)
+{
+ static const char *const reg_class_names[] = REG_CLASS_NAMES;
+ int i;
+
+ fprintf (f, "Class cover:\n");
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ fprintf (f, " %s", reg_class_names[ira_reg_class_cover[i]]);
+ fprintf (f, "\nClass translation:\n");
+ for (i = 0; i < N_REG_CLASSES; i++)
+ fprintf (f, " %s -> %s\n", reg_class_names[i],
+ reg_class_names[ira_class_translate[i]]);
+}
+
+/* Output all cover classes and the translation map into
+ stderr. */
+void
+ira_debug_class_cover (void)
+{
+ print_class_cover (stderr);
+}
+
+/* Set up different arrays concerning class subsets, cover and
+ important classes. */
+static void
+find_reg_class_closure (void)
+{
+ setup_reg_subclasses ();
+#ifdef IRA_COVER_CLASSES
+ setup_cover_and_important_classes ();
+ setup_class_translate ();
+ setup_reg_class_intersect_union ();
+#endif
+}
+
+
+
+/* Map: register class x machine mode -> number of hard registers of
+ given class needed to store value of given mode. If the number is
+ different, the size will be negative. */
+int ira_reg_class_nregs[N_REG_CLASSES][MAX_MACHINE_MODE];
+
+/* Maximal value of the previous array elements. */
+int ira_max_nregs;
+
+/* Form IRA_REG_CLASS_NREGS map. */
+static void
+setup_reg_class_nregs (void)
+{
+ int m;
+ enum reg_class cl;
+
+ ira_max_nregs = -1;
+ for (cl = 0; cl < N_REG_CLASSES; cl++)
+ for (m = 0; m < MAX_MACHINE_MODE; m++)
+ {
+ ira_reg_class_nregs[cl][m] = CLASS_MAX_NREGS (cl, m);
+ if (ira_max_nregs < ira_reg_class_nregs[cl][m])
+ ira_max_nregs = ira_reg_class_nregs[cl][m];
+ }
+}
+
+
+
+/* Array whose values are hard regset of hard registers available for
+ the allocation of given register class whose HARD_REGNO_MODE_OK
+ values for given mode are zero. */
+HARD_REG_SET prohibited_class_mode_regs[N_REG_CLASSES][NUM_MACHINE_MODES];
+
+/* Set up PROHIBITED_CLASS_MODE_REGS. */
+static void
+setup_prohibited_class_mode_regs (void)
+{
+ int i, j, k, hard_regno;
+ enum reg_class cl;
+
+ for (i = 0; i < ira_reg_class_cover_size; i++)
+ {
+ cl = ira_reg_class_cover[i];
+ for (j = 0; j < NUM_MACHINE_MODES; j++)
+ {
+ CLEAR_HARD_REG_SET (prohibited_class_mode_regs[cl][j]);
+ for (k = ira_class_hard_regs_num[cl] - 1; k >= 0; k--)
+ {
+ hard_regno = ira_class_hard_regs[cl][k];
+ if (! HARD_REGNO_MODE_OK (hard_regno, j))
+ SET_HARD_REG_BIT (prohibited_class_mode_regs[cl][j],
+ hard_regno);
+ }
+ }
+ }
+}
+
+
+
+/* Allocate and initialize IRA_REGISTER_MOVE_COST,
+ IRA_MAY_MOVE_IN_COST, and IRA_MAY_MOVE_OUT_COST for MODE if it is
+ not done yet. */
+void
+ira_init_register_move_cost (enum machine_mode mode)
+{
+ int cl1, cl2;
+
+ ira_assert (ira_register_move_cost[mode] == NULL
+ && ira_may_move_in_cost[mode] == NULL
+ && ira_may_move_out_cost[mode] == NULL);
+ if (move_cost[mode] == NULL)
+ init_move_cost (mode);
+ ira_register_move_cost[mode] = move_cost[mode];
+ /* Don't use ira_allocate because the tables exist out of scope of a
+ IRA call. */
+ ira_may_move_in_cost[mode]
+ = (move_table *) xmalloc (sizeof (move_table) * N_REG_CLASSES);
+ memcpy (ira_may_move_in_cost[mode], may_move_in_cost[mode],
+ sizeof (move_table) * N_REG_CLASSES);
+ ira_may_move_out_cost[mode]
+ = (move_table *) xmalloc (sizeof (move_table) * N_REG_CLASSES);
+ memcpy (ira_may_move_out_cost[mode], may_move_out_cost[mode],
+ sizeof (move_table) * N_REG_CLASSES);
+ for (cl1 = 0; cl1 < N_REG_CLASSES; cl1++)
+ {
+ for (cl2 = 0; cl2 < N_REG_CLASSES; cl2++)
+ {
+ if (ira_class_subset_p[cl1][cl2])
+ ira_may_move_in_cost[mode][cl1][cl2] = 0;
+ if (ira_class_subset_p[cl2][cl1])
+ ira_may_move_out_cost[mode][cl1][cl2] = 0;
+ }
+ }
+}
+
+
+
+/* Hard regsets whose all bits are correspondingly zero or one. */
+HARD_REG_SET ira_zero_hard_reg_set;
+HARD_REG_SET ira_one_hard_reg_set;
+
+/* This is called once during compiler work. It sets up
+ different arrays whose values don't depend on the compiled
+ function. */
+void
+ira_init_once (void)
+{
+ enum machine_mode mode;
+
+ CLEAR_HARD_REG_SET (ira_zero_hard_reg_set);
+ SET_HARD_REG_SET (ira_one_hard_reg_set);
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ {
+ ira_register_move_cost[mode] = NULL;
+ ira_may_move_in_cost[mode] = NULL;
+ ira_may_move_out_cost[mode] = NULL;
+ }
+ ira_init_costs_once ();
+}
+
+/* Free ira_register_move_cost, ira_may_move_in_cost, and
+ ira_may_move_out_cost for each mode. */
+static void
+free_register_move_costs (void)
+{
+ enum machine_mode mode;
+
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ {
+ if (ira_may_move_in_cost[mode] != NULL)
+ free (ira_may_move_in_cost[mode]);
+ if (ira_may_move_out_cost[mode] != NULL)
+ free (ira_may_move_out_cost[mode]);
+ ira_register_move_cost[mode] = NULL;
+ ira_may_move_in_cost[mode] = NULL;
+ ira_may_move_out_cost[mode] = NULL;
+ }
+}
+
+/* This is called every time when register related information is
+ changed. */
+void
+ira_init (void)
+{
+ free_register_move_costs ();
+ setup_reg_mode_hard_regset ();
+ setup_alloc_regs (flag_omit_frame_pointer != 0);
+ setup_class_subset_and_memory_move_costs ();
+ find_reg_class_closure ();
+ setup_reg_class_nregs ();
+ setup_prohibited_class_mode_regs ();
+ ira_init_costs ();
+}
+
+/* Function called once at the end of compiler work. */
+void
+ira_finish_once (void)
+{
+ ira_finish_costs_once ();
+ free_register_move_costs ();
+}
+
+
+
+/* Array whose values are hard regset of hard registers for which
+ move of the hard register in given mode into itself is
+ prohibited. */
+HARD_REG_SET ira_prohibited_mode_move_regs[NUM_MACHINE_MODES];
+
+/* Flag of that the above array has been initialized. */
+static bool ira_prohibited_mode_move_regs_initialized_p = false;
+
+/* Set up IRA_PROHIBITED_MODE_MOVE_REGS. */
+static void
+setup_prohibited_mode_move_regs (void)
+{
+ int i, j;
+ rtx test_reg1, test_reg2, move_pat, move_insn;
+
+ if (ira_prohibited_mode_move_regs_initialized_p)
+ return;
+ ira_prohibited_mode_move_regs_initialized_p = true;
+ test_reg1 = gen_rtx_REG (VOIDmode, 0);
+ test_reg2 = gen_rtx_REG (VOIDmode, 0);
+ move_pat = gen_rtx_SET (VOIDmode, test_reg1, test_reg2);
+ move_insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, move_pat, -1, 0);
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ {
+ SET_HARD_REG_SET (ira_prohibited_mode_move_regs[i]);
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ {
+ if (! HARD_REGNO_MODE_OK (j, i))
+ continue;
+ SET_REGNO (test_reg1, j);
+ PUT_MODE (test_reg1, i);
+ SET_REGNO (test_reg2, j);
+ PUT_MODE (test_reg2, i);
+ INSN_CODE (move_insn) = -1;
+ recog_memoized (move_insn);
+ if (INSN_CODE (move_insn) < 0)
+ continue;
+ extract_insn (move_insn);
+ if (! constrain_operands (1))
+ continue;
+ CLEAR_HARD_REG_BIT (ira_prohibited_mode_move_regs[i], j);
+ }
+ }
+}
+
+
+
+/* Function specific hard registers that can not be used for the
+ register allocation. */
+HARD_REG_SET ira_no_alloc_regs;
+
+/* Return TRUE if *LOC contains an asm. */
+static int
+insn_contains_asm_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+ if ( !*loc)
+ return FALSE;
+ if (GET_CODE (*loc) == ASM_OPERANDS)
+ return TRUE;
+ return FALSE;
+}
+
+
+/* Return TRUE if INSN contains an ASM. */
+static bool
+insn_contains_asm (rtx insn)
+{
+ return for_each_rtx (&insn, insn_contains_asm_1, NULL);
+}
+
+/* Set up regs_asm_clobbered. */
+static void
+compute_regs_asm_clobbered (char *regs_asm_clobbered)
+{
+ basic_block bb;
+
+ memset (regs_asm_clobbered, 0, sizeof (char) * FIRST_PSEUDO_REGISTER);
+
+ FOR_EACH_BB (bb)
+ {
+ rtx insn;
+ FOR_BB_INSNS_REVERSE (bb, insn)
+ {
+ struct df_ref **def_rec;
+
+ if (insn_contains_asm (insn))
+ for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+ {
+ struct df_ref *def = *def_rec;
+ unsigned int dregno = DF_REF_REGNO (def);
+ if (dregno < FIRST_PSEUDO_REGISTER)
+ {
+ unsigned int i;
+ enum machine_mode mode = GET_MODE (DF_REF_REAL_REG (def));
+ unsigned int end = dregno
+ + hard_regno_nregs[dregno][mode] - 1;
+
+ for (i = dregno; i <= end; ++i)
+ regs_asm_clobbered[i] = 1;
+ }
+ }
+ }
+ }
+}
+
+
+/* Set up ELIMINABLE_REGSET, IRA_NO_ALLOC_REGS, and REGS_EVER_LIVE. */
+static void
+setup_eliminable_regset (void)
+{
+ int i;
+ /* Like regs_ever_live, but 1 if a reg is set or clobbered from an
+ asm. Unlike regs_ever_live, elements of this array corresponding
+ to eliminable regs (like the frame pointer) are set if an asm
+ sets them. */
+ char *regs_asm_clobbered
+ = (char *) alloca (FIRST_PSEUDO_REGISTER * sizeof (char));
+#ifdef ELIMINABLE_REGS
+ static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+ /* FIXME: If EXIT_IGNORE_STACK is set, we will not save and restore
+ sp for alloca. So we can't eliminate the frame pointer in that
+ case. At some point, we should improve this by emitting the
+ sp-adjusting insns for this case. */
+ int need_fp
+ = (! flag_omit_frame_pointer
+ || (cfun->calls_alloca && EXIT_IGNORE_STACK)
+ || crtl->accesses_prior_frames
+ || crtl->stack_realign_needed
+ || FRAME_POINTER_REQUIRED);
+
+ frame_pointer_needed = need_fp;
+
+ COPY_HARD_REG_SET (ira_no_alloc_regs, no_unit_alloc_regs);
+ CLEAR_HARD_REG_SET (eliminable_regset);
+
+ compute_regs_asm_clobbered (regs_asm_clobbered);
+ /* Build the regset of all eliminable registers and show we can't
+ use those that we already know won't be eliminated. */
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < (int) ARRAY_SIZE (eliminables); i++)
+ {
+ bool cannot_elim
+ = (! CAN_ELIMINATE (eliminables[i].from, eliminables[i].to)
+ || (eliminables[i].to == STACK_POINTER_REGNUM && need_fp));
+
+ if (! regs_asm_clobbered[eliminables[i].from])
+ {
+ SET_HARD_REG_BIT (eliminable_regset, eliminables[i].from);
+
+ if (cannot_elim)
+ SET_HARD_REG_BIT (ira_no_alloc_regs, eliminables[i].from);
+ }
+ else if (cannot_elim)
+ error ("%s cannot be used in asm here",
+ reg_names[eliminables[i].from]);
+ else
+ df_set_regs_ever_live (eliminables[i].from, true);
+ }
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ if (! regs_asm_clobbered[HARD_FRAME_POINTER_REGNUM])
+ {
+ SET_HARD_REG_BIT (eliminable_regset, HARD_FRAME_POINTER_REGNUM);
+ if (need_fp)
+ SET_HARD_REG_BIT (ira_no_alloc_regs, HARD_FRAME_POINTER_REGNUM);
+ }
+ else if (need_fp)
+ error ("%s cannot be used in asm here",
+ reg_names[HARD_FRAME_POINTER_REGNUM]);
+ else
+ df_set_regs_ever_live (HARD_FRAME_POINTER_REGNUM, true);
+#endif
+
+#else
+ if (! regs_asm_clobbered[FRAME_POINTER_REGNUM])
+ {
+ SET_HARD_REG_BIT (eliminable_regset, FRAME_POINTER_REGNUM);
+ if (need_fp)
+ SET_HARD_REG_BIT (ira_no_alloc_regs, FRAME_POINTER_REGNUM);
+ }
+ else if (need_fp)
+ error ("%s cannot be used in asm here", reg_names[FRAME_POINTER_REGNUM]);
+ else
+ df_set_regs_ever_live (FRAME_POINTER_REGNUM, true);
+#endif
+}
+
+
+
+/* The length of the following two arrays. */
+int ira_reg_equiv_len;
+
+/* The element value is TRUE if the corresponding regno value is
+ invariant. */
+bool *ira_reg_equiv_invariant_p;
+
+/* The element value is equiv constant of given pseudo-register or
+ NULL_RTX. */
+rtx *ira_reg_equiv_const;
+
+/* Set up the two arrays declared above. */
+static void
+find_reg_equiv_invariant_const (void)
+{
+ int i;
+ bool invariant_p;
+ rtx list, insn, note, constant, x;
+
+ for (i = FIRST_PSEUDO_REGISTER; i < reg_equiv_init_size; i++)
+ {
+ constant = NULL_RTX;
+ invariant_p = false;
+ for (list = reg_equiv_init[i]; list != NULL_RTX; list = XEXP (list, 1))
+ {
+ insn = XEXP (list, 0);
+ note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
+
+ if (note == NULL_RTX)
+ continue;
+
+ x = XEXP (note, 0);
+
+ if (! function_invariant_p (x)
+ || ! flag_pic
+ /* A function invariant is often CONSTANT_P but may
+ include a register. We promise to only pass CONSTANT_P
+ objects to LEGITIMATE_PIC_OPERAND_P. */
+ || (CONSTANT_P (x) && LEGITIMATE_PIC_OPERAND_P (x)))
+ {
+ /* It can happen that a REG_EQUIV note contains a MEM
+ that is not a legitimate memory operand. As later
+ stages of the reload assume that all addresses found
+ in the reg_equiv_* arrays were originally legitimate,
+ we ignore such REG_EQUIV notes. */
+ if (memory_operand (x, VOIDmode))
+ invariant_p = MEM_READONLY_P (x);
+ else if (function_invariant_p (x))
+ {
+ if (GET_CODE (x) == PLUS
+ || x == frame_pointer_rtx || x == arg_pointer_rtx)
+ invariant_p = true;
+ else
+ constant = x;
+ }
+ }
+ }
+ ira_reg_equiv_invariant_p[i] = invariant_p;
+ ira_reg_equiv_const[i] = constant;
+ }
+}
+
+
+
+/* Set up REG_RENUMBER and CALLER_SAVE_NEEDED (used by reload) from
+ the allocation found by IRA. */
+static void
+setup_reg_renumber (void)
+{
+ int regno, hard_regno;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ caller_save_needed = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ /* There are no caps at this point. */
+ ira_assert (ALLOCNO_CAP_MEMBER (a) == NULL);
+ if (! ALLOCNO_ASSIGNED_P (a))
+ /* It can happen if A is not referenced but partially anticipated
+ somewhere in a region. */
+ ALLOCNO_ASSIGNED_P (a) = true;
+ ira_free_allocno_updated_costs (a);
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ regno = (int) REGNO (ALLOCNO_REG (a));
+ reg_renumber[regno] = (hard_regno < 0 ? -1 : hard_regno);
+ if (hard_regno >= 0 && ALLOCNO_CALLS_CROSSED_NUM (a) != 0
+ && ! ira_hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a),
+ call_used_reg_set))
+ {
+ ira_assert (!optimize || flag_caller_saves
+ || regno >= ira_reg_equiv_len
+ || ira_reg_equiv_const[regno]
+ || ira_reg_equiv_invariant_p[regno]);
+ caller_save_needed = 1;
+ }
+ }
+}
+
+/* Set up allocno assignment flags for further allocation
+ improvements. */
+static void
+setup_allocno_assignment_flags (void)
+{
+ int hard_regno;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (! ALLOCNO_ASSIGNED_P (a))
+ /* It can happen if A is not referenced but partially anticipated
+ somewhere in a region. */
+ ira_free_allocno_updated_costs (a);
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ /* Don't assign hard registers to allocnos which are destination
+ of removed store at the end of loop. It has no sense to keep
+ the same value in different hard registers. It is also
+ impossible to assign hard registers correctly to such
+ allocnos because the cost info and info about intersected
+ calls are incorrect for them. */
+ ALLOCNO_ASSIGNED_P (a) = (hard_regno >= 0
+ || ALLOCNO_MEM_OPTIMIZED_DEST_P (a)
+ || (ALLOCNO_MEMORY_COST (a)
+ - ALLOCNO_COVER_CLASS_COST (a)) < 0);
+ ira_assert (hard_regno < 0
+ || ! ira_hard_reg_not_in_set_p (hard_regno, ALLOCNO_MODE (a),
+ reg_class_contents
+ [ALLOCNO_COVER_CLASS (a)]));
+ }
+}
+
+/* Evaluate overall allocation cost and the costs for using hard
+ registers and memory for allocnos. */
+static void
+calculate_allocation_cost (void)
+{
+ int hard_regno, cost;
+ ira_allocno_t a;
+ ira_allocno_iterator ai;
+
+ ira_overall_cost = ira_reg_cost = ira_mem_cost = 0;
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ ira_assert (hard_regno < 0
+ || ! ira_hard_reg_not_in_set_p
+ (hard_regno, ALLOCNO_MODE (a),
+ reg_class_contents[ALLOCNO_COVER_CLASS (a)]));
+ if (hard_regno < 0)
+ {
+ cost = ALLOCNO_MEMORY_COST (a);
+ ira_mem_cost += cost;
+ }
+ else if (ALLOCNO_HARD_REG_COSTS (a) != NULL)
+ {
+ cost = (ALLOCNO_HARD_REG_COSTS (a)
+ [ira_class_hard_reg_index
+ [ALLOCNO_COVER_CLASS (a)][hard_regno]]);
+ ira_reg_cost += cost;
+ }
+ else
+ {
+ cost = ALLOCNO_COVER_CLASS_COST (a);
+ ira_reg_cost += cost;
+ }
+ ira_overall_cost += cost;
+ }
+
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file,
+ "+++Costs: overall %d, reg %d, mem %d, ld %d, st %d, move %d\n",
+ ira_overall_cost, ira_reg_cost, ira_mem_cost,
+ ira_load_cost, ira_store_cost, ira_shuffle_cost);
+ fprintf (ira_dump_file, "+++ move loops %d, new jumps %d\n",
+ ira_move_loops_num, ira_additional_jumps_num);
+ }
+
+}
+
+#ifdef ENABLE_IRA_CHECKING
+/* Check the correctness of the allocation. We do need this because
+ of complicated code to transform more one region internal
+ representation into one region representation. */
+static void
+check_allocation (void)
+{
+ ira_allocno_t a, conflict_a;
+ int hard_regno, conflict_hard_regno, nregs, conflict_nregs;
+ ira_allocno_conflict_iterator aci;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (ALLOCNO_CAP_MEMBER (a) != NULL
+ || (hard_regno = ALLOCNO_HARD_REGNO (a)) < 0)
+ continue;
+ nregs = hard_regno_nregs[hard_regno][ALLOCNO_MODE (a)];
+ FOR_EACH_ALLOCNO_CONFLICT (a, conflict_a, aci)
+ if ((conflict_hard_regno = ALLOCNO_HARD_REGNO (conflict_a)) >= 0)
+ {
+ conflict_nregs
+ = (hard_regno_nregs
+ [conflict_hard_regno][ALLOCNO_MODE (conflict_a)]);
+ if ((conflict_hard_regno <= hard_regno
+ && hard_regno < conflict_hard_regno + conflict_nregs)
+ || (hard_regno <= conflict_hard_regno
+ && conflict_hard_regno < hard_regno + nregs))
+ {
+ fprintf (stderr, "bad allocation for %d and %d\n",
+ ALLOCNO_REGNO (a), ALLOCNO_REGNO (conflict_a));
+ gcc_unreachable ();
+ }
+ }
+ }
+}
+#endif
+
+/* Fix values of array REG_EQUIV_INIT after live range splitting done
+ by IRA. */
+static void
+fix_reg_equiv_init (void)
+{
+ int max_regno = max_reg_num ();
+ int i, new_regno;
+ rtx x, prev, next, insn, set;
+
+ if (reg_equiv_init_size < max_regno)
+ {
+ reg_equiv_init
+ = (rtx *) ggc_realloc (reg_equiv_init, max_regno * sizeof (rtx));
+ while (reg_equiv_init_size < max_regno)
+ reg_equiv_init[reg_equiv_init_size++] = NULL_RTX;
+ for (i = FIRST_PSEUDO_REGISTER; i < reg_equiv_init_size; i++)
+ for (prev = NULL_RTX, x = reg_equiv_init[i]; x != NULL_RTX; x = next)
+ {
+ next = XEXP (x, 1);
+ insn = XEXP (x, 0);
+ set = single_set (insn);
+ ira_assert (set != NULL_RTX
+ && (REG_P (SET_DEST (set)) || REG_P (SET_SRC (set))));
+ if (REG_P (SET_DEST (set))
+ && ((int) REGNO (SET_DEST (set)) == i
+ || (int) ORIGINAL_REGNO (SET_DEST (set)) == i))
+ new_regno = REGNO (SET_DEST (set));
+ else if (REG_P (SET_SRC (set))
+ && ((int) REGNO (SET_SRC (set)) == i
+ || (int) ORIGINAL_REGNO (SET_SRC (set)) == i))
+ new_regno = REGNO (SET_SRC (set));
+ else
+ gcc_unreachable ();
+ if (new_regno == i)
+ prev = x;
+ else
+ {
+ if (prev == NULL_RTX)
+ reg_equiv_init[i] = next;
+ else
+ XEXP (prev, 1) = next;
+ XEXP (x, 1) = reg_equiv_init[new_regno];
+ reg_equiv_init[new_regno] = x;
+ }
+ }
+ }
+}
+
+#ifdef ENABLE_IRA_CHECKING
+/* Print redundant memory-memory copies. */
+static void
+print_redundant_copies (void)
+{
+ int hard_regno;
+ ira_allocno_t a;
+ ira_copy_t cp, next_cp;
+ ira_allocno_iterator ai;
+
+ FOR_EACH_ALLOCNO (a, ai)
+ {
+ if (ALLOCNO_CAP_MEMBER (a) != NULL)
+ /* It is a cap. */
+ continue;
+ hard_regno = ALLOCNO_HARD_REGNO (a);
+ if (hard_regno >= 0)
+ continue;
+ for (cp = ALLOCNO_COPIES (a); cp != NULL; cp = next_cp)
+ if (cp->first == a)
+ next_cp = cp->next_first_allocno_copy;
+ else
+ {
+ next_cp = cp->next_second_allocno_copy;
+ if (internal_flag_ira_verbose > 4 && ira_dump_file != NULL
+ && cp->insn != NULL_RTX
+ && ALLOCNO_HARD_REGNO (cp->first) == hard_regno)
+ fprintf (ira_dump_file,
+ " Redundant move from %d(freq %d):%d\n",
+ INSN_UID (cp->insn), cp->freq, hard_regno);
+ }
+ }
+}
+#endif
+
+/* Setup preferred and alternative classes for new pseudo-registers
+ created by IRA starting with START. */
+static void
+setup_preferred_alternate_classes_for_new_pseudos (int start)
+{
+ int i, old_regno;
+ int max_regno = max_reg_num ();
+
+ for (i = start; i < max_regno; i++)
+ {
+ old_regno = ORIGINAL_REGNO (regno_reg_rtx[i]);
+ ira_assert (i != old_regno);
+ setup_reg_classes (i, reg_preferred_class (old_regno),
+ reg_alternate_class (old_regno));
+ if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
+ fprintf (ira_dump_file,
+ " New r%d: setting preferred %s, alternative %s\n",
+ i, reg_class_names[reg_preferred_class (old_regno)],
+ reg_class_names[reg_alternate_class (old_regno)]);
+ }
+}
+
+
+
+/* Regional allocation can create new pseudo-registers. This function
+ expands some arrays for pseudo-registers. */
+static void
+expand_reg_info (int old_size)
+{
+ int i;
+ int size = max_reg_num ();
+
+ resize_reg_info ();
+ for (i = old_size; i < size; i++)
+ {
+ reg_renumber[i] = -1;
+ setup_reg_classes (i, GENERAL_REGS, ALL_REGS);
+ }
+}
+
+
+
+/* This page contains code for sorting the insn chain used by reload.
+ In the old register allocator, the insn chain order corresponds to
+ the order of insns in RTL. By putting insns with higher execution
+ frequency BBs first, reload has a better chance to generate less
+ expensive operand reloads for such insns. */
+
+/* Map bb index -> order number in the BB chain in RTL code. */
+static int *basic_block_order_nums;
+
+/* Map chain insn uid -> order number in the insn chain before sorting
+ the insn chain. */
+static int *chain_insn_order;
+
+/* The function is used to sort insn chain according insn execution
+ frequencies. */
+static int
+chain_freq_compare (const void *v1p, const void *v2p)
+{
+ const struct insn_chain *c1 = *(struct insn_chain * const *)v1p;
+ const struct insn_chain *c2 = *(struct insn_chain * const *)v2p;
+ int diff;
+
+ diff = (BASIC_BLOCK (c2->block)->frequency
+ - BASIC_BLOCK (c1->block)->frequency);
+ if (diff)
+ return diff;
+ /* Keep the same order in BB scope. */
+ return (chain_insn_order[INSN_UID(c1->insn)]
+ - chain_insn_order[INSN_UID(c2->insn)]);
+}
+
+/* Sort the insn chain according insn original order. */
+static int
+chain_bb_compare (const void *v1p, const void *v2p)
+{
+ const struct insn_chain *c1 = *(struct insn_chain * const *)v1p;
+ const struct insn_chain *c2 = *(struct insn_chain * const *)v2p;
+ int diff;
+
+ diff = (basic_block_order_nums[c1->block]
+ - basic_block_order_nums[c2->block]);
+ if (diff)
+ return diff;
+ /* Keep the same order in BB scope. */
+ return (chain_insn_order[INSN_UID(c1->insn)]
+ - chain_insn_order[INSN_UID(c2->insn)]);
+}
+
+/* Sort the insn chain according to insn frequencies if
+ FREQ_P or according to insn original order otherwise. */
+void
+ira_sort_insn_chain (bool freq_p)
+{
+ struct insn_chain *chain, **chain_arr;
+ basic_block bb;
+ int i, n;
+
+ chain_insn_order = (int *) ira_allocate (get_max_uid () * sizeof (int));
+ for (n = 0, chain = reload_insn_chain; chain != 0; chain = chain->next)
+ {
+ chain_insn_order[INSN_UID (chain->insn)] = n;
+ n++;
+ }
+ if (n <= 1)
+ return;
+ chain_arr
+ = (struct insn_chain **) ira_allocate (n * sizeof (struct insn_chain *));
+ basic_block_order_nums
+ = (int *) ira_allocate (sizeof (int) * last_basic_block);
+ n = 0;
+ FOR_EACH_BB (bb)
+ {
+ basic_block_order_nums[bb->index] = n++;
+ }
+ for (n = 0, chain = reload_insn_chain; chain != 0; chain = chain->next)
+ chain_arr[n++] = chain;
+ qsort (chain_arr, n, sizeof (struct insn_chain *),
+ freq_p ? chain_freq_compare : chain_bb_compare);
+ ira_free (chain_insn_order);
+ for (i = 1; i < n - 1; i++)
+ {
+ chain_arr[i]->next = chain_arr[i + 1];
+ chain_arr[i]->prev = chain_arr[i - 1];
+ }
+ chain_arr[i]->next = NULL;
+ chain_arr[i]->prev = chain_arr[i - 1];
+ reload_insn_chain = chain_arr[0];
+ reload_insn_chain->prev = NULL;
+ reload_insn_chain->next = chain_arr[1];
+ ira_free (basic_block_order_nums);
+ ira_free (chain_arr);
+}
+
+
+
+/* All natural loops. */
+struct loops ira_loops;
+
+/* This is the main entry of IRA. */
+static void
+ira (FILE *f)
+{
+ int overall_cost_before, allocated_reg_info_size;
+ bool loops_p;
+ int max_regno_before_ira, ira_max_point_before_emit;
+ int rebuild_p;
+ int saved_flag_ira_algorithm;
+ basic_block bb;
+
+ timevar_push (TV_IRA);
+
+ if (flag_ira_verbose < 10)
+ {
+ internal_flag_ira_verbose = flag_ira_verbose;
+ ira_dump_file = f;
+ }
+ else
+ {
+ internal_flag_ira_verbose = flag_ira_verbose - 10;
+ ira_dump_file = stderr;
+ }
+
+ setup_prohibited_mode_move_regs ();
+
+ df_note_add_problem ();
+
+ if (optimize == 1)
+ {
+ df_live_add_problem ();
+ df_live_set_all_dirty ();
+ }
+#ifdef ENABLE_CHECKING
+ df->changeable_flags |= DF_VERIFY_SCHEDULED;
+#endif
+ df_analyze ();
+ df_clear_flags (DF_NO_INSN_RESCAN);
+ regstat_init_n_sets_and_refs ();
+ regstat_compute_ri ();
+
+ /* If we are not optimizing, then this is the only place before
+ register allocation where dataflow is done. And that is needed
+ to generate these warnings. */
+ if (warn_clobbered)
+ generate_setjmp_warnings ();
+
+ rebuild_p = update_equiv_regs ();
+
+#ifndef IRA_NO_OBSTACK
+ gcc_obstack_init (&ira_obstack);
+#endif
+ bitmap_obstack_initialize (&ira_bitmap_obstack);
+ if (optimize)
+ {
+ max_regno = max_reg_num ();
+ ira_reg_equiv_len = max_regno;
+ ira_reg_equiv_invariant_p
+ = (bool *) ira_allocate (max_regno * sizeof (bool));
+ memset (ira_reg_equiv_invariant_p, 0, max_regno * sizeof (bool));
+ ira_reg_equiv_const = (rtx *) ira_allocate (max_regno * sizeof (rtx));
+ memset (ira_reg_equiv_const, 0, max_regno * sizeof (rtx));
+ find_reg_equiv_invariant_const ();
+ if (rebuild_p)
+ {
+ timevar_push (TV_JUMP);
+ rebuild_jump_labels (get_insns ());
+ purge_all_dead_edges ();
+ timevar_pop (TV_JUMP);
+ }
+ }
+
+ max_regno_before_ira = allocated_reg_info_size = max_reg_num ();
+ allocate_reg_info ();
+ setup_eliminable_regset ();
+
+ ira_overall_cost = ira_reg_cost = ira_mem_cost = 0;
+ ira_load_cost = ira_store_cost = ira_shuffle_cost = 0;
+ ira_move_loops_num = ira_additional_jumps_num = 0;
+
+ ira_assert (current_loops == NULL);
+ flow_loops_find (&ira_loops);
+ current_loops = &ira_loops;
+ saved_flag_ira_algorithm = flag_ira_algorithm;
+ if (optimize && number_of_loops () > (unsigned) IRA_MAX_LOOPS_NUM)
+ flag_ira_algorithm = IRA_ALGORITHM_CB;
+
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "Building IRA IR\n");
+ loops_p = ira_build (optimize
+ && (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
+ || flag_ira_algorithm == IRA_ALGORITHM_MIXED));
+ if (optimize)
+ ira_color ();
+ else
+ ira_fast_allocation ();
+
+ ira_max_point_before_emit = ira_max_point;
+
+ ira_emit (loops_p);
+
+ if (optimize)
+ {
+ max_regno = max_reg_num ();
+
+ if (! loops_p)
+ ira_initiate_assign ();
+ else
+ {
+ expand_reg_info (allocated_reg_info_size);
+ setup_preferred_alternate_classes_for_new_pseudos
+ (allocated_reg_info_size);
+ allocated_reg_info_size = max_regno;
+
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
+ fprintf (ira_dump_file, "Flattening IR\n");
+ ira_flattening (max_regno_before_ira, ira_max_point_before_emit);
+ /* New insns were generated: add notes and recalculate live
+ info. */
+ df_analyze ();
+
+ flow_loops_find (&ira_loops);
+ current_loops = &ira_loops;
+
+ setup_allocno_assignment_flags ();
+ ira_initiate_assign ();
+ ira_reassign_conflict_allocnos (max_regno);
+ }
+ }
+
+ setup_reg_renumber ();
+
+ calculate_allocation_cost ();
+
+#ifdef ENABLE_IRA_CHECKING
+ if (optimize)
+ check_allocation ();
+#endif
+
+ delete_trivially_dead_insns (get_insns (), max_reg_num ());
+ max_regno = max_reg_num ();
+
+ /* Determine if the current function is a leaf before running IRA
+ since this can impact optimizations done by the prologue and
+ epilogue thus changing register elimination offsets. */
+ current_function_is_leaf = leaf_function_p ();
+
+ /* And the reg_equiv_memory_loc array. */
+ VEC_safe_grow (rtx, gc, reg_equiv_memory_loc_vec, max_regno);
+ memset (VEC_address (rtx, reg_equiv_memory_loc_vec), 0,
+ sizeof (rtx) * max_regno);
+ reg_equiv_memory_loc = VEC_address (rtx, reg_equiv_memory_loc_vec);
+
+ if (max_regno != max_regno_before_ira)
+ {
+ regstat_free_n_sets_and_refs ();
+ regstat_free_ri ();
+ regstat_init_n_sets_and_refs ();
+ regstat_compute_ri ();
+ }
+
+ allocate_initial_values (reg_equiv_memory_loc);
+
+ overall_cost_before = ira_overall_cost;
+ if (optimize)
+ {
+ fix_reg_equiv_init ();
+
+#ifdef ENABLE_IRA_CHECKING
+ print_redundant_copies ();
+#endif
+
+ ira_spilled_reg_stack_slots_num = 0;
+ ira_spilled_reg_stack_slots
+ = ((struct ira_spilled_reg_stack_slot *)
+ ira_allocate (max_regno
+ * sizeof (struct ira_spilled_reg_stack_slot)));
+ memset (ira_spilled_reg_stack_slots, 0,
+ max_regno * sizeof (struct ira_spilled_reg_stack_slot));
+ }
+
+ timevar_pop (TV_IRA);
+
+ timevar_push (TV_RELOAD);
+ df_set_flags (DF_NO_INSN_RESCAN);
+ build_insn_chain ();
+
+ if (optimize)
+ ira_sort_insn_chain (true);
+
+ reload_completed = !reload (get_insns (), optimize > 0);
+
+ timevar_pop (TV_RELOAD);
+
+ timevar_push (TV_IRA);
+
+ if (optimize)
+ {
+ ira_free (ira_spilled_reg_stack_slots);
+
+ ira_finish_assign ();
+
+ }
+ if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL
+ && overall_cost_before != ira_overall_cost)
+ fprintf (ira_dump_file, "+++Overall after reload %d\n", ira_overall_cost);
+ ira_destroy ();
+
+ flow_loops_free (&ira_loops);
+ free_dominance_info (CDI_DOMINATORS);
+ FOR_ALL_BB (bb)
+ bb->loop_father = NULL;
+ current_loops = NULL;
+
+ flag_ira_algorithm = saved_flag_ira_algorithm;
+
+ regstat_free_ri ();
+ regstat_free_n_sets_and_refs ();
+
+ if (optimize)
+ {
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ ira_free (ira_reg_equiv_invariant_p);
+ ira_free (ira_reg_equiv_const);
+ }
+
+ bitmap_obstack_release (&ira_bitmap_obstack);
+#ifndef IRA_NO_OBSTACK
+ obstack_free (&ira_obstack, NULL);
+#endif
+
+ /* The code after the reload has changed so much that at this point
+ we might as well just rescan everything. Not that
+ df_rescan_all_insns is not going to help here because it does not
+ touch the artificial uses and defs. */
+ df_finish_pass (true);
+ if (optimize > 1)
+ df_live_add_problem ();
+ df_scan_alloc (NULL);
+ df_scan_blocks ();
+
+ if (optimize)
+ df_analyze ();
+
+ timevar_pop (TV_IRA);
+}
+
+
+
+static bool
+gate_ira (void)
+{
+ return flag_ira != 0;
+}
+
+/* Run the integrated register allocator. */
+static unsigned int
+rest_of_handle_ira (void)
+{
+ ira (dump_file);
+ return 0;
+}
+
+struct rtl_opt_pass pass_ira =
+{
+ {
+ RTL_PASS,
+ "ira", /* name */
+ gate_ira, /* gate */
+ rest_of_handle_ira, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect /* todo_flags_finish */
+ }
+};
diff --git a/gcc/ira.h b/gcc/ira.h
new file mode 100644
index 0000000..8a90785
--- /dev/null
+++ b/gcc/ira.h
@@ -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);
+
diff --git a/gcc/local-alloc.c b/gcc/local-alloc.c
index 98b6770..e7bbcda 100644
--- a/gcc/local-alloc.c
+++ b/gcc/local-alloc.c
@@ -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 */
diff --git a/gcc/opts.c b/gcc/opts.c
index 1de217e..badc0a1 100644
--- a/gcc/opts.c
+++ b/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;
diff --git a/gcc/params.def b/gcc/params.def
index 8dd1cf4..c71b2b6 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -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. */
diff --git a/gcc/params.h b/gcc/params.h
index 7e08ec7..8147b60 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -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 */
diff --git a/gcc/passes.c b/gcc/passes.c
index f47c6ec..f45507f 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -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);
{
diff --git a/gcc/postreload.c b/gcc/postreload.c
index 5f6fec1..7659bab 100644
--- a/gcc/postreload.c
+++ b/gcc/postreload.c
@@ -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);
}
diff --git a/gcc/regclass.c b/gcc/regclass.c
index 386214d..b12d416 100644
--- a/gcc/regclass.c
+++ b/gcc/regclass.c
@@ -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
diff --git a/gcc/regmove.c b/gcc/regmove.c
index 7de8d0d..e25dbec 100644
--- a/gcc/regmove.c
+++ b/gcc/regmove.c
@@ -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))
diff --git a/gcc/regs.h b/gcc/regs.h
index 20587ac..00be997 100644
--- a/gcc/regs.h
+++ b/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). */
diff --git a/gcc/reload.c b/gcc/reload.c
index 5a79c44..8163700 100644
--- a/gcc/reload.c
+++ b/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
diff --git a/gcc/reload.h b/gcc/reload.h
index 3873f5c..17d8a3e 100644
--- a/gcc/reload.h
+++ b/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;
diff --git a/gcc/reload1.c b/gcc/reload1.c
index 3ee0fc3..bb0d423 100644
--- a/gcc/reload1.c
+++ b/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
{
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 5281b14..42fc2ad 100644
--- a/gcc/rtl.h
+++ b/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);
diff --git a/gcc/testsuite/gcc.dg/20080410-1.c b/gcc/testsuite/gcc.dg/20080410-1.c
new file mode 100644
index 0000000..ebd783d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/20080410-1.c
@@ -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 ();
+}
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a2693fc..94fa087 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -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")
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 38d8eaf..7ace61c 100644
--- a/gcc/toplev.c
+++ b/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);
diff --git a/gcc/toplev.h b/gcc/toplev.h
index 0064613..5555bf1 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -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 *);
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index acb0519..f4e02e8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -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;