+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>
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)
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 \
$(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)
$(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) \
$(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) \
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) \
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) \
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. */
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;
/* 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.
#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
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
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 *);
static struct insn_chain *insert_one_insn (struct insn_chain *, int, int,
rtx);
static void add_stored_regs (rtx, const_rtx, void *);
+
\f
+
static GTY(()) rtx savepat;
static GTY(()) rtx restpat;
static GTY(()) rtx test_reg;
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. */
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);
{
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);
}
}
}
+
\f
+
/* Initialize save areas by showing that we haven't allocated any yet. */
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.
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
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. */
if (regno_save_mem[i][j] != 0)
set_mem_alias_set (regno_save_mem[i][j], get_frame_alias_set ());
}
+
\f
+
/* Find the places where hard regs are live across calls and save them. */
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));
}
}
- 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
/* 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),
/* 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),
#include "expr.h"
#include "output.h"
#include "graphds.h"
+#include "params.h"
/* Checks whether BB is executed exactly once in each LOOP iteration. */
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
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. */
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
{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
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. */
#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
: (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. */
{ 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
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. */
{ 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
{ 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
{ 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])
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. */
(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
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;
{-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.
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 \
@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
-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
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
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
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
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
@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
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;
+
\f
/* Other basic status info about current function. */
/* 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 *);
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);
\f
/* Look through the list of eliminable registers. Set ELIM_SET to the
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);
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;
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;
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);
}
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);
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,
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
{
RTL_PASS,
"greg", /* name */
- NULL, /* gate */
+ gate_handle_global_alloc, /* gate */
rest_of_handle_global_alloc, /* execute */
NULL, /* sub */
NULL, /* next */
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. */
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. */
--- /dev/null
+/* 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;
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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;
+ }
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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]);
+ }
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+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);
+ }
+ }
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+#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 ();
+}
--- /dev/null
+/* 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;
+
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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;
+ }
+}
+
+\f
+
+/* 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));
+ }
+ }
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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 ();
+}
+
+\f
+
+/* 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);
+}
--- /dev/null
+/* 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)))
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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);
+}
--- /dev/null
+/* 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;
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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
+ &nbs