This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
RFA: small patch to fix IRA crash of m68k compiler
- From: Vladimir Makarov <vmakarov at redhat dot com>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>
- Cc: Andreas Schwab <schwab at suse dot de>
- Date: Mon, 08 Sep 2008 15:14:48 -0400
- Subject: RFA: small patch to fix IRA crash of m68k compiler
The patch fixes a crash of m68k compiler found by Andreas Schwab
http://gcc.gnu.org/ml/gcc/2008-09/msg00183.html
2008-09-08 Vladimir Makarov <vmakarov@redhat.com>
* ira-conflicts.c (process_regs_for_copy): Check that the hard
regno is not negative.
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/passes.texi ./doc/passes.texi
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/passes.texi 2008-08-21 21:13:15.000000000 -0400
+++ ./doc/passes.texi 2008-08-22 21:53:40.000000000 -0400
@@ -841,6 +841,28 @@ Global register allocation. This pass a
the remaining pseudo registers (those whose life spans are not
contained in one basic block). The pass is located in @file{global.c}.
+@item
+The optional integrated register allocator (@acronym{IRA}). It can be
+used instead of the local and global allocator. It is called
+integrated because coalescing, register live range splitting, and hard
+register preferencing are done on-the-fly during coloring. It also
+has better integration with the reload pass. Pseudo-registers spilled
+by the allocator or the reload have still a chance to get
+hard-registers if the reload evicts some pseudo-registers from
+hard-registers. The allocator helps to choose better pseudos for
+spilling based on their live ranges and to coalesce stack slots
+allocated for the spilled pseudo-registers. IRA is a regional
+register allocator which is transformed into Chaitin-Briggs allocator
+if there is one region. By default, IRA chooses regions using
+register pressure but the user can force it to use one region or
+regions corresponding to all loops.
+
+Source files of the allocator are @file{ira.c}, @file{ira-build.c},
+@file{ira-costs.c}, @file{ira-conflicts.c}, @file{ira-color.c},
+@file{ira-emit.c}, @file{ira-lives}, plus header files @file{ira.h}
+and @file{ira-int.h} used for the communication between the allocator
+and the rest of the compiler and between the IRA files.
+
@cindex reloading
@item
Reloading. This pass renumbers pseudo registers with the hardware
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/tm.texi ./doc/tm.texi
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/tm.texi 2008-08-21 21:13:15.000000000 -0400
+++ ./doc/tm.texi 2008-08-22 21:53:41.000000000 -0400
@@ -2026,6 +2026,18 @@ The macro body should not assume anythin
On most machines, it is not necessary to define this macro.
@end defmac
+@defmac IRA_HARD_REGNO_ADD_COST_MULTIPLIER (@var{regno})
+In some case register allocation order is not enough for the
+Integrated Register Allocator (@acronym{IRA}) to generate a good code.
+If this macro is defined, it should return a floating point value
+based on @var{regno}. The cost of using @var{regno} for a pseudo will
+be increased by approximately the pseudo's usage frequency times the
+value returned by this macro. Not defining this macro is equivalent
+to having it always return @code{0.0}.
+
+On most machines, it is not necessary to define this macro.
+@end defmac
+
@node Values in Registers
@subsection How Values Fit in Registers
@@ -2814,6 +2826,19 @@ as below:
@end smallexample
@end defmac
+@defmac IRA_COVER_CLASSES
+The macro defines cover classes for the Integrated Register Allocator
+(@acronym{IRA}). Cover classes are a set of non-intersecting register
+classes covering all hard registers used for register allocation
+purposes. Any move between two registers in the same cover class
+should be cheaper than load or store of the registers. The macro
+value should be the initializer for an array of register class values,
+with @code{LIM_REG_CLASSES} used as the end marker.
+
+You must define this macro in order to use the integrated register
+allocator for the target.
+@end defmac
+
@node Old Constraints
@section Obsolete Macros for Defining Constraints
@cindex defining constraints, obsolete method
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/invoke.texi ./doc/invoke.texi
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/doc/invoke.texi 2008-08-21 21:13:15.000000000 -0400
+++ ./doc/invoke.texi 2008-08-25 11:33:44.000000000 -0400
@@ -274,7 +274,8 @@ Objective-C and Objective-C++ Dialects}.
@xref{Debugging Options,,Options for Debugging Your Program or GCC}.
@gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol
-fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
--fdump-noaddr -fdump-unnumbered -fdump-translation-unit@r{[}-@var{n}@r{]} @gol
+-fdump-noaddr -fdump-unnumbered @gol
+-fdump-translation-unit@r{[}-@var{n}@r{]} @gol
-fdump-class-hierarchy@r{[}-@var{n}@r{]} @gol
-fdump-ipa-all -fdump-ipa-cgraph -fdump-ipa-inline @gol
-fdump-statistics @gol
@@ -332,7 +333,10 @@ Objective-C and Objective-C++ Dialects}.
-finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
-finline-small-functions -fipa-cp -fipa-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
@@ -5672,6 +5681,49 @@ optimization.
Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
+@item -fira
+@opindex fira
+Use the integrated register allocator (@acronym{IRA}) for register
+allocation. It is a default if @acronym{IRA} has been ported for the
+target.
+
+@item -fira-algorithm=@var{algorithm}
+Use specified algorithm for the integrated register allocator. The
+@var{algorithm} argument should be one of @code{regional}, @code{CB},
+or @code{mixed}. The second algorithm specifies Chaitin-Briggs
+coloring, the first one specifies regional coloring based on
+Chaitin-Briggs coloring, and the third one which is the default
+specifies a mix of Chaitin-Briggs and regional algorithms where loops
+with small register pressure are ignored. The first algorithm can
+give best result for machines with small size and irregular register
+set, the second one is faster and generates decent code and the
+smallest size code, and the mixed algorithm usually give the best
+results in most cases and for most architectures.
+
+@item -fira-coalesce
+@opindex fira-coalesce
+Do optimistic register coalescing. This option might be profitable for
+architectures with big regular register files.
+
+@item -fno-ira-share-save-slots
+@opindex fno-ira-share-save-slots
+Switch off sharing stack slots used for saving call used hard
+registers living through a call. Each hard register will get a
+separate stack slot and as a result function stack frame will be
+bigger.
+
+@item -fno-ira-share-spill-slots
+@opindex fno-ira-share-spill-slots
+Switch off sharing stack slots allocated for pseudo-registers. Each
+pseudo-register which did not get a hard register will get a separate
+stack slot and as a result function stack frame will be bigger.
+
+@item -fira-verbose=@var{n}
+@opindex fira-verbose
+Set up how verbose dump file for the integrated register allocator
+will be. Default value is 5. If the value is greater or equal to 10,
+the dump file will be stderr as if the value were @var{n} minus 10.
+
@item -fdelayed-branch
@opindex fdelayed-branch
If supported for the target machine, attempt to reorder instructions
@@ -7371,6 +7423,13 @@ processing. If this limit is hit, SCCVN
function will not be done and optimizations depending on it will
be disabled. The default maximum SCC size is 10000.
+@item ira-max-loops-num
+IRA uses a regional register allocation by default. If a function
+contains loops more than number given by the parameter, non-regional
+register allocator will be used even when option
+@option{-fira-algorithm} is given. The default value of the parameter
+is 20.
+
@end table
@end table
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/flags.h ./flags.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/flags.h 2008-08-21 21:16:44.000000000 -0400
+++ ./flags.h 2008-08-22 21:53:45.000000000 -0400
@@ -205,6 +205,19 @@ extern int flag_debug_asm;
extern int flag_next_runtime;
extern int flag_dump_rtl_in_asm;
+
+/* The algorithm used for the integrated register allocator (IRA). */
+enum ira_algorithm
+{
+ IRA_ALGORITHM_REGIONAL,
+ IRA_ALGORITHM_CB,
+ IRA_ALGORITHM_MIXED
+};
+
+extern enum ira_algorithm flag_ira_algorithm;
+
+extern unsigned int flag_ira_verbose;
+
/* Other basic status info about current function. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/postreload.c ./postreload.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/postreload.c 2008-08-21 21:16:44.000000000 -0400
+++ ./postreload.c 2008-08-22 18:09:58.000000000 -0400
@@ -1565,7 +1565,7 @@ move2add_note_store (rtx dst, const_rtx
static bool
gate_handle_postreload (void)
{
- return (optimize > 0);
+ return (optimize > 0 && reload_completed);
}
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload.c ./reload.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload.c 2008-08-21 21:16:44.000000000 -0400
+++ ./reload.c 2008-08-22 21:53:54.000000000 -0400
@@ -1549,8 +1549,10 @@ push_reload (rtx in, rtx out, rtx *inloc
&& reg_mentioned_p (XEXP (note, 0), in)
/* Check that a former pseudo is valid; see find_dummy_reload. */
&& (ORIGINAL_REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER
- || (!bitmap_bit_p (DF_LIVE_OUT (ENTRY_BLOCK_PTR),
- ORIGINAL_REGNO (XEXP (note, 0)))
+ || (! bitmap_bit_p (flag_ira
+ ? DF_LR_OUT (ENTRY_BLOCK_PTR)
+ : DF_LIVE_OUT (ENTRY_BLOCK_PTR),
+ ORIGINAL_REGNO (XEXP (note, 0)))
&& hard_regno_nregs[regno][GET_MODE (XEXP (note, 0))] == 1))
&& ! refers_to_regno_for_reload_p (regno,
end_hard_regno (rel_mode,
@@ -2027,7 +2029,9 @@ find_dummy_reload (rtx real_in, rtx real
can ignore the conflict). We must never introduce writes
to such hardregs, as they would clobber the other live
pseudo. See PR 20973. */
- || (!bitmap_bit_p (DF_LIVE_OUT (ENTRY_BLOCK_PTR),
+ || (!bitmap_bit_p (flag_ira
+ ? DF_LR_OUT (ENTRY_BLOCK_PTR)
+ : DF_LIVE_OUT (ENTRY_BLOCK_PTR),
ORIGINAL_REGNO (in))
/* Similarly, only do this if we can be sure that the death
note is still valid. global can assign some hardreg to
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/tree-pass.h ./tree-pass.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/tree-pass.h 2008-08-21 21:16:44.000000000 -0400
+++ ./tree-pass.h 2008-08-22 21:53:54.000000000 -0400
@@ -469,6 +469,7 @@ extern struct rtl_opt_pass pass_sms;
extern struct rtl_opt_pass pass_sched;
extern struct rtl_opt_pass pass_local_alloc;
extern struct rtl_opt_pass pass_global_alloc;
+extern struct rtl_opt_pass pass_ira;
extern struct rtl_opt_pass pass_postreload;
extern struct rtl_opt_pass pass_clean_state;
extern struct rtl_opt_pass pass_branch_prob;
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload.h ./reload.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload.h 2008-08-21 21:16:44.000000000 -0400
+++ ./reload.h 2008-08-25 11:35:41.000000000 -0400
@@ -1,6 +1,6 @@
-/* Communication between reload.c and reload1.c.
- Copyright (C) 1987, 1991, 1992, 1993, 1994, 1995, 1997, 1998,
- 1999, 2000, 2001, 2003, 2004, 2007 Free Software Foundation, Inc.
+/* Communication between reload.c, reload1.c and the rest of compiler.
+ Copyright (C) 1987, 1991, 1992, 1993, 1994, 1995, 1997, 1998, 1999,
+ 2000, 2001, 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
This file is part of GCC.
@@ -209,14 +209,20 @@ struct insn_chain
int block;
/* The rtx of the insn. */
rtx insn;
- /* Register life information: record all live hard registers, and all
- live pseudos that have a hard register. */
+ /* Register life information: record all live hard registers, and
+ all live pseudos that have a hard register. This set also
+ contains pseudos spilled by IRA. */
regset_head live_throughout;
regset_head dead_or_set;
-
+ /* Register life information: record all all live pseudos that was
+ saved through given insn. */
+ regset_head saved;
/* Copies of the global variables computed by find_reloads. */
struct reload *rld;
int n_reloads;
+ /* It is defined only when IS_CALLER_SAVE_INSN is TRUE. The value
+ is regno of pseudo saved/restored by the insn. */
+ int saved_pseudo_regno;
/* Indicates which registers have already been used for spills. */
HARD_REG_SET used_spill_regs;
@@ -360,6 +366,9 @@ extern void setup_save_areas (void);
/* Find the places where hard regs are live across calls and save them. */
extern void save_call_clobbered_regs (void);
+/* Print global save info. */
+extern void debug_save_data (void);
+
/* Replace (subreg (reg)) with the appropriate (reg) for any operands. */
extern void cleanup_subreg_operands (rtx);
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/cfgloopanal.c ./cfgloopanal.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/cfgloopanal.c 2008-08-21 21:16:44.000000000 -0400
+++ ./cfgloopanal.c 2008-08-22 18:09:59.000000000 -0400
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.
#include "expr.h"
#include "output.h"
#include "graphds.h"
+#include "params.h"
/* Checks whether BB is executed exactly once in each LOOP iteration. */
@@ -372,6 +373,7 @@ init_set_costs (void)
unsigned
estimate_reg_pressure_cost (unsigned n_new, unsigned n_old)
{
+ unsigned cost;
unsigned regs_needed = n_new + n_old;
/* If we have enough registers, we should use them and not restrict
@@ -379,12 +381,25 @@ estimate_reg_pressure_cost (unsigned n_n
if (regs_needed + target_res_regs <= target_avail_regs)
return 0;
- /* If we are close to running out of registers, try to preserve them. */
if (regs_needed <= target_avail_regs)
- return target_reg_cost * n_new;
-
- /* If we run out of registers, it is very expensive to add another one. */
- return target_spill_cost * n_new;
+ /* If we are close to running out of registers, try to preserve
+ them. */
+ cost = target_reg_cost * n_new;
+ else
+ /* If we run out of registers, it is very expensive to add another
+ one. */
+ cost = target_spill_cost * n_new;
+
+ if (optimize && flag_ira && (flag_ira_algorithm == IRA_ALGORITHM_REGIONAL
+ || flag_ira_algorithm == IRA_ALGORITHM_MIXED)
+ && number_of_loops () <= (unsigned) IRA_MAX_LOOPS_NUM)
+ /* IRA regional allocation deals with high register pressure
+ better. So decrease the cost (to do more accurate the cost
+ calculation for IRA, we need to know how many registers lives
+ through the loop transparently). */
+ cost /= 2;
+
+ return cost;
}
/* Sets EDGE_LOOP_EXIT flag for all loop exits. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/params.h ./params.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/params.h 2008-08-21 21:16:44.000000000 -0400
+++ ./params.h 2008-08-22 18:09:59.000000000 -0400
@@ -167,6 +167,8 @@ typedef enum compiler_param
PARAM_VALUE (PARAM_L2_CACHE_SIZE)
#define USE_CANONICAL_TYPES \
PARAM_VALUE (PARAM_USE_CANONICAL_TYPES)
+#define IRA_MAX_LOOPS_NUM \
+ PARAM_VALUE (PARAM_IRA_MAX_LOOPS_NUM)
#define SWITCH_CONVERSION_BRANCH_RATIO \
PARAM_VALUE (PARAM_SWITCH_CONVERSION_BRANCH_RATIO)
#endif /* ! GCC_PARAMS_H */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/toplev.c ./toplev.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/toplev.c 2008-08-21 21:16:44.000000000 -0400
+++ ./toplev.c 2008-08-22 21:54:04.000000000 -0400
@@ -66,6 +66,7 @@ along with GCC; see the file COPYING3.
#include "diagnostic.h"
#include "params.h"
#include "reload.h"
+#include "ira.h"
#include "dwarf2asm.h"
#include "integrate.h"
#include "real.h"
@@ -270,6 +271,14 @@ int flag_next_runtime = 0;
enum tls_model flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC;
+/* Set the default algorithm for the integrated register allocator. */
+
+enum ira_algorithm flag_ira_algorithm = IRA_ALGORITHM_MIXED;
+
+/* Set the default value for -fira-verbose. */
+
+unsigned int flag_ira_verbose = 5;
+
/* Nonzero means change certain warnings into errors.
Usually these are warnings about failure to conform to some standard. */
@@ -2009,6 +2018,7 @@ backend_init (void)
save_register_info ();
/* Initialize the target-specific back end pieces. */
+ ira_init_once ();
backend_init_target ();
}
@@ -2029,9 +2039,10 @@ lang_dependent_init_target (void)
/* Do the target-specific parts of expr initialization. */
init_expr_target ();
- /* Although the actions of init_set_costs are language-independent,
- it uses optabs, so we cannot call it from backend_init. */
+ /* Although the actions of these functions are language-independent,
+ they use optabs, so we cannot call them from backend_init. */
init_set_costs ();
+ ira_init ();
expand_dummy_function_end ();
}
@@ -2132,6 +2143,8 @@ finalize (void)
statistics_fini ();
finish_optimization_passes ();
+ ira_finish_once ();
+
if (mem_report)
dump_memory_report (true);
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/toplev.h ./toplev.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/toplev.h 2008-08-21 21:16:44.000000000 -0400
+++ ./toplev.h 2008-08-22 21:54:04.000000000 -0400
@@ -139,6 +139,12 @@ extern int flag_unroll_all_loops;
extern int flag_unswitch_loops;
extern int flag_cprop_registers;
extern int time_report;
+extern int flag_ira;
+extern int flag_ira_coalesce;
+extern int flag_ira_move_spills;
+extern int flag_ira_propagate_cost;
+extern int flag_ira_share_save_slots;
+extern int flag_ira_share_spill_slots;
/* Things to do with target switches. */
extern void print_version (FILE *, const char *);
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/regs.h ./regs.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/regs.h 2008-08-21 21:16:44.000000000 -0400
+++ ./regs.h 2008-08-22 18:09:59.000000000 -0400
@@ -262,9 +262,27 @@ extern int caller_save_needed;
#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) 0
#endif
+/* 1 if the corresponding class does contain register of given
+ mode. */
+extern char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
+
+typedef unsigned short move_table[N_REG_CLASSES];
+
+/* Maximum cost of moving from a register in one class to a register
+ in another class. */
+extern move_table *move_cost[MAX_MACHINE_MODE];
+
/* Specify number of hard registers given machine mode occupy. */
extern unsigned char hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
+/* Similar, but here we don't have to move if the first index is a
+ subset of the second so in that case the cost is zero. */
+extern move_table *may_move_in_cost[MAX_MACHINE_MODE];
+
+/* Similar, but here we don't have to move if the first index is a
+ superset of the second so in that case the cost is zero. */
+extern move_table *may_move_out_cost[MAX_MACHINE_MODE];
+
/* Return an exclusive upper bound on the registers occupied by hard
register (reg:MODE REGNO). */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/caller-save.c ./caller-save.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/caller-save.c 2008-08-21 21:16:45.000000000 -0400
+++ ./caller-save.c 2008-08-25 11:50:36.000000000 -0400
@@ -1,6 +1,6 @@
/* Save and restore call-clobbered registers which are live across a call.
Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
This file is part of GCC.
@@ -35,9 +35,15 @@ along with GCC; see the file COPYING3.
#include "toplev.h"
#include "tm_p.h"
#include "addresses.h"
+#include "output.h"
+#include "cfgloop.h"
#include "df.h"
#include "ggc.h"
+/* Call used hard registers which can not be saved because there is no
+ insn for this. */
+HARD_REG_SET no_caller_save_reg_set;
+
#ifndef MAX_MOVE_MAX
#define MAX_MOVE_MAX MOVE_MAX
#endif
@@ -62,6 +68,12 @@ static enum machine_mode
static rtx
regno_save_mem[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
+/* The number of elements in the subsequent array. */
+static int save_slots_num;
+
+/* Allocated slots so far. */
+static rtx save_slots[FIRST_PSEUDO_REGISTER];
+
/* We will only make a register eligible for caller-save if it can be
saved in its widest mode with a simple SET insn as long as the memory
address is valid. We record the INSN_CODE is those insns here since
@@ -84,18 +96,55 @@ static int n_regs_saved;
/* Computed by mark_referenced_regs, all regs referenced in a given
insn. */
static HARD_REG_SET referenced_regs;
+/* Computed by mark_referenced_regs, all regs modified in a given insn. */
+static HARD_REG_SET modified_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 calculate_local_save_info (void);
+static bool restore_trans_fun (int);
+static void restore_con_fun_0 (basic_block);
+static void restore_con_fun_n (edge);
+static void calculate_restore_in_out (void);
+static int calculate_restore_here (void);
+static bool save_trans_fun (int);
+static void save_con_fun_0 (basic_block);
+static void save_con_fun_n (edge);
+static void calculate_save_in_out (void);
+static int calculate_save_here (void);
+static bool free_trans_fun (int);
+static void free_con_fun_0 (basic_block);
+static void free_con_fun_n (edge);
+static void calculate_free_in_out (void);
+
+static void make_global_save_analysis (void);
+static void print_annotated_hard_reg_set (FILE *, HARD_REG_SET,
+ unsigned char *, int *);
+static void print_hard_reg_set (FILE *, HARD_REG_SET);
+static void print_save_data (FILE *);
+static void set_hard_reg_saved (HARD_REG_SET, unsigned char *,
+ enum machine_mode *, int *, int *);
static void mark_set_regs (rtx, const_rtx, void *);
-static void mark_referenced_regs (rtx);
+static void add_stored_regs (rtx, const_rtx, void *);
+static void mark_referenced_regs (rtx, int);
static int insert_save (struct insn_chain *, int, int, HARD_REG_SET *,
- enum machine_mode *);
+ enum machine_mode *, int *);
static int insert_restore (struct insn_chain *, int, int, int,
- enum machine_mode *);
+ enum machine_mode *, int *);
static struct insn_chain *insert_one_insn (struct insn_chain *, int, int,
rtx);
static void add_stored_regs (rtx, const_rtx, void *);
+
+
static GTY(()) rtx savepat;
static GTY(()) rtx restpat;
static GTY(()) rtx test_reg;
@@ -180,6 +229,7 @@ init_caller_save (void)
rtx address;
int i, j;
+ CLEAR_HARD_REG_SET (no_caller_save_reg_set);
/* First find all the registers that we need to deal with and all
the modes that they can have. If we can't find a mode to use,
we can't have the register live over calls. */
@@ -217,7 +267,7 @@ init_caller_save (void)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT
(reg_class_contents
- [(int) base_reg_class (regno_save_mode [i][1], PLUS, CONST_INT)], i))
+ [(int) base_reg_class (regno_save_mode[i][1], PLUS, CONST_INT)], i))
break;
gcc_assert (i < FIRST_PSEUDO_REGISTER);
@@ -264,10 +314,14 @@ init_caller_save (void)
{
call_fixed_regs[i] = 1;
SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ if (call_used_regs[i])
+ SET_HARD_REG_BIT (no_caller_save_reg_set, i);
}
}
}
+
+
/* Initialize save areas by showing that we haven't allocated any yet. */
void
@@ -278,6 +332,100 @@ init_save_areas (void)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
for (j = 1; j <= MOVE_MAX_WORDS; j++)
regno_save_mem[i][j] = 0;
+ save_slots_num = 0;
+
+}
+
+/* The structure represents a hard register which should be saved
+ through the call. It is used when the integrated register
+ allocator (IRA) is used and sharing save slots is on. */
+struct saved_hard_reg
+{
+ /* Order number starting with 0. */
+ int num;
+ /* The hard regno. */
+ int hard_regno;
+ /* Execution frequency of all calls through which given hard
+ register should be saved. */
+ int call_freq;
+ /* Stack slot reserved to save the hard register through calls. */
+ rtx slot;
+ /* True if it is first hard register in the chain of hard registers
+ sharing the same stack slot. */
+ int first_p;
+ /* Order number of the next hard register structure with the same
+ slot in the chain. -1 represents end of the chain. */
+ int next;
+};
+
+/* Map: hard register number to the corresponding structure. */
+static struct saved_hard_reg *hard_reg_map[FIRST_PSEUDO_REGISTER];
+
+/* The number of all structures representing hard registers should be
+ saved, in order words, the number of used elements in the following
+ array. */
+static int saved_regs_num;
+
+/* Pointers to all the structures. Index is the order number of the
+ corresponding structure. */
+static struct saved_hard_reg *all_saved_regs[FIRST_PSEUDO_REGISTER];
+
+/* First called function for work with saved hard registers. */
+static void
+initiate_saved_hard_regs (void)
+{
+ int i;
+
+ saved_regs_num = 0;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ hard_reg_map[i] = NULL;
+}
+
+/* Allocate and return new saved hard register with given REGNO and
+ CALL_FREQ. */
+static struct saved_hard_reg *
+new_saved_hard_reg (int regno, int call_freq)
+{
+ struct saved_hard_reg *saved_reg;
+
+ saved_reg
+ = (struct saved_hard_reg *) xmalloc (sizeof (struct saved_hard_reg));
+ hard_reg_map[regno] = all_saved_regs[saved_regs_num] = saved_reg;
+ saved_reg->num = saved_regs_num++;
+ saved_reg->hard_regno = regno;
+ saved_reg->call_freq = call_freq;
+ saved_reg->first_p = FALSE;
+ saved_reg->next = -1;
+ return saved_reg;
+}
+
+/* Free memory allocated for the saved hard registers. */
+static void
+finish_saved_hard_regs (void)
+{
+ int i;
+
+ for (i = 0; i < saved_regs_num; i++)
+ free (all_saved_regs[i]);
+}
+
+/* The function is used to sort the saved hard register structures
+ according their frequency. */
+static int
+saved_hard_reg_compare_func (const void *v1p, const void *v2p)
+{
+ const struct saved_hard_reg *p1 = *(struct saved_hard_reg * const *) v1p;
+ const struct saved_hard_reg *p2 = *(struct saved_hard_reg * const *) v2p;
+
+ if (flag_omit_frame_pointer)
+ {
+ if (p1->call_freq - p2->call_freq != 0)
+ return p1->call_freq - p2->call_freq;
+ }
+ else if (p2->call_freq - p1->call_freq != 0)
+ return p2->call_freq - p1->call_freq;
+
+ return p1->num - p2->num;
}
/* Allocate save areas for any hard registers that might need saving.
@@ -286,6 +434,10 @@ init_save_areas (void)
overestimate slightly (especially if some of these registers are later
used as spill registers), but it should not be significant.
+ For IRA we use priority coloring to decrease stack slots needed for
+ saving hard registers through calls. We build conflicts for them
+ to do coloring.
+
Future work:
In the fallback case we should iterate backwards across all possible
@@ -317,65 +469,297 @@ setup_save_areas (void)
unsigned int regno = reg_renumber[i];
unsigned int endregno
= end_hard_regno (GET_MODE (regno_reg_rtx[i]), regno);
-
for (r = regno; r < endregno; r++)
if (call_used_regs[r])
SET_HARD_REG_BIT (hard_regs_used, r);
}
- /* Now run through all the call-used hard-registers and allocate
- space for them in the caller-save area. Try to allocate space
- in a manner which allows multi-register saves/restores to be done. */
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- for (j = MOVE_MAX_WORDS; j > 0; j--)
- {
- int do_save = 1;
+ if (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)
+ {
+ 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);
+ }
+ }
+ }
+ /* 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;
- /* 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);
- }
+ 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++)
+ {
+ saved_reg = call_saved_regs[i];
+ for (j = 0; j < call_saved_regs_num; j++)
+ if (i != j)
+ {
+ saved_reg2 = call_saved_regs[j];
+ saved_reg_conflicts[saved_reg->num * saved_regs_num
+ + saved_reg2->num]
+ = saved_reg_conflicts[saved_reg2->num * saved_regs_num
+ + saved_reg->num]
+ = TRUE;
+ }
+ }
+ }
+ /* Sort saved hard regs. */
+ qsort (all_saved_regs, saved_regs_num, sizeof (struct saved_hard_reg *),
+ saved_hard_reg_compare_func);
+ /* Initiate slots available from the previous reload
+ iteration. */
+ prev_save_slots_num = save_slots_num;
+ memcpy (prev_save_slots, save_slots, save_slots_num * sizeof (rtx));
+ save_slots_num = 0;
+ /* Allocate stack slots for the saved hard registers. */
+ for (i = 0; i < saved_regs_num; i++)
+ {
+ saved_reg = all_saved_regs[i];
+ regno = saved_reg->hard_regno;
+ for (j = 0; j < i; j++)
+ {
+ saved_reg2 = all_saved_regs[j];
+ if (! saved_reg2->first_p)
+ continue;
+ slot = saved_reg2->slot;
+ for (k = j; k >= 0; k = next_k)
+ {
+ saved_reg3 = all_saved_regs[k];
+ next_k = saved_reg3->next;
+ if (saved_reg_conflicts[saved_reg->num * saved_regs_num
+ + saved_reg3->num])
+ break;
+ }
+ if (k < 0
+ && (GET_MODE_SIZE (regno_save_mode[regno][1])
+ <= GET_MODE_SIZE (regno_save_mode
+ [saved_reg2->hard_regno][1])))
+ {
+ saved_reg->slot
+ = adjust_address_nv
+ (slot, regno_save_mode[saved_reg->hard_regno][1], 0);
+ regno_save_mem[regno][1] = saved_reg->slot;
+ saved_reg->next = saved_reg2->next;
+ saved_reg2->next = i;
+ if (dump_file != NULL)
+ fprintf (dump_file, "%d uses slot of %d\n",
+ regno, saved_reg2->hard_regno);
+ break;
+ }
+ }
+ if (j == i)
+ {
+ saved_reg->first_p = TRUE;
+ for (best_slot_num = -1, j = 0; j < prev_save_slots_num; j++)
+ {
+ slot = prev_save_slots[j];
+ if (slot == NULL_RTX)
+ continue;
+ if (GET_MODE_SIZE (regno_save_mode[regno][1])
+ <= GET_MODE_SIZE (GET_MODE (slot))
+ && best_slot_num < 0)
+ best_slot_num = j;
+ if (GET_MODE (slot) == regno_save_mode[regno][1])
+ break;
+ }
+ if (best_slot_num >= 0)
+ {
+ saved_reg->slot = prev_save_slots[best_slot_num];
+ saved_reg->slot
+ = adjust_address_nv
+ (saved_reg->slot,
+ regno_save_mode[saved_reg->hard_regno][1], 0);
+ if (dump_file != NULL)
+ fprintf (dump_file,
+ "%d uses a slot from prev iteration\n", regno);
+ prev_save_slots[best_slot_num] = NULL_RTX;
+ if (best_slot_num + 1 == prev_save_slots_num)
+ prev_save_slots_num--;
+ }
+ else
+ {
+ saved_reg->slot
+ = assign_stack_local_1
+ (regno_save_mode[regno][1],
+ GET_MODE_SIZE (regno_save_mode[regno][1]), 0, true);
+ if (dump_file != NULL)
+ fprintf (dump_file, "%d uses a new slot\n", regno);
+ }
+ regno_save_mem[regno][1] = saved_reg->slot;
+ save_slots[save_slots_num++] = saved_reg->slot;
+ }
+ }
+ free (saved_reg_conflicts);
+ finish_saved_hard_regs ();
+ }
+ else
+ {
+ /* Now run through all the call-used hard-registers and allocate
+ space for them in the caller-save area. Try to allocate space
+ in a manner which allows multi-register saves/restores to be done. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = MOVE_MAX_WORDS; j > 0; j--)
+ {
+ int do_save = 1;
+
+ /* If no mode exists for this size, try another. Also break out
+ if we have already saved this hard register. */
+ if (regno_save_mode[i][j] == VOIDmode || regno_save_mem[i][1] != 0)
+ continue;
+
+ /* See if any register in this group has been saved. */
+ for (k = 0; k < j; k++)
+ if (regno_save_mem[i + k][1])
+ {
+ do_save = 0;
+ break;
+ }
+ if (! do_save)
+ continue;
+
+ for (k = 0; k < j; k++)
+ if (! TEST_HARD_REG_BIT (hard_regs_used, i + k))
+ {
+ do_save = 0;
+ break;
+ }
+ if (! do_save)
+ continue;
+
+ /* We have found an acceptable mode to store in. Since
+ hard register is always saved in the widest mode
+ available, the mode may be wider than necessary, it is
+ OK to reduce the alignment of spill space. We will
+ verify that it is equal to or greater than required
+ when we restore and save the hard register in
+ insert_restore and insert_save. */
+ regno_save_mem[i][j]
+ = assign_stack_local_1 (regno_save_mode[i][j],
+ GET_MODE_SIZE (regno_save_mode[i][j]),
+ 0, true);
+
+ /* Setup single word save area just in case... */
+ for (k = 0; k < j; k++)
+ /* This should not depend on WORDS_BIG_ENDIAN.
+ The order of words in regs is the same as in memory. */
+ regno_save_mem[i + k][1]
+ = adjust_address_nv (regno_save_mem[i][j],
+ regno_save_mode[i + k][1],
+ k * UNITS_PER_WORD);
+ }
+ }
/* Now loop again and set the alias set of any save areas we made to
the alias set used to represent frame objects. */
@@ -384,61 +768,1033 @@ setup_save_areas (void)
if (regno_save_mem[i][j] != 0)
set_mem_alias_set (regno_save_mem[i][j], get_frame_alias_set ());
}
+
+
+
+/* This page contains code for global analysis for better placement
+ save/restore insn when IRA is used.
+
+ First, we calculate local BB info in function calculate_local_save_info:
+ o hard registers set/mentioned in basic blocks (they prevents
+ moving corresponding saves/restores further up/down in CFG) --
+ see comments for save_kill/restore_kill.
+ o hard registers needed to be saved/restored around calls and
+ which are not set/mentioned before/after the call in the basic
+ block -- see comments for save_gen/restore_gen.
+ o free hard registers at the basic block start -- see comments
+ for free_gen. This set can be different from save_gen. For
+ example, in the following situation
+
+ BB:
+ ...
+ insn using R
+ ...
+ call through which R live through and should be saved/restored.
+ ...
+
+ save_gen contains R because we can put save at the BB start but
+ free_gen does not contain R because we can not use R for other
+ purposes from the BB start till the insn using R.
+
+ Second, we calculate the global info for placement saves/restores
+ in functions -- see comments for save_in, save_out, restore_in,
+ restore_out, free_in, free_out.
+
+ The equations for the global info calculation are not trivial
+ because we can not put saves/restores on CFG edges as the reload
+ can not work when new BBs added. For example, it means that all
+ save_out set of BB should be a subset of intersections of save_in
+ of all successors of the BB. Preventing putting the code on edges
+ makes data flow problem bidirectional. The data flow functions are
+ also complicated by necessity to propagate saving modes (e.g. x86
+ fp register can be saved in XFmode or DFmode).
+
+ The equations do not permit to propagate info into the loops
+ because we can not put saves/restores in more frequently executed
+ points. But if the loop does not contains references for a hard
+ register, we permit to propagate info for the register into the
+ loop because the info will be propagated through all the loop and
+ as consequence corresponding saves/restores will be moved through
+ the loop.
+
+ Third, using this info we place saves/restores and set up member
+ `saved' in function save_call_clobbered_regs.
+
+ */
+
+/* The following structure contains basic block data flow information
+ used to optimize save/restore placement. Data flow equations are
+ bidirectional because we don't want to put save/restore code on CFG
+ edges. */
+struct bb_info
+{
+ /* True if save_out/restore_in should be empty for this block. */
+ int empty_save_out_p, empty_restore_in_p;
+ /* Hard registers set/mentioned in the BB. */
+ HARD_REG_SET save_kill;
+ HARD_REG_SET restore_kill;
+ /* Hard registers needed to be saved and this save not killed (see above)
+ by an insn in the BB before that. */
+ HARD_REG_SET save_gen;
+ /* Hard registers needed to be restored and this restore not killed
+ by an insn in the BB after that. */
+ HARD_REG_SET restore_gen;
+ /* Hard registers free and not killed by an insn in the BB before
+ that. */
+ HARD_REG_SET free_gen;
+ /* Registers needed to be saved/restored at the start and end of the
+ basic block. */
+ HARD_REG_SET save_in, save_out, restore_in, restore_out;
+ /* Registers free at the start and end of the basic block. */
+ HARD_REG_SET free_in, free_out;
+ /* Hard registers living at the start/end of the basic block. */
+ HARD_REG_SET live_at_start, live_at_end;
+ /* We don't want to generate save/restore insns on edges because it
+ changes CFG during the reload. To prevent this we use the
+ following set. This set defines what hard registers should be
+ saved/restored at the start/end of basic block. */
+ HARD_REG_SET save_here, restore_here;
+ /* It corresponds to set SAVE_GEN right after the call of
+ calculate_local_save_info. */
+ unsigned char save_in_mode[FIRST_PSEUDO_REGISTER];
+ /* Saving modes at the end of the basic block. */
+ unsigned char save_out_mode[FIRST_PSEUDO_REGISTER];
+ /* Restoring modes at the start of the basic block. */
+ unsigned char restore_in_mode[FIRST_PSEUDO_REGISTER];
+ /* It corresponds to set RESTORE_GEN right after the call of
+ calculate_local_save_info. */
+ unsigned char restore_out_mode[FIRST_PSEUDO_REGISTER];
+ /* Analogous but the corresponding pseudo-register numbers. */
+ int save_in_pseudo[FIRST_PSEUDO_REGISTER];
+ int save_out_pseudo[FIRST_PSEUDO_REGISTER];
+ int restore_in_pseudo[FIRST_PSEUDO_REGISTER];
+ int restore_out_pseudo[FIRST_PSEUDO_REGISTER];
+};
+
+/* Macros for accessing data flow information of basic blocks. */
+#define BB_INFO(BB) ((struct bb_info *) (BB)->aux)
+#define BB_INFO_BY_INDEX(N) BB_INFO (BASIC_BLOCK(N))
+
+/* The following structure contains loop info necessary for
+ save/restore placement optimization. */
+struct loop_info
+{
+ /* All hard registers mentioned in the loop. If a pseudo is
+ mentioned in the loop, the hard registers assigned to it are also
+ believed to be mentioned in the loop. */
+ HARD_REG_SET mentioned_regs;
+};
+
+/* Macro for accessing data flow information of LOOP. */
+#define LOOP_INFO(LOOP) ((struct loop_info *) (LOOP)->aux)
+
+/* The function calculates sets KILL, GEN, LIVE_AT_START and
+ RESTORE_OUT_MODES corresponding to GEN for basic blocks. */
+static void
+calculate_local_save_info (void)
+{
+ int i, empty_save_out_p, empty_restore_in_p;
+ struct insn_chain *chain, *next;
+ struct bb_info *bb_info;
+ /* Computed in mark_set_regs, holds all registers set by the current
+ instruction. */
+ HARD_REG_SET save_kill, restore_kill;
+ HARD_REG_SET this_insn_sets, save_gen, restore_gen, free_gen, temp_set;
+ unsigned regno;
+ reg_set_iterator rsi;
+ /* A map: hard register being saved to the mode used for its
+ saving. */
+ enum machine_mode save_mode[FIRST_PSEUDO_REGISTER];
+ /* A map: hard register being saved to the pseudo-register assigned
+ to the hard register at given point. */
+ int save_pseudo[FIRST_PSEUDO_REGISTER];
+
+ CLEAR_HARD_REG_SET (save_gen);
+ CLEAR_HARD_REG_SET (restore_gen);
+ CLEAR_HARD_REG_SET (free_gen);
+ CLEAR_HARD_REG_SET (save_kill);
+ CLEAR_HARD_REG_SET (restore_kill);
+ empty_save_out_p = FALSE;
+ empty_restore_in_p = FALSE;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ save_mode[i] = VOIDmode;
+ save_pseudo[i] = -1;
+ }
+
+ for (chain = reload_insn_chain; chain != 0; chain = next)
+ {
+ rtx insn = chain->insn;
+ enum rtx_code code = GET_CODE (insn);
+
+ next = chain->next;
+
+ bb_info = BB_INFO_BY_INDEX (chain->block);
+ if (INSN_P (insn))
+ {
+ CLEAR_HARD_REG_SET (referenced_regs);
+ CLEAR_HARD_REG_SET (modified_regs);
+ mark_referenced_regs (PATTERN (insn), FALSE);
+ AND_COMPL_HARD_REG_SET (restore_gen, referenced_regs);
+ IOR_HARD_REG_SET (restore_kill, referenced_regs);
+ IOR_HARD_REG_SET (save_kill, modified_regs);
+
+ if (code == CALL_INSN && find_reg_note (insn, REG_NORETURN, NULL))
+ {
+ SET_HARD_REG_SET (save_kill);
+ SET_HARD_REG_SET (restore_kill);
+ }
+ else if (code == CALL_INSN)
+ {
+ HARD_REG_SET hard_regs_to_save, used_regs;
+
+ /* Use the register life information in CHAIN to compute
+ which regs are live during the call. */
+ REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+ &chain->live_throughout);
+ /* Look through all live pseudos, mark their hard registers
+ and choose proper mode for saving. */
+ EXECUTE_IF_SET_IN_REG_SET
+ (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi)
+ {
+ int r = reg_renumber[regno];
+ int nregs;
+ enum machine_mode mode;
+
+ /* Remember live_throughout can contain spilled
+ registers when IRA is used. */
+ if (flag_ira && optimize && r < 0)
+ continue;
+ gcc_assert (r >= 0);
+
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ mode = HARD_REGNO_CALLER_SAVE_MODE
+ (r, nregs, PSEUDO_REGNO_MODE (regno));
+ if (nregs == 1)
+ {
+ SET_HARD_REG_BIT (hard_regs_to_save, r);
+ save_mode[r] = mode;
+ save_pseudo[r] = regno;
+ }
+ else
+ {
+ while (nregs-- > 0)
+ {
+ SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+ save_mode[r + nregs]
+ = regno_save_mode[r + nregs][1];
+ save_pseudo[r + nregs] = regno;
+ }
+ }
+ }
+
+ /* 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);
+
+ /* Compute which hard regs must be saved before this call. */
+ AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
+ AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets);
+ COPY_HARD_REG_SET (used_regs, call_used_reg_set);
+ AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+ IOR_HARD_REG_SET (restore_gen, hard_regs_to_save);
+ COPY_HARD_REG_SET (temp_set, hard_regs_to_save);
+ AND_COMPL_HARD_REG_SET (temp_set, save_kill);
+ AND_COMPL_HARD_REG_SET (temp_set, save_gen);
+ IOR_HARD_REG_SET (save_gen, temp_set);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (temp_set, i))
+ {
+ bb_info->save_in_mode[i] = save_mode[i];
+ bb_info->save_in_pseudo[i] = save_pseudo[i];
+ }
+ COPY_HARD_REG_SET (temp_set, hard_regs_to_save);
+ AND_COMPL_HARD_REG_SET (temp_set, save_kill);
+ AND_COMPL_HARD_REG_SET (temp_set, restore_kill);
+ IOR_HARD_REG_SET (free_gen, temp_set);
+ }
+ }
+
+ if (chain->next == 0 || chain->next->block != chain->block)
+ {
+ basic_block bb = BASIC_BLOCK (chain->block);
+ edge e;
+ edge_iterator ei;
+ loop_p loop;
+
+ if (! empty_save_out_p)
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & EDGE_ABNORMAL)
+ {
+ empty_save_out_p = TRUE;
+ break;
+ }
+ bb_info->empty_save_out_p = empty_save_out_p;
+ empty_save_out_p = FALSE;
+ if (! empty_restore_in_p)
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (e->flags & EDGE_ABNORMAL)
+ {
+ empty_restore_in_p = TRUE;
+ break;
+ }
+ bb_info->empty_restore_in_p = empty_restore_in_p;
+ empty_restore_in_p = FALSE;
+ COPY_HARD_REG_SET (bb_info->save_gen, save_gen);
+ COPY_HARD_REG_SET (bb_info->restore_gen, restore_gen);
+ COPY_HARD_REG_SET (bb_info->free_gen, free_gen);
+ CLEAR_HARD_REG_SET (bb_info->restore_in);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ bb_info->save_out_mode[i] = VOIDmode;
+ bb_info->save_out_pseudo[i] = -1;
+ if (!TEST_HARD_REG_BIT (save_gen, i))
+ {
+ bb_info->save_in_mode[i] = VOIDmode;
+ bb_info->save_in_pseudo[i] = -1;
+ }
+ bb_info->restore_in_mode[i] = VOIDmode;
+ bb_info->restore_in_pseudo[i] = -1;
+ if (TEST_HARD_REG_BIT (restore_gen, i))
+ {
+ bb_info->restore_out_mode[i] = save_mode[i];
+ bb_info->restore_out_pseudo[i] = save_pseudo[i];
+ }
+ else
+ {
+ bb_info->restore_out_mode[i] = VOIDmode;
+ bb_info->restore_out_pseudo[i] = -1;
+ }
+ }
+ COPY_HARD_REG_SET (bb_info->save_kill, save_kill);
+ COPY_HARD_REG_SET (bb_info->restore_kill, restore_kill);
+ for (loop = bb->loop_father;
+ loop != current_loops->tree_root;
+ loop = loop_outer (loop))
+ {
+ struct loop_info *loop_info = LOOP_INFO (loop);
+
+ IOR_HARD_REG_SET (loop_info->mentioned_regs, save_kill);
+ IOR_HARD_REG_SET (loop_info->mentioned_regs, restore_kill);
+ }
+ CLEAR_HARD_REG_SET (bb_info->save_in);
+ CLEAR_HARD_REG_SET (bb_info->restore_out);
+ CLEAR_HARD_REG_SET (bb_info->free_in);
+ /* We don't use LIVE for IRA. */
+ REG_SET_TO_HARD_REG_SET
+ (bb_info->live_at_end,
+ flag_ira ? DF_LR_OUT (bb) : DF_LIVE_OUT (bb));
+ EXECUTE_IF_SET_IN_REG_SET
+ ((flag_ira ? DF_LR_OUT (bb) : DF_LIVE_OUT (bb)),
+ FIRST_PSEUDO_REGISTER, regno, rsi)
+ {
+ int r = reg_renumber[regno];
+ int nregs;
+
+ if (r < 0)
+ continue;
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ while (nregs-- > 0)
+ SET_HARD_REG_BIT (bb_info->live_at_end, r + nregs);
+ }
+ REG_SET_TO_HARD_REG_SET
+ (bb_info->live_at_start,
+ flag_ira ? DF_LR_IN (bb) : DF_LIVE_IN (bb));
+ EXECUTE_IF_SET_IN_REG_SET
+ ((flag_ira ? DF_LR_IN (bb) : DF_LIVE_IN (bb)),
+ FIRST_PSEUDO_REGISTER, regno, rsi)
+ {
+ int r = reg_renumber[regno];
+ int nregs;
+
+ if (r < 0)
+ continue;
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ while (nregs-- > 0)
+ SET_HARD_REG_BIT (bb_info->live_at_start, r + nregs);
+ }
+ CLEAR_HARD_REG_SET (bb_info->save_here);
+ CLEAR_HARD_REG_SET (bb_info->restore_here);
+ CLEAR_HARD_REG_SET (save_gen);
+ CLEAR_HARD_REG_SET (restore_gen);
+ CLEAR_HARD_REG_SET (free_gen);
+ CLEAR_HARD_REG_SET (save_kill);
+ CLEAR_HARD_REG_SET (restore_kill);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ save_mode[i] = VOIDmode;
+ save_pseudo[i] = -1;
+ }
+ }
+ }
+}
+
+/* The transfer function used by the DF equation solver to propagate restore
+ info through block with BB_INDEX according to the following
+ equation:
+
+ bb.restore_out = (bb.restore_in - bb.restore_kill) U bb.restore_gen.
+
+ The function also propagates saving modes of the hard
+ registers. */
+static bool
+restore_trans_fun (int bb_index)
+{
+ int i;
+ HARD_REG_SET temp_set;
+ struct bb_info *bb_info = BB_INFO_BY_INDEX (bb_index);
+
+ COPY_HARD_REG_SET (temp_set, bb_info->restore_in);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->restore_kill);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (temp_set, i)
+ && ! TEST_HARD_REG_BIT (bb_info->restore_gen, i))
+ {
+ gcc_assert (bb_info->restore_out_mode[i] == VOIDmode
+ || ((bb_info->restore_out_mode[i]
+ == bb_info->restore_in_mode[i])
+ && (bb_info->restore_out_pseudo[i]
+ == bb_info->restore_in_pseudo[i])));
+ bb_info->restore_out_mode[i] = bb_info->restore_in_mode[i];
+ bb_info->restore_out_pseudo[i] = bb_info->restore_in_pseudo[i];
+ }
+ IOR_HARD_REG_SET (temp_set, bb_info->restore_gen);
+ if (! hard_reg_set_equal_p (temp_set, bb_info->restore_out))
+ {
+ COPY_HARD_REG_SET (bb_info->restore_out, temp_set);
+ return true;
+ }
+ return false;
+}
+
+/* The confluence function used by the DF equation solver to set up restore info
+ for a block BB without predecessor. */
+static void
+restore_con_fun_0 (basic_block bb)
+{
+ CLEAR_HARD_REG_SET (BB_INFO (bb)->restore_in);
+}
+
+/* The confluence function used by the DF equation solver to propagate
+ restore info from predecessor to successor on edge E (pred->bb)
+ according to the following equation:
+
+ bb.restore_in = empty for entry block or one with empty_restore_in_p
+ | ^(pred.restore_out - pred.restore_here) ^ bb.live_at_start
+
+ If the edge is a loop enter we do not propagate the info because we
+ don't want to put restores in more frequently executed places.
+
+ */
+static void
+restore_con_fun_n (edge e)
+{
+ int i;
+ HARD_REG_SET temp_set;
+ basic_block pred = e->src;
+ basic_block bb = e->dest;
+ struct bb_info *bb_info = BB_INFO (bb);
+
+ if (bb_info->empty_restore_in_p)
+ return;
+
+ COPY_HARD_REG_SET (temp_set, BB_INFO (pred)->restore_out);
+ AND_COMPL_HARD_REG_SET (temp_set, BB_INFO (pred)->restore_here);
+ if (bb->loop_depth > pred->loop_depth)
+ AND_COMPL_HARD_REG_SET (temp_set,
+ LOOP_INFO (bb->loop_father)->mentioned_regs);
+ AND_HARD_REG_SET (temp_set, bb_info->live_at_start);
+ if (EDGE_PRED (bb, 0) == e)
+ COPY_HARD_REG_SET (bb_info->restore_in, temp_set);
+ else
+ AND_HARD_REG_SET (bb_info->restore_in, temp_set);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (temp_set, i))
+ {
+ if (bb_info->restore_in_mode[i] != VOIDmode
+ && BB_INFO (pred)->restore_out_mode[i] != VOIDmode
+ && (bb_info->restore_in_mode[i]
+ != BB_INFO (pred)->restore_out_mode[i]
+ || bb_info->restore_in_pseudo[i]
+ != BB_INFO (pred)->restore_out_pseudo[i]))
+ CLEAR_HARD_REG_BIT (bb_info->restore_in, i);
+ else if (BB_INFO (pred)->restore_out_mode[i] != VOIDmode)
+ {
+ bb_info->restore_in_mode[i]
+ = BB_INFO (pred)->restore_out_mode[i];
+ bb_info->restore_in_pseudo[i]
+ = BB_INFO (pred)->restore_out_pseudo[i];
+ }
+ }
+}
+
+/* Basic blocks for data flow problem -- all bocks except the special
+ ones. */
+static bitmap all_blocks;
+
+/* The function calculates global restore information according
+ to the following equations:
+
+ bb.restore_in = empty for entry block or one with empty_restore_in_p
+ | ^(pred.restore_out - pred.restore_here) ^ bb.live_at_start
+ bb.restore_out = (bb.restore_in - bb.restore_kill) U bb.restore_gen.
+
+ The function also calculates restore_in_mode and restore_out_mode.
+*/
+static void
+calculate_restore_in_out (void)
+{
+ df_simple_dataflow (DF_FORWARD, NULL, restore_con_fun_0, restore_con_fun_n,
+ restore_trans_fun, all_blocks,
+ df_get_postorder (DF_FORWARD),
+ df_get_n_blocks (DF_FORWARD));
+}
+
+/* The function calculates RESTORE_HERE according to the equation
+ bb.restore_here = U ((bb.restore_out - succ.restore_in)
+ ^ succ.live_at_start). */
+static int
+calculate_restore_here (void)
+{
+ basic_block bb;
+ edge e;
+ edge_iterator ei;
+ HARD_REG_SET restore_here, temp_set;
+ int changed_p = FALSE;
+
+ FOR_EACH_BB (bb)
+ {
+ CLEAR_HARD_REG_SET (restore_here);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ basic_block dest = e->dest;
+
+ COPY_HARD_REG_SET (temp_set, BB_INFO (bb)->restore_out);
+ AND_COMPL_HARD_REG_SET (temp_set, BB_INFO (dest)->restore_in);
+ AND_HARD_REG_SET (temp_set, BB_INFO (dest)->live_at_start);
+ IOR_HARD_REG_SET (restore_here, temp_set);
+ }
+
+ if (! hard_reg_set_equal_p (restore_here, BB_INFO (bb)->restore_here))
+ {
+ COPY_HARD_REG_SET (BB_INFO (bb)->restore_here, restore_here);
+ changed_p = TRUE;
+ }
+ }
+ return changed_p;
+}
+
+/* The transfer function used by the DF equation solver to propagate save info
+ through block with BB_INDEX according to the following
+ equation:
+
+ bb.save_in = ((bb.save_out - bb.save_kill) U bb.save_gen) - bb.restore_in.
+
+ The function also propagates saving modes of the hard
+ registers. */
+
+static bool
+save_trans_fun (int bb_index)
+{
+ int i;
+ HARD_REG_SET temp_set;
+ struct bb_info *bb_info = BB_INFO_BY_INDEX (bb_index);
+
+ COPY_HARD_REG_SET (temp_set, bb_info->save_out);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->save_kill);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (temp_set, i)
+ && ! TEST_HARD_REG_BIT (bb_info->save_gen, i))
+ {
+ gcc_assert (bb_info->save_in_mode[i] == VOIDmode
+ || ((bb_info->save_in_mode[i]
+ == bb_info->save_out_mode[i])
+ && (bb_info->save_in_pseudo[i]
+ == bb_info->save_out_pseudo[i])));
+ bb_info->save_in_mode[i] = bb_info->save_out_mode[i];
+ bb_info->save_in_pseudo[i] = bb_info->save_out_pseudo[i];
+ }
+ IOR_HARD_REG_SET (temp_set, bb_info->save_gen);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->restore_in);
+ if (! hard_reg_set_equal_p (temp_set, bb_info->save_in))
+ {
+ COPY_HARD_REG_SET (bb_info->save_in, temp_set);
+ return true;
+ }
+ return false;
+}
+
+/* The confluence function used by the DF equation solver to set up
+ save info for a block BB without successor. */
+static void
+save_con_fun_0 (basic_block bb)
+{
+ CLEAR_HARD_REG_SET (BB_INFO (bb)->save_out);
+}
+
+/* The confluence function used by the DF equation solver to propagate
+ save info from successor to predecessor on edge E (bb->succ)
+ according to the following equation:
+
+ bb.save_out = empty for exit block or one with empty_save_out_p
+ | ^(succ.save_in - succ.save_here) ^ bb.live_at_end
+
+ If the edge is a loop exit we do not propagate the info because we
+ don't want to put saves in more frequently executed places.
+*/
+static void
+save_con_fun_n (edge e)
+{
+ int i;
+ HARD_REG_SET temp_set;
+ basic_block succ = e->dest;
+ basic_block bb = e->src;
+ struct bb_info *bb_info = BB_INFO (bb);
+
+ if (bb_info->empty_save_out_p)
+ return;
+
+ COPY_HARD_REG_SET (temp_set, BB_INFO (succ)->save_in);
+ AND_COMPL_HARD_REG_SET (temp_set, BB_INFO (succ)->save_here);
+ if (bb->loop_depth > succ->loop_depth)
+ AND_COMPL_HARD_REG_SET (temp_set,
+ LOOP_INFO (bb->loop_father)->mentioned_regs);
+ AND_HARD_REG_SET (temp_set, bb_info->live_at_end);
+ if (EDGE_SUCC (bb, 0) == e)
+ COPY_HARD_REG_SET (bb_info->save_out, temp_set);
+ else
+ AND_HARD_REG_SET (bb_info->save_out, temp_set);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (temp_set, i))
+ {
+ if (bb_info->save_out_mode[i] != VOIDmode
+ && BB_INFO (succ)->save_in_mode[i] != VOIDmode
+ && ((bb_info->save_out_mode[i]
+ != BB_INFO (succ)->save_in_mode[i])
+ || (bb_info->save_out_pseudo[i]
+ != BB_INFO (succ)->save_in_pseudo[i])))
+ CLEAR_HARD_REG_BIT (bb_info->save_out, i);
+ else if (BB_INFO (succ)->save_in_mode[i] != VOIDmode)
+ {
+ bb_info->save_out_mode[i]
+ = BB_INFO (succ)->save_in_mode[i];
+ bb_info->save_out_pseudo[i]
+ = BB_INFO (succ)->save_in_pseudo[i];
+ }
+ }
+}
+
+/* The transfer function calculates global save information according
+ to the following equations:
+
+ bb.save_out = empty for exit block or one with empty_save_out_p
+ | ^(succ.save_in - succ.save_here) ^ bb.live_at_end
+ bb.save_in = ((bb.save_out - bb.save_kill) U bb.save_gen) - bb.restore_in.
+
+ The function also calculates save_in_mode and save_out_mode.
+*/
+static void
+calculate_save_in_out (void)
+{
+ df_simple_dataflow (DF_BACKWARD, NULL, save_con_fun_0, save_con_fun_n,
+ save_trans_fun, all_blocks,
+ df_get_postorder (DF_BACKWARD),
+ df_get_n_blocks (DF_BACKWARD));
+}
+
+/* The function calculates SAVE_HERE according to the equation
+ bb.save_here = U ((bb.save_in - pred.save_out) ^ pred.live_at_end). */
+static int
+calculate_save_here (void)
+{
+ basic_block bb;
+ edge e;
+ edge_iterator ei;
+ HARD_REG_SET save_here, temp_set;
+ int changed_p = FALSE;
+
+ FOR_EACH_BB (bb)
+ {
+ CLEAR_HARD_REG_SET (save_here);
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ basic_block src = e->src;
+
+ COPY_HARD_REG_SET (temp_set, BB_INFO (bb)->save_in);
+ AND_COMPL_HARD_REG_SET (temp_set, BB_INFO (src)->save_out);
+ AND_HARD_REG_SET (temp_set, BB_INFO (src)->live_at_end);
+ IOR_HARD_REG_SET (save_here, temp_set);
+ }
+
+ if (! hard_reg_set_equal_p (save_here, BB_INFO (bb)->save_here))
+ {
+ COPY_HARD_REG_SET (BB_INFO (bb)->save_here, save_here);
+ changed_p = TRUE;
+ }
+ }
+ return changed_p;
+}
+
+/* The transfer function used by the DF equation solver to propagate
+ free info through block with BB_INDEX according to the following
+ equation:
+
+ bb.free_in = ((bb.free_out - bb.save_kill - bb.restore_kill) U bb.free_gen)
+ - bb.save_here)
+
+*/
+static bool
+free_trans_fun (int bb_index)
+{
+ HARD_REG_SET temp_set;
+ struct bb_info *bb_info = BB_INFO_BY_INDEX (bb_index);
+
+ COPY_HARD_REG_SET (temp_set, bb_info->free_out);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->save_kill);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->restore_kill);
+ IOR_HARD_REG_SET (temp_set, bb_info->free_gen);
+ AND_COMPL_HARD_REG_SET (temp_set, bb_info->save_here);
+ if (! hard_reg_set_equal_p (temp_set, bb_info->free_in))
+ {
+ COPY_HARD_REG_SET (bb_info->free_in, temp_set);
+ return true;
+ }
+ return false;
+}
+
+/* The confluence function used by the DF equation solver to set up
+ free info for a block BB without successor. */
+static void
+free_con_fun_0 (basic_block bb)
+{
+ CLEAR_HARD_REG_SET (BB_INFO (bb)->free_out);
+}
+
+/* The confluence function used by the DF equation solver to propagate
+ free info from successor to predecessor on edge E (bb->succ)
+ according to the following equation:
+
+ bb.free_out = ^(succ.free_in ^ bb.live_at_end)
+*/
+static void
+free_con_fun_n (edge e)
+{
+ HARD_REG_SET temp_set;
+ basic_block succ = e->dest;
+ basic_block bb = e->src;
+ struct bb_info *bb_info = BB_INFO (bb);
+
+ COPY_HARD_REG_SET (temp_set, BB_INFO (succ)->free_in);
+ if (EDGE_SUCC (bb, 0) == e)
+ COPY_HARD_REG_SET (bb_info->free_out, temp_set);
+ else
+ AND_HARD_REG_SET (bb_info->free_out, temp_set);
+ AND_HARD_REG_SET (bb_info->free_out, bb_info->live_at_end);
+}
+
+/* The function calculates global free information according
+ to the following equations:
+
+ bb.free_out = ^(succ.free_in ^ bb.live_at_end)
+ bb.free_in = ((bb.free_out - bb.save_kill - bb.restore_kill) U bb.free_gen)
+ - bb.save_here)
+
+ The function also calculates free_in_mode and free_out_mode.
+*/
+static void
+calculate_free_in_out (void)
+{
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ struct bb_info *bb_info = BB_INFO (bb);
+
+ COPY_HARD_REG_SET (bb_info->free_in, bb_info->save_in);
+ IOR_HARD_REG_SET (bb_info->free_in, bb_info->restore_in);
+ COPY_HARD_REG_SET (bb_info->free_out, bb_info->save_out);
+ IOR_HARD_REG_SET (bb_info->free_out, bb_info->restore_out);
+ }
+ df_simple_dataflow (DF_BACKWARD, NULL, free_con_fun_0, free_con_fun_n,
+ free_trans_fun, all_blocks,
+ df_get_postorder (DF_BACKWARD),
+ df_get_n_blocks (DF_BACKWARD));
+}
+
+/* The function calculates the global save/restore information used to put
+ save/restore code without generating new blocks. This is a
+ bidirectional data flow problem (calculation of SAVE_IN and
+ SAVE_OUT is a backward data flow problem and SAVE_HERE is forward
+ one; calculation of RESTORE_IN and RESTORE_OUT is a forward data
+ flow problem and RESTORE_HERE is backward one). It is complicated
+ by necessity of calculation of modes for saving/restoring callee
+ clobbered hard registers. */
+static void
+make_global_save_analysis (void)
+{
+ basic_block bb;
+ int iter, changed_p;
+
+ all_blocks = BITMAP_ALLOC (NULL);
+ FOR_ALL_BB (bb)
+ {
+ bitmap_set_bit (all_blocks, bb->index);
+ }
+ calculate_local_save_info ();
+ for (iter = 1;; iter++)
+ {
+ calculate_restore_in_out ();
+ changed_p = calculate_restore_here ();
+ if (! changed_p)
+ break;
+ }
+ if (dump_file != NULL)
+ fprintf (dump_file, " Number of global restore analysis iterations %d\n",
+ iter);
+ for (iter = 1;; iter++)
+ {
+ calculate_save_in_out ();
+ changed_p = calculate_save_here ();
+ if (! changed_p)
+ break;
+ }
+ if (dump_file != NULL)
+ fprintf (dump_file, " Number of global save analysis iterations %d\n",
+ iter);
+ calculate_free_in_out ();
+ BITMAP_FREE (all_blocks);
+}
+
+/* Print hard registers in SET to file F. The registers are printed
+ with its mode given in MODES and corresponding pseudo given in
+ PSEUDOS. */
+static void
+print_annotated_hard_reg_set (FILE *f, HARD_REG_SET set,
+ unsigned char *modes, int *pseudos)
+{
+ int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (set, i))
+ {
+ fprintf (f, " %d", i);
+ if (pseudos[i] >= 0)
+ fprintf (f, "(%d)", pseudos[i]);
+ fprintf (f, ":%s %s", GET_MODE_NAME (modes[i]), reg_names[i]);
+ }
+}
+
+/* Print hard registers in SET to file F. */
+static void
+print_hard_reg_set (FILE *f, HARD_REG_SET set)
+{
+ int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (set, i))
+ fprintf (f, " %d %s", i, reg_names[i]);
+}
+
+/* Print the save information for each block to file F. */
+static void
+print_save_data (FILE *f)
+{
+ basic_block bb;
+ struct bb_info *bb_info;
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_BB (bb)
+ {
+ bb_info = BB_INFO (bb);
+ fprintf (f, "bb %d (preds", bb->index);
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ fprintf (f, " %d", e->src->index);
+ }
+ fprintf (f, ") (succs");
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ fprintf (f, " %d", e->dest->index);
+ }
+ fprintf (f, ")\n empty_save_out_p=%d", bb_info->empty_save_out_p);
+ fprintf (f, ", empty_restore_in_p=%d", bb_info->empty_restore_in_p);
+ fprintf (f, "\n save_in:");
+ print_annotated_hard_reg_set
+ (f, bb_info->save_in, bb_info->save_in_mode, bb_info->save_in_pseudo);
+ fprintf (f, "\n save_out:");
+ print_annotated_hard_reg_set
+ (f, bb_info->save_out,
+ bb_info->save_out_mode, bb_info->save_out_pseudo);
+ fprintf (f, "\n restore_in:");
+ print_annotated_hard_reg_set
+ (f, bb_info->restore_in,
+ bb_info->restore_in_mode, bb_info->restore_in_pseudo);
+ fprintf (f, "\n restore_out:");
+ print_annotated_hard_reg_set
+ (f, bb_info->restore_out,
+ bb_info->restore_out_mode, bb_info->restore_out_pseudo);
+ fprintf (f, "\n free_in:");
+ print_hard_reg_set (f, bb_info->free_in);
+ fprintf (f, "\n free_out:");
+ print_hard_reg_set (f, bb_info->free_out);
+ fprintf (f, "\n live_at_start:");
+ print_hard_reg_set (f, bb_info->live_at_start);
+ fprintf (f, "\n live_at_end:");
+ print_hard_reg_set (f, bb_info->live_at_end);
+ fprintf (f, "\n save_here:");
+ print_hard_reg_set (f, bb_info->save_here);
+ fprintf (f, "\n restore_here:");
+ print_hard_reg_set (f, bb_info->restore_here);
+ fprintf (f, "\n save_kill:");
+ print_hard_reg_set (f, bb_info->save_kill);
+ fprintf (f, "\n restore_kill:");
+ print_hard_reg_set (f, bb_info->restore_kill);
+ fprintf (f, "\n save_gen:");
+ print_hard_reg_set (f, bb_info->save_gen);
+ fprintf (f, "\n restore_gen:");
+ print_hard_reg_set (f, bb_info->restore_gen);
+ fprintf (f, "\n free_gen:");
+ print_hard_reg_set (f, bb_info->save_gen);
+ fprintf (f, "\n\n");
+ }
+}
+
+/* Print the save information for each block to stderr. */
+void
+debug_save_data (void)
+{
+ print_save_data (stderr);
+}
+
+
+/* Setup hard registers in SET to save. Setup also their save modes
+ in SAVE_MODE from FROM_SAVE_MODE and their pseudos in SAVE_PSEUDO
+ from FROM_SAVE_PSEUDO. */
+static void
+set_hard_reg_saved (HARD_REG_SET set, unsigned char *from_saved_mode,
+ enum machine_mode *save_mode, int *from_saved_pseudo,
+ int *save_pseudo)
+{
+ int regno;
+
+ n_regs_saved = 0;
+ COPY_HARD_REG_SET (hard_regs_saved, set);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (set, regno))
+ {
+ n_regs_saved++;
+ save_mode[regno] = from_saved_mode[regno];
+ save_pseudo[regno] = from_saved_pseudo[regno];
+ }
+ else
+ {
+ save_mode[regno] = VOIDmode;
+ save_pseudo[regno] = -1;
+ }
+}
+
+
/* Find the places where hard regs are live across calls and save them. */
void
save_call_clobbered_regs (void)
{
- struct insn_chain *chain, *next;
- enum machine_mode save_mode [FIRST_PSEUDO_REGISTER];
-
+ unsigned int regno;
/* Computed in mark_set_regs, holds all registers set by the current
instruction. */
- HARD_REG_SET this_insn_sets;
+ HARD_REG_SET this_insn_sets, hard_regs_to_save, saved;
+ struct insn_chain *chain, *last, *next, *prev, *last_restore_chain, *where;
+ struct bb_info *bb_info;
+ enum machine_mode save_mode[FIRST_PSEUDO_REGISTER];
+ loop_p loop;
+ loop_iterator li;
+ int save_pseudo[FIRST_PSEUDO_REGISTER];
+ int free_pseudo[FIRST_PSEUDO_REGISTER];
+ bool do_placement_opt_p = 0 /* flag_ira && optimize */;
+
+ if (do_placement_opt_p)
+ {
+ /* Do global analysis for better placement of spill code. */
+ alloc_aux_for_blocks (sizeof (struct bb_info));
+ FOR_EACH_LOOP (li, loop, 0)
+ {
+ loop->aux = xmalloc (sizeof (struct loop_info));
+ CLEAR_HARD_REG_SET (LOOP_INFO (loop)->mentioned_regs);
+ }
+ memset (BB_INFO (ENTRY_BLOCK_PTR), 0, sizeof (struct bb_info));
+ memset (BB_INFO (EXIT_BLOCK_PTR), 0, sizeof (struct bb_info));
+ make_global_save_analysis ();
+ }
CLEAR_HARD_REG_SET (hard_regs_saved);
n_regs_saved = 0;
+ if (do_placement_opt_p && reload_insn_chain != NULL)
+ {
+ bb_info = BB_INFO_BY_INDEX (reload_insn_chain->block);
+ set_hard_reg_saved (bb_info->restore_in,
+ bb_info->restore_in_mode, save_mode,
+ bb_info->restore_in_pseudo, save_pseudo);
+ }
+
+ last = NULL;
for (chain = reload_insn_chain; chain != 0; chain = next)
{
rtx insn = chain->insn;
enum rtx_code code = GET_CODE (insn);
+ last = chain;
next = chain->next;
- gcc_assert (!chain->is_caller_save_insn);
-
if (INSN_P (insn))
{
- /* If some registers have been saved, see if INSN references
- any of them. We must restore them before the insn if so. */
-
if (n_regs_saved)
{
- int regno;
-
- if (code == JUMP_INSN)
+ if (!do_placement_opt_p && code == JUMP_INSN)
/* Restore all registers if this is a JUMP_INSN. */
COPY_HARD_REG_SET (referenced_regs, hard_regs_saved);
else
{
CLEAR_HARD_REG_SET (referenced_regs);
- mark_referenced_regs (PATTERN (insn));
+ CLEAR_HARD_REG_SET (modified_regs);
+ mark_referenced_regs (PATTERN (insn), FALSE);
AND_HARD_REG_SET (referenced_regs, hard_regs_saved);
}
+ /* If some registers have been saved, see if INSN
+ references any of them. We must restore them before
+ the insn if so. */
+
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (referenced_regs, regno))
- regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode);
+ {
+ unsigned int before = regno;
+
+ regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS,
+ save_mode, save_pseudo);
+ if (do_placement_opt_p)
+ {
+ gcc_assert (before == regno);
+ save_mode[before] = VOIDmode;
+ save_pseudo[before] = -1;
+ }
+ }
}
if (code == CALL_INSN
&& ! SIBLING_CALL_P (insn)
&& ! find_reg_note (insn, REG_NORETURN, NULL))
{
- unsigned regno;
- HARD_REG_SET hard_regs_to_save;
+ HARD_REG_SET used_regs;
reg_set_iterator rsi;
/* Use the register life information in CHAIN to compute which
@@ -447,10 +1803,13 @@ save_call_clobbered_regs (void)
&chain->live_throughout);
/* Save hard registers always in the widest mode available. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
- save_mode [regno] = regno_save_mode [regno][1];
- else
- save_mode [regno] = VOIDmode;
+ {
+ if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+ save_mode[regno] = regno_save_mode[regno][1];
+ else
+ save_mode[regno] = VOIDmode;
+ save_pseudo[regno] = -1;
+ }
/* Look through all live pseudos, mark their hard registers
and choose proper mode for saving. */
@@ -461,15 +1820,23 @@ save_call_clobbered_regs (void)
int nregs;
enum machine_mode mode;
+ /* Remember live_throughout can contain spilled
+ registers when IRA is used. */
+ if (flag_ira && optimize && r < 0)
+ continue;
gcc_assert (r >= 0);
+
nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
mode = HARD_REGNO_CALLER_SAVE_MODE
- (r, nregs, PSEUDO_REGNO_MODE (regno));
+ (r, nregs, PSEUDO_REGNO_MODE (regno));
if (GET_MODE_BITSIZE (mode)
> GET_MODE_BITSIZE (save_mode[r]))
save_mode[r] = mode;
while (nregs-- > 0)
- SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+ {
+ SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+ save_pseudo[r + nregs] = regno;
+ }
}
/* Record all registers set in this call insn. These don't need
@@ -483,12 +1850,21 @@ save_call_clobbered_regs (void)
AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets);
AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
- AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set);
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
- regno += insert_save (chain, 1, regno, &hard_regs_to_save, save_mode);
+ COPY_HARD_REG_SET (used_regs, call_used_reg_set);
+ AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+ if (do_placement_opt_p)
+ IOR_HARD_REG_SET (hard_regs_saved, hard_regs_to_save);
+ else
+ {
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+ regno += insert_save (chain, 1, regno,
+ &hard_regs_to_save, save_mode,
+ save_pseudo);
+
+ }
+
/* Must recompute n_regs_saved. */
n_regs_saved = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
@@ -497,27 +1873,308 @@ save_call_clobbered_regs (void)
}
}
- if (chain->next == 0 || chain->next->block > chain->block)
+ if (chain->next == 0 || chain->next->block != chain->block)
{
- int regno;
+ struct bb_info *next_bb_info;
+
+ next_bb_info = (chain->next != NULL
+ ? BB_INFO_BY_INDEX (chain->next->block) : NULL);
+
/* At the end of the basic block, we must restore any registers that
remain saved. If the last insn in the block is a JUMP_INSN, put
the restore before the insn, otherwise, put it after the insn. */
+ if (do_placement_opt_p)
+ set_hard_reg_saved
+ (BB_INFO_BY_INDEX (chain->block)->restore_here,
+ BB_INFO_BY_INDEX (chain->block)->restore_out_mode, save_mode,
+ BB_INFO_BY_INDEX (chain->block)->restore_out_pseudo,
+ save_pseudo);
+
if (n_regs_saved)
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
- regno += insert_restore (chain, JUMP_P (insn),
- regno, MOVE_MAX_WORDS, save_mode);
+ {
+ unsigned int before = regno;
+
+ regno += insert_restore (chain, JUMP_P (insn),
+ regno, MOVE_MAX_WORDS, save_mode,
+ save_pseudo);
+ gcc_assert (before == regno);
+ save_mode[before] = VOIDmode;
+ save_pseudo[before] = -1;
+ }
+
+ if (do_placement_opt_p && next_bb_info != NULL)
+ set_hard_reg_saved (next_bb_info->restore_in,
+ next_bb_info->restore_in_mode, save_mode,
+ next_bb_info->restore_in_pseudo, save_pseudo);
+
+ }
+ }
+
+ if (!do_placement_opt_p)
+ return;
+
+ CLEAR_HARD_REG_SET (hard_regs_to_save);
+ n_regs_saved = 0;
+ last_restore_chain = NULL;
+
+ if (last == NULL)
+ CLEAR_HARD_REG_SET (saved);
+ else
+ {
+ bb_info = BB_INFO_BY_INDEX (last->block);
+ set_hard_reg_saved (bb_info->save_out,
+ bb_info->save_out_mode, save_mode,
+ bb_info->save_out_pseudo, save_pseudo);
+ COPY_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
+ COPY_HARD_REG_SET (saved, bb_info->free_out);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (!TEST_HARD_REG_BIT (saved, regno))
+ free_pseudo[regno] = -1;
+ else
+ {
+ if (TEST_HARD_REG_BIT (bb_info->save_out, regno))
+ free_pseudo [regno] = bb_info->save_out_pseudo [regno];
+ else
+ {
+ gcc_assert (TEST_HARD_REG_BIT (bb_info->restore_out, regno));
+ free_pseudo [regno] = bb_info->restore_out_pseudo [regno];
+ }
+ }
+ }
+ for (chain = last; chain != 0; chain = prev)
+ {
+ rtx insn = chain->insn;
+ enum rtx_code code = GET_CODE (insn);
+
+ prev = chain->prev;
+
+ if (INSN_P (insn))
+ {
+ bb_info = BB_INFO_BY_INDEX (chain->block);
+
+ CLEAR_HARD_REG_SET (referenced_regs);
+ CLEAR_HARD_REG_SET (modified_regs);
+ mark_referenced_regs (PATTERN (insn), FALSE);
+ AND_HARD_REG_SET (modified_regs, hard_regs_to_save);
+ AND_COMPL_HARD_REG_SET (saved, referenced_regs);
+ AND_COMPL_HARD_REG_SET (saved, modified_regs);
+
+ if (chain->is_caller_save_insn)
+ {
+ if (last_restore_chain == NULL)
+ last_restore_chain = chain;
+ }
+ else
+ {
+ /* If some registers have been saved, see if INSN
+ references any of them. We must restore them before
+ the insn if so. */
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (modified_regs, regno))
+ {
+ unsigned int before = regno;
+
+ /* We should put save insns before restore insns
+ between the two calls because the same stack
+ slot for different hard registers can be used
+ for restoring in the first call and saving for
+ the second call. */
+ regno += insert_save (last_restore_chain != NULL
+ ? last_restore_chain : chain,
+ last_restore_chain == NULL
+ && JUMP_P (chain->insn),
+ regno, &hard_regs_to_save,
+ save_mode, save_pseudo);
+ gcc_assert (before == regno);
+
+ CLEAR_HARD_REG_BIT (saved, regno);
+ CLEAR_HARD_REG_BIT (hard_regs_to_save, regno);
+ save_mode[before] = VOIDmode;
+ save_pseudo[before] = -1;
+ free_pseudo[before] = -1;
+ }
+ }
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (saved, regno) && free_pseudo[regno] >= 0)
+ SET_REGNO_REG_SET (&chain->saved, free_pseudo[regno]);
+ if (chain->is_caller_save_insn && chain->saved_pseudo_regno >= 0)
+ {
+ int i;
+ enum machine_mode mode;
+
+ mode = GET_MODE (regno_reg_rtx[chain->saved_pseudo_regno]);
+ regno = reg_renumber[chain->saved_pseudo_regno];
+ add_to_hard_reg_set (&saved, mode, regno);
+ for (i = hard_regno_nregs[regno][mode] - 1; i >= 0; i--)
+ free_pseudo[regno + i] = chain->saved_pseudo_regno;
+ }
+ if (code == CALL_INSN
+ && ! SIBLING_CALL_P (insn)
+ && ! find_reg_note (insn, REG_NORETURN, NULL))
+ {
+ HARD_REG_SET used_regs;
+ reg_set_iterator rsi;
+
+ last_restore_chain = NULL;
+
+ /* Use the register life information in CHAIN to
+ compute which regs are live during the call. */
+ REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+ &chain->live_throughout);
+ /* Save hard registers always in the widest mode
+ available. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+ {
+ CLEAR_HARD_REG_SET (this_insn_sets);
+ note_stores (PATTERN (insn), mark_set_regs,
+ &this_insn_sets);
+ save_mode[regno] = regno_save_mode[regno][1];
+ }
+ else
+ save_mode[regno] = VOIDmode;
+ save_pseudo[regno] = -1;
+ }
+
+ /* Look through all live pseudos, mark their hard
+ registers and choose proper mode for saving. */
+ EXECUTE_IF_SET_IN_REG_SET
+ (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi)
+ {
+ int r = reg_renumber[regno];
+ int nregs;
+ enum machine_mode mode;
+
+ /* Remember live_throughout can contain spilled
+ registers when IRA is used. */
+ if (flag_ira && r < 0)
+ continue;
+ gcc_assert (r >= 0);
+
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ mode = HARD_REGNO_CALLER_SAVE_MODE
+ (r, nregs, PSEUDO_REGNO_MODE (regno));
+ if (GET_MODE_BITSIZE (mode)
+ > GET_MODE_BITSIZE (save_mode[r]))
+ save_mode[r] = mode;
+ while (nregs-- > 0)
+ {
+ SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+ save_pseudo[r + nregs] = regno;
+ free_pseudo[r + nregs] = regno;
+ }
+ }
+
+ /* 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);
+
+ /* Compute which hard regs must be saved before this call. */
+ AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
+ AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets);
+ COPY_HARD_REG_SET (used_regs, call_used_reg_set);
+ AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+ COPY_HARD_REG_SET (saved, hard_regs_to_save);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (!TEST_HARD_REG_BIT (saved, regno))
+ {
+ free_pseudo[regno] = -1;
+ save_pseudo[regno] = -1;
+ save_mode[regno] = VOIDmode;
+ }
+ }
}
+
+ if (chain->prev == 0 || chain->prev->block != chain->block)
+ {
+ struct bb_info *prev_bb_info;
+
+ prev_bb_info = (chain->prev != NULL
+ ? BB_INFO_BY_INDEX (chain->prev->block) : NULL);
+
+ /* At the start of the basic block, we must save any
+ registers from save_here. */
+
+ set_hard_reg_saved
+ (BB_INFO_BY_INDEX (chain->block)->save_here,
+ BB_INFO_BY_INDEX (chain->block)->save_in_mode, save_mode,
+ BB_INFO_BY_INDEX (chain->block)->save_in_pseudo, save_pseudo);
+ COPY_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
+
+ where = (last_restore_chain != NULL
+ ? last_restore_chain->next : chain);
+ /* An addr_vec is placed outside any basic block but its
+ chain has the same block as the block of subsequent insn.
+ So skip to the real start of the basic block. */
+ if (GET_CODE (where->insn) == CODE_LABEL && where->next != NULL
+ && JUMP_P (where->next->insn)
+ && (GET_CODE (PATTERN (where->next->insn)) == ADDR_DIFF_VEC
+ || GET_CODE (PATTERN (where->next->insn)) == ADDR_VEC)
+ && where->next->next != NULL)
+ where = where->next->next;
+
+ if (!hard_reg_set_empty_p (hard_regs_to_save))
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+ {
+ unsigned int before = regno;
+
+ regno += insert_save (where, INSN_P (where->insn), regno,
+ &hard_regs_to_save, save_mode,
+ save_pseudo);
+ gcc_assert (before == regno);
+ save_mode[before] = VOIDmode;
+ save_pseudo[before] = -1;
+ }
+
+ if (prev_bb_info != NULL)
+ {
+ last_restore_chain = NULL;
+ set_hard_reg_saved (prev_bb_info->save_out,
+ prev_bb_info->save_out_mode, save_mode,
+ prev_bb_info->save_out_pseudo, save_pseudo);
+ COPY_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
+ COPY_HARD_REG_SET (saved, prev_bb_info->free_out);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (!TEST_HARD_REG_BIT (saved, regno))
+ free_pseudo[regno] = -1;
+ else
+ {
+ if (TEST_HARD_REG_BIT (prev_bb_info->save_out, regno))
+ free_pseudo [regno]
+ = prev_bb_info->save_out_pseudo [regno];
+ else
+ {
+ gcc_assert (TEST_HARD_REG_BIT (prev_bb_info->restore_out, regno));
+ free_pseudo [regno]
+ = prev_bb_info->restore_out_pseudo [regno];
+ }
+ }
+ }
+ }
+ }
+
+ FOR_EACH_LOOP (li, loop, 0)
+ {
+ free (loop->aux);
}
+ if (flag_ira && optimize)
+ free_aux_for_blocks ();
}
-/* Here from note_stores, or directly from save_call_clobbered_regs, when
- an insn stores a value in a register.
- Set the proper bit or bits in this_insn_sets. All pseudos that have
- been assigned hard regs have had their register number changed already,
- so we can ignore pseudos. */
+/* Here from note_stores, or directly from save_call_clobbered_regs,
+ when an insn stores a value in a register. Set the proper bit or
+ bits in this_insn_sets. */
static void
mark_set_regs (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, void *data)
{
@@ -527,16 +2184,34 @@ mark_set_regs (rtx reg, const_rtx setter
if (GET_CODE (reg) == SUBREG)
{
rtx inner = SUBREG_REG (reg);
- if (!REG_P (inner) || REGNO (inner) >= FIRST_PSEUDO_REGISTER)
+ if (!REG_P (inner))
return;
- regno = subreg_regno (reg);
- endregno = regno + subreg_nregs (reg);
+ if ((regno = REGNO (inner)) >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_renumber[regno] < 0)
+ return;
+ regno = reg_renumber[regno];
+ endregno
+ = hard_regno_nregs[regno][PSEUDO_REGNO_MODE (REGNO (inner))];
+ }
+ else
+ {
+ regno = subreg_regno (reg);
+ endregno = regno + subreg_nregs (reg);
+ }
}
- else if (REG_P (reg)
- && REGNO (reg) < FIRST_PSEUDO_REGISTER)
+ else if (REG_P (reg))
{
- regno = REGNO (reg);
- endregno = END_HARD_REGNO (reg);
+ if ((regno = REGNO (reg)) < FIRST_PSEUDO_REGISTER)
+ endregno = END_HARD_REGNO (reg);
+ else
+ {
+ if (reg_renumber[regno] < 0)
+ return;
+ regno = reg_renumber[regno];
+ endregno
+ = hard_regno_nregs[regno][PSEUDO_REGNO_MODE (REGNO (reg))];
+ }
}
else
return;
@@ -583,72 +2258,93 @@ add_stored_regs (rtx reg, const_rtx sett
SET_REGNO_REG_SET ((regset) data, i);
}
-/* Walk X and record all referenced registers in REFERENCED_REGS. */
+/* Walk X and record all referenced registers in REFERENCED_REG and
+ modified registers in MODIFIED_REGS. */
static void
-mark_referenced_regs (rtx x)
+mark_referenced_regs (rtx x, int set_p)
{
- enum rtx_code code = GET_CODE (x);
+ enum rtx_code code;
const char *fmt;
int i, j;
-
- if (code == SET)
- mark_referenced_regs (SET_SRC (x));
- if (code == SET || code == CLOBBER)
+ int stop_p;
+
+ for (stop_p = FALSE; !stop_p; )
{
- x = SET_DEST (x);
+ while (GET_CODE (x) == STRICT_LOW_PART || GET_CODE (x) == ZERO_EXTRACT)
+ x = XEXP (x, 0);
code = GET_CODE (x);
- if ((code == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
- || code == PC || code == CC0
- || (code == SUBREG && REG_P (SUBREG_REG (x))
- && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
- /* If we're setting only part of a multi-word register,
- we shall mark it as referenced, because the words
- that are not being set should be restored. */
- && ((GET_MODE_SIZE (GET_MODE (x))
- >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
- <= UNITS_PER_WORD))))
- return;
- }
- if (code == MEM || code == SUBREG)
- {
- x = XEXP (x, 0);
- code = GET_CODE (x);
- }
-
- if (code == REG)
- {
- int regno = REGNO (x);
- int hardregno = (regno < FIRST_PSEUDO_REGISTER ? regno
- : reg_renumber[regno]);
-
- if (hardregno >= 0)
- add_to_hard_reg_set (&referenced_regs, GET_MODE (x), hardregno);
- /* If this is a pseudo that did not get a hard register, scan its
- memory location, since it might involve the use of another
- register, which might be saved. */
- else if (reg_equiv_mem[regno] != 0)
- mark_referenced_regs (XEXP (reg_equiv_mem[regno], 0));
- else if (reg_equiv_address[regno] != 0)
- mark_referenced_regs (reg_equiv_address[regno]);
- return;
- }
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- mark_referenced_regs (XEXP (x, i));
- else if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- mark_referenced_regs (XVECEXP (x, i, j));
+ switch (code)
+ {
+ case SET:
+ gcc_assert (!set_p);
+ mark_referenced_regs (SET_DEST (x), TRUE);
+ x = SET_SRC (x);
+ break;
+ case CLOBBER:
+ mark_referenced_regs (SET_DEST (x), TRUE);
+ return;
+ case PRE_INC:
+ case POST_INC:
+ case PRE_DEC:
+ case POST_DEC:
+ gcc_assert (!set_p);
+ x = XEXP (x, 0);
+ mark_referenced_regs (x, TRUE);
+ break;
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ set_p = FALSE;
+ mark_referenced_regs (XEXP (x, 0), FALSE);
+ mark_referenced_regs (XEXP (x, 0), TRUE);
+ x = XEXP (x, 1);
+ mark_referenced_regs (x, FALSE);
+ break;
+ case SUBREG:
+ x = SUBREG_REG (x);
+ break;
+ case REG:
+ {
+ int regno = REGNO (x);
+ int hardregno = (regno < FIRST_PSEUDO_REGISTER ? regno
+ : reg_renumber[regno]);
+
+ if (hardregno >= 0)
+ {
+ if (set_p)
+ add_to_hard_reg_set (&modified_regs, GET_MODE (x), hardregno);
+ add_to_hard_reg_set (&referenced_regs, GET_MODE (x), hardregno);
+ }
+ /* If this is a pseudo that did not get a hard register, scan
+ its memory location, since it might involve the use of
+ another register, which might be saved. */
+ else if (reg_equiv_mem[regno] != 0)
+ mark_referenced_regs (XEXP (reg_equiv_mem[regno], 0), set_p);
+ else if (reg_equiv_address[regno] != 0)
+ mark_referenced_regs (reg_equiv_address[regno], set_p);
+ return;
+ }
+ default:
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ mark_referenced_regs (XEXP (x, i), FALSE);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ mark_referenced_regs (XVECEXP (x, i, j), FALSE);
+ }
+ stop_p = TRUE;
+ break;
+ }
}
}
-/* Insert a sequence of insns to restore. Place these insns in front of
- CHAIN if BEFORE_P is nonzero, behind the insn otherwise. MAXRESTORE is
- the maximum number of registers which should be restored during this call.
- It should never be less than 1 since we only work with entire registers.
+/* Insert a sequence of insns to restore. Place these insns in front
+ of CHAIN if BEFORE_P is nonzero, behind the insn otherwise.
+ MAXRESTORE is the maximum number of registers which should be
+ restored during this call. It should never be less than 1 since we
+ only work with entire registers. SAVE_PSEUDO maps hard registers
+ into the corresponding pseudos.
Note that we have verified in init_caller_save that we can do this
with a simple SET, so use it. Set INSN_CODE to what we save there
@@ -660,7 +2356,8 @@ mark_referenced_regs (rtx x)
static int
insert_restore (struct insn_chain *chain, int before_p, int regno,
- int maxrestore, enum machine_mode *save_mode)
+ int maxrestore, enum machine_mode *save_mode,
+ int *save_pseudo)
{
int i, k;
rtx pat = NULL_RTX;
@@ -703,23 +2400,31 @@ insert_restore (struct insn_chain *chain
break;
}
- mem = regno_save_mem [regno][numregs];
- if (save_mode [regno] != VOIDmode
- && save_mode [regno] != GET_MODE (mem)
- && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]])
- mem = adjust_address (mem, save_mode[regno], 0);
+ mem = regno_save_mem[regno][numregs];
+ if (save_mode[regno] != VOIDmode
+ && save_mode[regno] != GET_MODE (mem)
+ && numregs == (unsigned int) hard_regno_nregs[regno][save_mode[regno]]
+ && reg_save_code (regno, save_mode[regno]) >= 0)
+ mem = adjust_address_nv (mem, save_mode[regno], 0);
else
mem = copy_rtx (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,
gen_rtx_REG (GET_MODE (mem),
regno), mem);
code = reg_restore_code (regno, GET_MODE (mem));
new_chain = insert_one_insn (chain, before_p, code, pat);
+ new_chain->saved_pseudo_regno = save_pseudo[regno];
+
+ if (dump_file != NULL)
+ fprintf (dump_file, "inserting restore insn %u for pseudo %d %s %u\n",
+ INSN_UID (new_chain->insn), save_pseudo[regno],
+ before_p ? "before" : "after", INSN_UID (chain->insn));
/* Clear status for all registers we restored. */
for (k = 0; k < i; k++)
@@ -737,7 +2442,8 @@ insert_restore (struct insn_chain *chain
static int
insert_save (struct insn_chain *chain, int before_p, int regno,
- HARD_REG_SET (*to_save), enum machine_mode *save_mode)
+ HARD_REG_SET (*to_save), enum machine_mode *save_mode,
+ int *save_pseudo)
{
int i;
unsigned int k;
@@ -780,23 +2486,31 @@ insert_save (struct insn_chain *chain, i
break;
}
- mem = regno_save_mem [regno][numregs];
- if (save_mode [regno] != VOIDmode
- && save_mode [regno] != GET_MODE (mem)
- && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]])
- mem = adjust_address (mem, save_mode[regno], 0);
+ mem = regno_save_mem[regno][numregs];
+ if (save_mode[regno] != VOIDmode
+ && save_mode[regno] != GET_MODE (mem)
+ && numregs == (unsigned int) hard_regno_nregs[regno][save_mode[regno]]
+ && reg_save_code (regno, save_mode[regno]) >= 0)
+ mem = adjust_address_nv (mem, save_mode[regno], 0);
else
mem = copy_rtx (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),
regno));
code = reg_save_code (regno, GET_MODE (mem));
new_chain = insert_one_insn (chain, before_p, code, pat);
+ new_chain->saved_pseudo_regno = save_pseudo[regno];
+
+ if (dump_file != NULL)
+ fprintf (dump_file, "inserting save insn %u for pseudo %d %s %u\n",
+ INSN_UID (new_chain->insn), save_pseudo[regno],
+ before_p ? "before" : "after", INSN_UID (chain->insn));
/* Set hard_regs_saved and dead_or_set for all the registers we saved. */
for (k = 0; k < numregs; k++)
@@ -909,15 +2623,23 @@ insert_one_insn (struct insn_chain *chai
new_chain->next->prev = new_chain;
chain->next = new_chain;
new_chain->prev = chain;
- new_chain->insn = emit_insn_after (pat, insn);
+ if (GET_CODE (insn) != CODE_LABEL)
+ new_chain->insn = emit_insn_after (pat, insn);
+ else
+ {
+ /* Put the insn after bb note in a empty basic block. */
+ gcc_assert (NEXT_INSN (insn) && NOTE_P (NEXT_INSN (insn)));
+ new_chain->insn = emit_insn_after (pat, NEXT_INSN (insn));
+ }
/* ??? It would be nice if we could exclude the already / still saved
registers from the live sets, and observe REG_UNUSED notes. */
COPY_REG_SET (&new_chain->live_throughout, &chain->live_throughout);
/* Registers that are set in CHAIN->INSN live in the new insn.
(Unless there is a REG_UNUSED note for them, but we don't
look for them here.) */
- note_stores (PATTERN (chain->insn), add_stored_regs,
- &new_chain->live_throughout);
+ if (INSN_P (chain->insn))
+ note_stores (PATTERN (chain->insn), add_stored_regs,
+ &new_chain->live_throughout);
CLEAR_REG_SET (&new_chain->dead_or_set);
if (chain->insn == BB_END (BASIC_BLOCK (chain->block)))
BB_END (BASIC_BLOCK (chain->block)) = new_chain->insn;
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/global.c ./global.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/global.c 2008-08-21 21:16:45.000000000 -0400
+++ ./global.c 2008-08-25 11:35:41.000000000 -0400
@@ -188,7 +188,7 @@ compute_regs_asm_clobbered (char *regs_a
/* All registers that can be eliminated. */
-static HARD_REG_SET eliminable_regset;
+HARD_REG_SET eliminable_regset;
static int regno_compare (const void *, const void *);
static int allocno_compare (const void *, const void *);
@@ -197,7 +197,6 @@ static void prune_preferences (void);
static void set_preferences (void);
static void find_reg (int, HARD_REG_SET, int, int, int);
static void dump_conflicts (FILE *);
-static void build_insn_chain (void);
/* Look through the list of eliminable registers. Set ELIM_SET to the
@@ -1355,7 +1366,8 @@ mark_elimination (int from, int to)
FOR_EACH_BB (bb)
{
- regset r = DF_LIVE_IN (bb);
+ /* We don't use LIVE info in IRA. */
+ regset r = (flag_ira ? DF_LR_IN (bb) : DF_LIVE_IN (bb));
if (REGNO_REG_SET_P (r, from))
{
CLEAR_REGNO_REG_SET (r, from);
@@ -1372,6 +1384,7 @@ print_insn_chain (FILE *file, struct ins
fprintf (file, "insn=%d, ", INSN_UID(c->insn));
bitmap_print (file, &c->live_throughout, "live_throughout: ", ", ");
bitmap_print (file, &c->dead_or_set, "dead_or_set: ", "\n");
+ bitmap_print (file, &c->saved, "saved: ", "\n");
}
@@ -1385,11 +1398,21 @@ print_insn_chains (FILE *file)
print_insn_chain (file, c);
}
+/* Return true if pseudo REGNO should be added to set live_throughout
+ or dead_or_set of the insn chains for reload consideration. */
+
+static bool
+pseudo_for_reload_consideration_p (int regno)
+{
+ /* Consider spilled pseudos too for IRA because they still have a
+ chance to get hard-registers in the reload when IRA is used. */
+ return reg_renumber[regno] >= 0 || (flag_ira && optimize);
+}
/* Walk the insns of the current function and build reload_insn_chain,
and record register life information. */
-static void
+void
build_insn_chain (void)
{
unsigned int i;
@@ -1412,7 +1435,6 @@ build_insn_chain (void)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (eliminable_regset, i))
bitmap_set_bit (elim_regset, i);
-
FOR_EACH_BB_REVERSE (bb)
{
bitmap_iterator bi;
@@ -1430,7 +1452,7 @@ build_insn_chain (void)
EXECUTE_IF_SET_IN_BITMAP (df_get_live_out (bb), FIRST_PSEUDO_REGISTER, i, bi)
{
- if (reg_renumber[i] >= 0)
+ if (pseudo_for_reload_consideration_p (i))
bitmap_set_bit (live_relevant_regs, i);
}
@@ -1467,11 +1489,13 @@ build_insn_chain (void)
if (!fixed_regs[regno])
bitmap_set_bit (&c->dead_or_set, regno);
}
- else if (reg_renumber[regno] >= 0)
+ else if (pseudo_for_reload_consideration_p (regno))
bitmap_set_bit (&c->dead_or_set, regno);
}
- if ((regno < FIRST_PSEUDO_REGISTER || reg_renumber[regno] >= 0)
+ if ((regno < FIRST_PSEUDO_REGISTER
+ || reg_renumber[regno] >= 0
+ || (flag_ira && optimize))
&& (!DF_REF_FLAGS_IS_SET (def, DF_REF_CONDITIONAL)))
{
rtx reg = DF_REF_REG (def);
@@ -1567,11 +1591,12 @@ build_insn_chain (void)
if (!fixed_regs[regno])
bitmap_set_bit (&c->dead_or_set, regno);
}
- else if (reg_renumber[regno] >= 0)
+ else if (pseudo_for_reload_consideration_p (regno))
bitmap_set_bit (&c->dead_or_set, regno);
}
- if (regno < FIRST_PSEUDO_REGISTER || reg_renumber[regno] >= 0)
+ if (regno < FIRST_PSEUDO_REGISTER
+ || pseudo_for_reload_consideration_p (regno))
{
if (GET_CODE (reg) == SUBREG
&& !DF_REF_FLAGS_IS_SET (use,
@@ -1748,6 +1773,13 @@ dump_global_regs (FILE *file)
fprintf (file, "\n\n");
}
+
+static bool
+gate_handle_global_alloc (void)
+{
+ return ! flag_ira;
+}
+
/* Run old register allocator. Return TRUE if we must exit
rest_of_compilation upon return. */
static unsigned int
@@ -1811,7 +1843,7 @@ struct rtl_opt_pass pass_global_alloc =
{
RTL_PASS,
"greg", /* name */
- NULL, /* gate */
+ gate_handle_global_alloc, /* gate */
rest_of_handle_global_alloc, /* execute */
NULL, /* sub */
NULL, /* next */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/opts.c ./opts.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/opts.c 2008-08-21 21:16:46.000000000 -0400
+++ ./opts.c 2008-08-25 11:31:49.000000000 -0400
@@ -878,6 +878,11 @@ decode_options (unsigned int argc, const
flag_section_anchors = 0;
}
+#ifdef IRA_COVER_CLASSES
+ /* Use IRA if it is implemented for the target. */
+ flag_ira = 1;
+#endif
+
/* Originally we just set the variables if a particular optimization level,
but with the advent of being able to change the optimization level for a
function, we need to reset optimizations. */
@@ -1119,6 +1124,14 @@ decode_options (unsigned int argc, const
flag_reorder_blocks = 1;
}
+#ifndef IRA_COVER_CLASSES
+ if (flag_ira)
+ {
+ inform ("-fira does not work on this architecture");
+ flag_ira = 0;
+ }
+#endif
+
/* Save the current optimization options if this is the first call. */
if (first_time_p)
{
@@ -1970,6 +1983,21 @@ common_handle_option (size_t scode, cons
warning (0, "unknown tls-model \"%s\"", arg);
break;
+ case OPT_fira_algorithm_:
+ if (!strcmp (arg, "regional"))
+ flag_ira_algorithm = IRA_ALGORITHM_REGIONAL;
+ else if (!strcmp (arg, "CB"))
+ flag_ira_algorithm = IRA_ALGORITHM_CB;
+ else if (!strcmp (arg, "mixed"))
+ flag_ira_algorithm = IRA_ALGORITHM_MIXED;
+ else
+ warning (0, "unknown ira algorithm \"%s\"", arg);
+ break;
+
+ case OPT_fira_verbose_:
+ flag_ira_verbose = value;
+ break;
+
case OPT_ftracer:
flag_tracer_set = true;
break;
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/timevar.def ./timevar.def
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/timevar.def 2008-08-21 21:16:46.000000000 -0400
+++ ./timevar.def 2008-08-22 18:09:59.000000000 -0400
@@ -179,6 +179,8 @@ DEFTIMEVAR (TV_SMS , "sms modulo s
DEFTIMEVAR (TV_SCHED , "scheduling")
DEFTIMEVAR (TV_LOCAL_ALLOC , "local alloc")
DEFTIMEVAR (TV_GLOBAL_ALLOC , "global alloc")
+DEFTIMEVAR (TV_IRA , "integrated RA")
+DEFTIMEVAR (TV_RELOAD , "reload")
DEFTIMEVAR (TV_RELOAD_CSE_REGS , "reload CSE regs")
DEFTIMEVAR (TV_SEQABSTR , "sequence abstraction")
DEFTIMEVAR (TV_GCSE_AFTER_RELOAD , "load CSE after reload")
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/regmove.c ./regmove.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/regmove.c 2008-08-21 21:16:46.000000000 -0400
+++ ./regmove.c 2008-08-22 18:09:59.000000000 -0400
@@ -1117,7 +1117,8 @@ regmove_optimize (rtx f, int nregs)
for (pass = 0; pass <= 2; pass++)
{
- if (! flag_regmove && pass >= flag_expensive_optimizations)
+ /* We need fewer optimizations for IRA. */
+ if ((! flag_regmove || flag_ira) && pass >= flag_expensive_optimizations)
goto done;
if (dump_file)
@@ -1165,7 +1166,9 @@ regmove_optimize (rtx f, int nregs)
}
}
}
- if (! flag_regmove)
+
+ /* All optimizations important for IRA have been done. */
+ if (! flag_regmove || flag_ira)
continue;
if (! find_matches (insn, &match))
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/hard-reg-set.h ./hard-reg-set.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/hard-reg-set.h 2008-08-21 21:16:46.000000000 -0400
+++ ./hard-reg-set.h 2008-08-22 18:09:59.000000000 -0400
@@ -538,6 +538,11 @@ extern char global_regs[FIRST_PSEUDO_REG
extern HARD_REG_SET regs_invalidated_by_call;
+/* Call used hard registers which can not be saved because there is no
+ insn for this. */
+
+extern HARD_REG_SET no_caller_save_reg_set;
+
#ifdef REG_ALLOC_ORDER
/* Table of register numbers in the order in which to try to use them. */
@@ -556,6 +561,10 @@ extern HARD_REG_SET reg_class_contents[N
extern unsigned int reg_class_size[N_REG_CLASSES];
+/* For each reg class, table listing all the classes contained in it. */
+
+extern enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
+
/* For each pair of reg classes,
a largest reg class contained in their union. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/local-alloc.c ./local-alloc.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/local-alloc.c 2008-08-21 21:16:46.000000000 -0400
+++ ./local-alloc.c 2008-08-22 18:09:59.000000000 -0400
@@ -298,7 +298,6 @@ static int equiv_init_movable_p (rtx, in
static int contains_replace_regs (rtx);
static int memref_referenced_p (rtx, rtx);
static int memref_used_between_p (rtx, rtx, rtx);
-static void update_equiv_regs (void);
static void no_equiv (rtx, const_rtx, void *);
static void block_alloc (int);
static int qty_sugg_compare (int, int);
@@ -795,9 +794,11 @@ memref_used_between_p (rtx memref, rtx s
into the using insn. If it succeeds, we can eliminate the register
completely.
- Initialize the REG_EQUIV_INIT array of initializing insns. */
+ Initialize the REG_EQUIV_INIT array of initializing insns.
-static void
+ Return non-zero if jump label rebuilding should be done. */
+
+int
update_equiv_regs (void)
{
rtx insn;
@@ -1183,6 +1184,8 @@ update_equiv_regs (void)
new_insn = emit_insn_before (PATTERN (equiv_insn), insn);
REG_NOTES (new_insn) = REG_NOTES (equiv_insn);
REG_NOTES (equiv_insn) = 0;
+ /* Rescan it to process the notes. */
+ df_insn_rescan (new_insn);
/* Make sure this insn is recognized before
reload begins, otherwise
@@ -1227,6 +1230,7 @@ update_equiv_regs (void)
end_alias_analysis ();
free (reg_equiv);
+ return recorded_label_ref;
}
/* Mark REG as having no known equivalence.
@@ -2442,6 +2446,12 @@ find_stack_regs (void)
}
#endif
+static bool
+gate_handle_local_alloc (void)
+{
+ return ! flag_ira;
+}
+
/* Run old register allocator. Return TRUE if we must exit
rest_of_compilation upon return. */
static unsigned int
@@ -2517,7 +2527,7 @@ struct rtl_opt_pass pass_local_alloc =
{
RTL_PASS,
"lreg", /* name */
- NULL, /* gate */
+ gate_handle_local_alloc, /* gate */
rest_of_handle_local_alloc, /* execute */
NULL, /* sub */
NULL, /* next */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/alias.c ./alias.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/alias.c 2008-08-21 21:16:46.000000000 -0400
+++ ./alias.c 2008-08-22 18:09:59.000000000 -0400
@@ -1975,6 +1975,34 @@ adjust_offset_for_component_ref (tree x,
return GEN_INT (ioffset);
}
+/* The function returns nonzero if X is an address containg VALUE. */
+static int
+value_addr_p (rtx x)
+{
+ if (GET_CODE (x) == VALUE)
+ return 1;
+ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == VALUE)
+ return 1;
+ return 0;
+}
+
+/* The function returns nonzero if X is a stack address. */
+static int
+stack_addr_p (rtx x)
+{
+ if (x == hard_frame_pointer_rtx || x == frame_pointer_rtx
+ || x == arg_pointer_rtx || x == stack_pointer_rtx)
+ return 1;
+ if (GET_CODE (x) == PLUS
+ && (XEXP (x, 0) == hard_frame_pointer_rtx
+ || XEXP (x, 0) == frame_pointer_rtx
+ || XEXP (x, 0) == arg_pointer_rtx
+ || XEXP (x, 0) == stack_pointer_rtx)
+ && CONSTANT_P (XEXP (x, 1)))
+ return 1;
+ return 0;
+}
+
/* Return nonzero if we can determine the exprs corresponding to memrefs
X and Y and they do not overlap. */
@@ -1984,9 +2012,27 @@ nonoverlapping_memrefs_p (const_rtx x, c
tree exprx = MEM_EXPR (x), expry = MEM_EXPR (y);
rtx rtlx, rtly;
rtx basex, basey;
+ rtx x_addr, y_addr;
rtx moffsetx, moffsety;
HOST_WIDE_INT offsetx = 0, offsety = 0, sizex, sizey, tem;
+ if (flag_ira && optimize && reload_completed)
+ {
+ /* We need this code for IRA because of stack slot sharing. RTL
+ in decl can be different than RTL used in insns. It is a
+ safe code although it can be conservative sometime. */
+ x_addr = canon_rtx (get_addr (XEXP (x, 0)));
+ y_addr = canon_rtx (get_addr (XEXP (y, 0)));
+
+ if (value_addr_p (x_addr) || value_addr_p (y_addr))
+ return 0;
+
+ if (stack_addr_p (x_addr) && stack_addr_p (y_addr)
+ && memrefs_conflict_p (SIZE_FOR_MODE (y), y_addr,
+ SIZE_FOR_MODE (x), x_addr, 0))
+ return 0;
+ }
+
/* Unless both have exprs, we can't tell anything. */
if (exprx == 0 || expry == 0)
return 0;
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/common.opt ./common.opt
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/common.opt 2008-08-21 21:16:46.000000000 -0400
+++ ./common.opt 2008-08-22 21:58:23.000000000 -0400
@@ -649,6 +653,30 @@ Common Report Var(flag_ipa_struct_reorg)
Perform structure layout optimizations based
on profiling information.
+fira
+Common Report Var(flag_ira) Init(0)
+Use integrated register allocator.
+
+fira-algorithm=
+Common Joined RejectNegative
+-fira-algorithm=[regional|CB|mixed] Set the used IRA algorithm
+
+fira-coalesce
+Common Report Var(flag_ira_coalesce) Init(0)
+Do optimistic coalescing.
+
+fira-share-save-slots
+Common Report Var(flag_ira_share_save_slots) Init(1)
+Share slots for saving different hard registers.
+
+fira-share-spill-slots
+Common Report Var(flag_ira_share_spill_slots) Init(1)
+Share stack slots for spilled pseudo-registers.
+
+fira-verbose=
+Common RejectNegative Joined UInteger
+-fira-verbose=<number> Control IRA's level of diagnostic messages.
+
fivopts
Common Report Var(flag_ivopts) Init(1) Optimization
Optimize induction variables on trees
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/regclass.c ./regclass.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/regclass.c 2008-08-21 21:16:46.000000000 -0400
+++ ./regclass.c 2008-08-22 18:10:01.000000000 -0400
@@ -178,7 +178,7 @@ static enum reg_class reg_class_supercla
/* For each reg class, table listing all the classes contained in it. */
-static enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
+enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
/* For each pair of reg classes,
a largest reg class contained in their union. */
@@ -211,24 +211,22 @@ bool have_regs_of_mode [MAX_MACHINE_MODE
/* 1 if class does contain register of given mode. */
-static char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
-
-typedef unsigned short move_table[N_REG_CLASSES];
+char contains_reg_of_mode [N_REG_CLASSES] [MAX_MACHINE_MODE];
/* Maximum cost of moving from a register in one class to a register in
another class. Based on REGISTER_MOVE_COST. */
-static move_table *move_cost[MAX_MACHINE_MODE];
+move_table *move_cost[MAX_MACHINE_MODE];
/* Similar, but here we don't have to move if the first index is a subset
of the second so in that case the cost is zero. */
-static move_table *may_move_in_cost[MAX_MACHINE_MODE];
+move_table *may_move_in_cost[MAX_MACHINE_MODE];
/* Similar, but here we don't have to move if the first index is a superset
of the second so in that case the cost is zero. */
-static move_table *may_move_out_cost[MAX_MACHINE_MODE];
+move_table *may_move_out_cost[MAX_MACHINE_MODE];
/* Keep track of the last mode we initialized move costs for. */
static int last_mode_for_init_move_cost;
@@ -313,7 +311,7 @@ init_reg_sets (void)
/* Initialize may_move_cost and friends for mode M. */
-static void
+void
init_move_cost (enum machine_mode m)
{
static unsigned short last_move_cost[N_REG_CLASSES][N_REG_CLASSES];
@@ -1024,6 +1022,7 @@ reg_preferred_class (int regno)
{
if (reg_pref == 0)
return GENERAL_REGS;
+
return (enum reg_class) reg_pref[regno].prefclass;
}
@@ -2283,6 +2282,32 @@ auto_inc_dec_reg_p (rtx reg, enum machin
}
#endif
+
+/* Allocate space for reg info. */
+void
+allocate_reg_info (void)
+{
+ int size = max_reg_num ();
+
+ gcc_assert (! reg_pref && ! reg_renumber);
+ reg_renumber = XNEWVEC (short, size);
+ reg_pref = XCNEWVEC (struct reg_pref, size);
+ memset (reg_renumber, -1, size * sizeof (short));
+}
+
+
+/* Resize reg info. The new elements will be uninitialized. */
+void
+resize_reg_info (void)
+{
+ int size = max_reg_num ();
+
+ gcc_assert (reg_pref && reg_renumber);
+ reg_renumber = XRESIZEVEC (short, reg_renumber, size);
+ reg_pref = XRESIZEVEC (struct reg_pref, reg_pref, size);
+}
+
+
/* Free up the space allocated by allocate_reg_info. */
void
free_reg_info (void)
@@ -2300,6 +2325,21 @@ free_reg_info (void)
}
}
+
+
+
+/* Set up preferred and alternate classes for REGNO as PREFCLASS and
+ ALTCLASS. */
+void
+setup_reg_classes (int regno,
+ enum reg_class prefclass, enum reg_class altclass)
+{
+ if (reg_pref == NULL)
+ return;
+ reg_pref[regno].prefclass = prefclass;
+ reg_pref[regno].altclass = altclass;
+}
+
/* This is the `regscan' pass of the compiler, run just before cse and
again just before loop. It finds the first and last use of each
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/rtl.h ./rtl.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/rtl.h 2008-08-21 21:16:46.000000000 -0400
+++ ./rtl.h 2008-08-22 18:10:01.000000000 -0400
@@ -1805,6 +1805,12 @@ rtx remove_list_elem (rtx, rtx *);
/* regclass.c */
+/* Initialize may_move_cost and friends for mode M. */
+extern void init_move_cost (enum machine_mode);
+/* Allocate register info memory. */
+extern void allocate_reg_info (void);
+/* Resize reg info. */
+extern void resize_reg_info (void);
/* Free up register info memory. */
extern void free_reg_info (void);
@@ -1815,6 +1821,7 @@ extern const char *decode_asm_operands (
extern enum reg_class reg_preferred_class (int);
extern enum reg_class reg_alternate_class (int);
+extern void setup_reg_classes (int, enum reg_class, enum reg_class);
extern void split_all_insns (void);
extern unsigned int split_all_insns_noflow (void);
@@ -2183,12 +2190,16 @@ extern bool can_copy_p (enum machine_mod
extern rtx fis_get_condition (rtx);
/* In global.c */
+#ifdef HARD_CONST
+extern HARD_REG_SET eliminable_regset;
+#endif
extern void mark_elimination (int, int);
extern void dump_global_regs (FILE *);
#ifdef HARD_CONST
/* Yes, this ifdef is silly, but HARD_REG_SET is not always defined. */
extern void retry_global_alloc (int, HARD_REG_SET);
#endif
+extern void build_insn_chain (void);
/* In regclass.c */
extern int reg_classes_intersect_p (enum reg_class, enum reg_class);
@@ -2214,6 +2225,7 @@ extern void dbr_schedule (rtx);
/* In local-alloc.c */
extern void dump_local_alloc (FILE *);
+extern int update_equiv_regs (void);
/* In reload1.c */
extern int function_invariant_p (const_rtx);
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/Makefile.in ./Makefile.in
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/Makefile.in 2008-08-21 21:16:46.000000000 -0400
+++ ./Makefile.in 2008-08-25 11:35:41.000000000 -0400
@@ -849,6 +849,7 @@ TREE_DATA_REF_H = tree-data-ref.h $(LAMB
VARRAY_H = varray.h $(MACHMODE_H) $(SYSTEM_H) coretypes.h $(TM_H)
TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h
REAL_H = real.h $(MACHMODE_H)
+IRA_INT_H = ira.h ira-int.h $(CFGLOOP_H) alloc-pool.h
DBGCNT_H = dbgcnt.h dbgcnt.def
EBIMAP_H = ebitmap.h sbitmap.h
IPA_PROP_H = ipa-prop.h $(TREE_H) vec.h $(CGRAPH_H)
@@ -1097,6 +1098,13 @@ OBJS-common = \
init-regs.o \
integrate.o \
intl.o \
+ ira.o \
+ ira-build.o \
+ ira-costs.o \
+ ira-conflicts.o \
+ ira-color.o \
+ ira-emit.o \
+ ira-lives.o \
jump.o \
lambda-code.o \
lambda-mat.o \
@@ -2408,7 +2416,7 @@ toplev.o : toplev.c $(CONFIG_H) $(SYSTEM
$(INSN_ATTR_H) output.h $(DIAGNOSTIC_H) debug.h insn-config.h intl.h \
$(RECOG_H) Makefile $(TOPLEV_H) dwarf2out.h sdbout.h dbxout.h $(EXPR_H) \
hard-reg-set.h $(BASIC_BLOCK_H) graph.h except.h $(REGS_H) $(TIMEVAR_H) \
- value-prof.h $(PARAMS_H) $(TM_P_H) reload.h dwarf2asm.h $(TARGET_H) \
+ value-prof.h $(PARAMS_H) $(TM_P_H) reload.h ira.h dwarf2asm.h $(TARGET_H) \
langhooks.h insn-flags.h $(CFGLAYOUT_H) $(CFGLOOP_H) hosthooks.h \
$(CGRAPH_H) $(COVERAGE_H) alloc-pool.h $(GGC_H) $(INTEGRATE_H) \
opts.h params.def tree-mudflap.h $(REAL_H) tree-pass.h $(GIMPLE_H)
@@ -2771,7 +2779,7 @@ cfgloop.o : cfgloop.c $(CONFIG_H) $(SYST
$(GGC_H)
cfgloopanal.o : cfgloopanal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
$(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(EXPR_H) coretypes.h $(TM_H) \
- $(OBSTACK_H) output.h graphds.h
+ $(OBSTACK_H) output.h graphds.h $(PARAMS_H)
graphds.o : graphds.c graphds.h $(CONFIG_H) $(SYSTEM_H) $(BITMAP_H) $(OBSTACK_H) \
coretypes.h vec.h vecprim.h
loop-iv.o : loop-iv.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(BASIC_BLOCK_H) \
@@ -2835,7 +2843,7 @@ reload1.o : reload1.c $(CONFIG_H) $(SYST
$(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
$(BASIC_BLOCK_H) $(RECOG_H) output.h $(FUNCTION_H) $(TOPLEV_H) $(TM_P_H) \
addresses.h except.h $(TREE_H) $(REAL_H) $(FLAGS_H) $(MACHMODE_H) \
- $(OBSTACK_H) $(DF_H) $(TARGET_H) dse.h
+ $(OBSTACK_H) $(DF_H) $(TARGET_H) dse.h ira.h
rtlhooks.o : rtlhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
rtlhooks-def.h $(EXPR_H) $(RECOG_H)
postreload.o : postreload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
@@ -2851,7 +2859,7 @@ postreload-gcse.o : postreload-gcse.c $(
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 $(CFGLOOP_H) ira.h gt-caller-save.h $(GGC_H)
bt-load.o : bt-load.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) except.h \
$(RTL_H) hard-reg-set.h $(REGS_H) $(TM_P_H) $(FIBHEAP_H) output.h $(EXPR_H) \
$(TARGET_H) $(FLAGS_H) $(INSN_ATTR_H) $(FUNCTION_H) tree-pass.h $(TOPLEV_H) \
@@ -2872,6 +2880,37 @@ stack-ptr-mod.o : stack-ptr-mod.c $(CONF
init-regs.o : init-regs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(TREE_H) $(RTL_H) $(REGS_H) $(EXPR_H) tree-pass.h \
$(BASIC_BLOCK_H) $(FLAGS_H) $(DF_H)
+ira-build.o: ira-build.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
+ insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) \
+ $(PARAMS_H) $(DF_H) sparseset.h $(IRA_INT_H)
+ira-costs.o: ira-costs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) insn-config.h $(RECOG_H) \
+ $(REGS_H) hard-reg-set.h $(FLAGS_H) errors.h \
+ $(EXPR_H) $(BASIC_BLOCK_H) $(TM_P_H) \
+ $(IRA_INT_H)
+ira-conflicts.o: ira-conflicts.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
+ insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
+ $(DF_H) sparseset.h $(IRA_INT_H)
+ira-color.o: ira-color.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
+ $(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
+ $(DF_H) $(SPLAY_TREE_H) $(IRA_INT_H)
+ira-emit.o: ira-emit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
+ $(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
+ $(IRA_INT_H)
+ira-lives.o: ira-lives.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(TARGET_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
+ insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) $(PARAMS_H) \
+ $(DF_H) sparseset.h $(IRA_INT_H)
+ira.o: ira.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
+ $(TARGET_H) $(TM_H) $(RTL_H) $(RECOG_H) \
+ $(REGS_H) hard-reg-set.h $(FLAGS_H) $(OBSTACK_H) \
+ $(EXPR_H) $(BASIC_BLOCK_H) $(TOPLEV_H) $(TM_P_H) \
+ $(DF_H) $(IRA_INT_H) $(PARAMS_H) $(TIMEVAR_H) $(INTEGRATE_H) \
+ tree-pass.h output.h
regmove.o : regmove.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
insn-config.h $(TIMEVAR_H) tree-pass.h $(DF_H)\
$(RECOG_H) output.h $(REGS_H) hard-reg-set.h $(FLAGS_H) $(FUNCTION_H) \
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/passes.c ./passes.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/passes.c 2008-08-21 21:16:46.000000000 -0400
+++ ./passes.c 2008-08-22 21:58:34.000000000 -0400
@@ -767,6 +767,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_subregs_of_mode_init);
NEXT_PASS (pass_local_alloc);
NEXT_PASS (pass_global_alloc);
+ NEXT_PASS (pass_ira);
NEXT_PASS (pass_subregs_of_mode_finish);
NEXT_PASS (pass_postreload);
{
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/params.def ./params.def
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/params.def 2008-08-21 21:16:46.000000000 -0400
+++ ./params.def 2008-08-22 18:10:01.000000000 -0400
@@ -710,6 +710,11 @@ DEFPARAM (PARAM_DF_DOUBLE_QUEUE_THRESHOL
"Multiplier used for determining the double-queueing threshold",
2, 0, 0)
+DEFPARAM (PARAM_IRA_MAX_LOOPS_NUM,
+ "ira-max-loops-num",
+ "max loops number for regional RA",
+ 50, 0, 0)
+
/* Switch initialization conversion will refuse to create arrays that are
bigger than this parameter times the number of switch branches. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload1.c ./reload1.c
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/reload1.c 2008-08-21 21:16:46.000000000 -0400
+++ ./reload1.c 2008-08-25 11:35:41.000000000 -0400
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.
#include "toplev.h"
#include "except.h"
#include "tree.h"
+#include "ira.h"
#include "df.h"
#include "target.h"
#include "dse.h"
@@ -257,6 +258,9 @@ static unsigned int spill_stack_slot_wid
/* Record which pseudos needed to be spilled. */
static regset_head spilled_pseudos;
+/* Record which pseudos changed their allocation in finish_spills. */
+static regset_head changed_allocation_pseudos;
+
/* Used for communication between order_regs_for_reload and count_pseudo.
Used to avoid counting one pseudo twice. */
static regset_head pseudos_counted;
@@ -389,7 +393,7 @@ static void delete_caller_save_insns (vo
static void spill_failure (rtx, enum reg_class);
static void count_spilled_pseudo (int, int, int);
static void delete_dead_insn (rtx);
-static void alter_reg (int, int);
+static void alter_reg (int, int, bool);
static void set_label_offsets (rtx, rtx, int);
static void check_eliminable_occurrences (rtx);
static void elimination_effects (rtx, enum machine_mode);
@@ -443,6 +447,8 @@ static rtx inc_for_reload (rtx, rtx, rtx
static void add_auto_inc_notes (rtx, rtx);
#endif
static void copy_eh_notes (rtx, rtx);
+static void substitute (rtx *, const_rtx, rtx);
+static bool gen_reload_chain_without_interm_reg_p (int, int);
static int reloads_conflict (int, int);
static rtx gen_reload (rtx, rtx, int, enum reload_type);
static rtx emit_insn_if_valid_for_reload (rtx);
@@ -501,6 +507,7 @@ init_reload (void)
reload_startobj = XOBNEWVAR (&reload_obstack, char, 0);
INIT_REG_SET (&spilled_pseudos);
+ INIT_REG_SET (&changed_allocation_pseudos);
INIT_REG_SET (&pseudos_counted);
}
@@ -518,6 +525,7 @@ new_insn_chain (void)
c = XOBNEW (&reload_obstack, struct insn_chain);
INIT_REG_SET (&c->live_throughout);
INIT_REG_SET (&c->dead_or_set);
+ INIT_REG_SET (&c->saved);
}
else
{
@@ -546,11 +554,11 @@ compute_use_by_pseudos (HARD_REG_SET *to
if (r < 0)
{
- /* reload_combine uses the information from
- DF_LIVE_IN (BASIC_BLOCK), which might still
- contain registers that have not actually been allocated
- since they have an equivalence. */
- gcc_assert (reload_completed);
+ /* reload_combine uses the information from DF_LIVE_IN,
+ which might still contain registers that have not
+ actually been allocated since they have an
+ equivalence. */
+ gcc_assert ((flag_ira && optimize) || reload_completed);
}
else
add_to_hard_reg_set (to, PSEUDO_REGNO_MODE (regno), r);
@@ -684,6 +692,9 @@ static int something_needs_operands_chan
/* Nonzero means we couldn't get enough spill regs. */
static int failure;
+/* Temporary array of pseudo-register number. */
+static int *temp_pseudo_reg_arr;
+
/* Main entry point for the reload pass.
FIRST is the first insn of the function being compiled.
@@ -700,7 +711,7 @@ static int failure;
int
reload (rtx first, int global)
{
- int i;
+ int i, n;
rtx insn;
struct elim_table *ep;
basic_block bb;
@@ -883,12 +894,21 @@ reload (rtx first, int global)
offsets_known_at = XNEWVEC (char, num_labels);
offsets_at = (HOST_WIDE_INT (*)[NUM_ELIMINABLE_REGS]) xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
- /* Alter each pseudo-reg rtx to contain its hard reg number.
- Assign stack slots to the pseudos that lack hard regs or equivalents.
+ /* Alter each pseudo-reg rtx to contain its hard reg number. Assign
+ stack slots to the pseudos that lack hard regs or equivalents.
Do not touch virtual registers. */
- for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
- alter_reg (i, -1);
+ temp_pseudo_reg_arr = XNEWVEC (int, max_regno - LAST_VIRTUAL_REGISTER - 1);
+ for (n = 0, i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+ temp_pseudo_reg_arr[n++] = i;
+
+ if (flag_ira && optimize)
+ /* Ask IRA to order pseudo-registers for better stack slot
+ sharing. */
+ ira_sort_regnos_for_alter_reg (temp_pseudo_reg_arr, n, reg_max_ref_width);
+
+ for (i = 0; i < n; i++)
+ alter_reg (temp_pseudo_reg_arr[i], -1, false);
/* If we have some registers we think can be eliminated, scan all insns to
see if there is an insn that sets one of these registers to something
@@ -1002,7 +1022,7 @@ reload (rtx first, int global)
the loop. */
reg_equiv_memory_loc[i] = 0;
reg_equiv_init[i] = 0;
- alter_reg (i, -1);
+ alter_reg (i, -1, true);
}
}
@@ -1036,7 +1056,12 @@ reload (rtx first, int global)
calculate_needs_all_insns (global);
- CLEAR_REG_SET (&spilled_pseudos);
+ if (! flag_ira || ! optimize)
+ /* Don't do it for IRA. We need this info because we don't
+ change live_throughout and dead_or_set for chains when IRA
+ is used. */
+ CLEAR_REG_SET (&spilled_pseudos);
+
did_spill = 0;
something_changed = 0;
@@ -1094,6 +1119,11 @@ reload (rtx first, int global)
obstack_free (&reload_obstack, reload_firstobj);
}
+ if (flag_ira && optimize)
+ /* Restore the original insn chain order for correct reload work
+ (e.g. for correct inheritance). */
+ ira_sort_insn_chain (false);
+
/* If global-alloc was run, notify it of any register eliminations we have
done. */
if (global)
@@ -1163,6 +1193,7 @@ reload (rtx first, int global)
regs. */
failed:
+ CLEAR_REG_SET (&changed_allocation_pseudos);
CLEAR_REG_SET (&spilled_pseudos);
reload_in_progress = 0;
@@ -1333,6 +1364,8 @@ reload (rtx first, int global)
VEC_free (rtx, gc, reg_equiv_memory_loc_vec);
reg_equiv_memory_loc = 0;
+ free (temp_pseudo_reg_arr);
+
if (offsets_known_at)
free (offsets_known_at);
if (offsets_at)
@@ -1573,10 +1606,24 @@ calculate_needs_all_insns (int global)
{
rtx set = single_set (insn);
if (set
- && SET_SRC (set) == SET_DEST (set)
- && REG_P (SET_SRC (set))
- && REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER)
+ &&
+ ((SET_SRC (set) == SET_DEST (set)
+ && REG_P (SET_SRC (set))
+ && REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER)
+ || (REG_P (SET_SRC (set)) && REG_P (SET_DEST (set))
+ && reg_renumber[REGNO (SET_SRC (set))] < 0
+ && reg_renumber[REGNO (SET_DEST (set))] < 0
+ && reg_equiv_memory_loc[REGNO (SET_SRC (set))] != NULL
+ && reg_equiv_memory_loc[REGNO (SET_DEST (set))] != NULL
+ && rtx_equal_p (reg_equiv_memory_loc
+ [REGNO (SET_SRC (set))],
+ reg_equiv_memory_loc
+ [REGNO (SET_DEST (set))]))))
{
+ if (flag_ira && optimize)
+ /* Inform IRA about the insn deletion. */
+ ira_mark_memory_move_deletion (REGNO (SET_DEST (set)),
+ REGNO (SET_SRC (set)));
delete_insn (insn);
/* Delete it from the reload chain. */
if (chain->prev)
@@ -1665,6 +1712,10 @@ static int spill_cost[FIRST_PSEUDO_REGIS
only the first hard reg for a multi-reg pseudo. */
static int spill_add_cost[FIRST_PSEUDO_REGISTER];
+/* Map of hard regno to pseudo regno currently occupying the hard
+ reg. */
+static int hard_regno_to_pseudo_regno[FIRST_PSEUDO_REGISTER];
+
/* Update the spill cost arrays, considering that pseudo REG is live. */
static void
@@ -1675,7 +1726,10 @@ count_pseudo (int reg)
int nregs;
if (REGNO_REG_SET_P (&pseudos_counted, reg)
- || REGNO_REG_SET_P (&spilled_pseudos, reg))
+ || REGNO_REG_SET_P (&spilled_pseudos, reg)
+ /* Ignore spilled pseudo-registers which can be here only if IRA
+ is used. */
+ || (flag_ira && optimize && r < 0))
return;
SET_REGNO_REG_SET (&pseudos_counted, reg);
@@ -1683,10 +1737,12 @@ count_pseudo (int reg)
gcc_assert (r >= 0);
spill_add_cost[r] += freq;
-
nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
while (nregs-- > 0)
- spill_cost[r + nregs] += freq;
+ {
+ hard_regno_to_pseudo_regno[r + nregs] = reg;
+ spill_cost[r + nregs] += freq;
+ }
}
/* Calculate the SPILL_COST and SPILL_ADD_COST arrays and determine the
@@ -1704,6 +1760,8 @@ order_regs_for_reload (struct insn_chain
memset (spill_cost, 0, sizeof spill_cost);
memset (spill_add_cost, 0, sizeof spill_add_cost);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ hard_regno_to_pseudo_regno[i] = -1;
/* Count number of uses of each hard reg by pseudo regs allocated to it
and then order them by decreasing use. First exclude hard registers
@@ -1721,11 +1779,13 @@ order_regs_for_reload (struct insn_chain
EXECUTE_IF_SET_IN_REG_SET
(&chain->live_throughout, FIRST_PSEUDO_REGISTER, i, rsi)
{
- count_pseudo (i);
+ if (!REGNO_REG_SET_P (&chain->saved, i))
+ count_pseudo (i);
}
EXECUTE_IF_SET_IN_REG_SET
(&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i, rsi)
{
+ gcc_assert (!REGNO_REG_SET_P (&chain->saved, i));
count_pseudo (i);
}
CLEAR_REG_SET (&pseudos_counted);
@@ -1746,18 +1806,25 @@ static HARD_REG_SET used_spill_regs_loca
static void
count_spilled_pseudo (int spilled, int spilled_nregs, int reg)
{
+ int freq = REG_FREQ (reg);
int r = reg_renumber[reg];
int nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
- if (REGNO_REG_SET_P (&spilled_pseudos, reg)
+ /* Ignore spilled pseudo-registers which can be here only if IRA is
+ used. */
+ if ((flag_ira && optimize && r < 0)
+ || REGNO_REG_SET_P (&spilled_pseudos, reg)
|| spilled + spilled_nregs <= r || r + nregs <= spilled)
return;
SET_REGNO_REG_SET (&spilled_pseudos, reg);
- spill_add_cost[r] -= REG_FREQ (reg);
+ spill_add_cost[r] -= freq;
while (nregs-- > 0)
- spill_cost[r + nregs] -= REG_FREQ (reg);
+ {
+ hard_regno_to_pseudo_regno[r + nregs] = -1;
+ spill_cost[r + nregs] -= freq;
+ }
}
/* Find reload register to use for reload number ORDER. */
@@ -1769,11 +1836,13 @@ find_reg (struct insn_chain *chain, int
struct reload *rl = rld + rnum;
int best_cost = INT_MAX;
int best_reg = -1;
- unsigned int i, j;
+ unsigned int i, j, n;
int k;
HARD_REG_SET not_usable;
HARD_REG_SET used_by_other_reload;
reg_set_iterator rsi;
+ static int regno_pseudo_regs[FIRST_PSEUDO_REGISTER];
+ static int best_regno_pseudo_regs[FIRST_PSEUDO_REGISTER];
COPY_HARD_REG_SET (not_usable, bad_spill_regs);
IOR_HARD_REG_SET (not_usable, bad_spill_regs_global);
@@ -1791,7 +1860,11 @@ find_reg (struct insn_chain *chain, int
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
+#ifdef REG_ALLOC_ORDER
+ unsigned int regno = reg_alloc_order[i];
+#else
unsigned int regno = i;
+#endif
if (! TEST_HARD_REG_BIT (not_usable, regno)
&& ! TEST_HARD_REG_BIT (used_by_other_reload, regno)
@@ -1810,6 +1883,38 @@ find_reg (struct insn_chain *chain, int
}
if (! ok)
continue;
+
+ if (flag_ira && optimize)
+ {
+ /* Ask IRA to find a better pseudo-register for
+ spilling. */
+ for (n = j = 0; j < this_nregs; j++)
+ {
+ int r = hard_regno_to_pseudo_regno[regno + j];
+
+ if (r < 0)
+ continue;
+ if (n == 0 || regno_pseudo_regs[n - 1] != r)
+ regno_pseudo_regs[n++] = r;
+ }
+ regno_pseudo_regs[n++] = -1;
+ if (best_reg < 0
+ || ira_better_spill_reload_regno_p (regno_pseudo_regs,
+ best_regno_pseudo_regs,
+ rl->in, rl->out,
+ chain->insn))
+ {
+ best_reg = regno;
+ for (j = 0;; j++)
+ {
+ best_regno_pseudo_regs[j] = regno_pseudo_regs[j];
+ if (regno_pseudo_regs[j] < 0)
+ break;
+ }
+ }
+ continue;
+ }
+
if (rl->in && REG_P (rl->in) && REGNO (rl->in) == regno)
this_cost--;
if (rl->out && REG_P (rl->out) && REGNO (rl->out) == regno)
@@ -1841,15 +1946,36 @@ find_reg (struct insn_chain *chain, int
rl->nregs = hard_regno_nregs[best_reg][rl->mode];
rl->regno = best_reg;
+ if (dump_file != NULL)
+ EXECUTE_IF_SET_IN_REG_SET
+ (&chain->saved, FIRST_PSEUDO_REGISTER, j, rsi)
+ {
+ int nregs;
+ int r = reg_renumber[j];
+
+ if (r < 0)
+ continue;
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (j)];
+ if ((best_reg <= r && r < best_reg + (int) rl->nregs)
+ || (r <= best_reg && best_reg < r + nregs))
+ {
+ fprintf (dump_file, "using saved reg %d (of %u) for insn %u\n",
+ r, j, INSN_UID (chain->insn));
+ break;
+ }
+ }
+
EXECUTE_IF_SET_IN_REG_SET
(&chain->live_throughout, FIRST_PSEUDO_REGISTER, j, rsi)
{
- count_spilled_pseudo (best_reg, rl->nregs, j);
+ if (!REGNO_REG_SET_P (&chain->saved, j))
+ count_spilled_pseudo (best_reg, rl->nregs, j);
}
EXECUTE_IF_SET_IN_REG_SET
(&chain->dead_or_set, FIRST_PSEUDO_REGISTER, j, rsi)
{
+ gcc_assert (!REGNO_REG_SET_P (&chain->saved, j));
count_spilled_pseudo (best_reg, rl->nregs, j);
}
@@ -1857,6 +1983,7 @@ find_reg (struct insn_chain *chain, int
{
gcc_assert (spill_cost[best_reg + i] == 0);
gcc_assert (spill_add_cost[best_reg + i] == 0);
+ gcc_assert (hard_regno_to_pseudo_regno[best_reg + i] == -1);
SET_HARD_REG_BIT (used_spill_regs_local, best_reg + i);
}
return 1;
@@ -1948,13 +2075,18 @@ delete_caller_save_insns (void)
{
struct insn_chain *c = reload_insn_chain;
- while (c != 0)
+ CLEAR_HARD_REG_SET (used_spill_regs);
+ for (;;)
{
while (c != 0 && c->is_caller_save_insn)
{
struct insn_chain *next = c->next;
rtx insn = c->insn;
+ if (dump_file)
+ fprintf (dump_file, "removing caller save insn %u\n",
+ INSN_UID (insn));
+
if (c == reload_insn_chain)
reload_insn_chain = next;
delete_insn (insn);
@@ -1967,8 +2099,52 @@ delete_caller_save_insns (void)
unused_insn_chains = c;
c = next;
}
- if (c != 0)
- c = c->next;
+ if (c == 0)
+ break;
+ /* Invalidating spill hard registers got from caller-saved hard
+ registers. */
+ if (c->need_reload)
+ {
+ HARD_REG_SET saved;
+ int i;
+ unsigned int regno;
+ reg_set_iterator rsi;
+
+ CLEAR_HARD_REG_SET (saved);
+ EXECUTE_IF_SET_IN_REG_SET (&c->saved, FIRST_PSEUDO_REGISTER,
+ regno, rsi)
+ {
+ int r = reg_renumber[regno];
+
+ if (r < 0)
+ continue;
+ add_to_hard_reg_set (&saved, PSEUDO_REGNO_MODE (regno), r);
+ }
+ if (!hard_reg_set_empty_p (saved))
+ for (i = 0; i < c->n_reloads; i++)
+ {
+ int nregs;
+ struct reload *rl = &c->rld[i];
+ int r = rl->regno;
+
+ if (rl->regno < 0)
+ continue;
+ nregs = rl->nregs;
+ for (nregs--; nregs >= 0; nregs--)
+ if (TEST_HARD_REG_BIT (saved, r + nregs))
+ {
+ if (dump_file != NULL)
+ fprintf (dump_file,
+ "invalidating %d reg for insn %u reload\n",
+ rl->regno, INSN_UID (c->insn));
+ rl->regno = -1;
+ break;
+ }
+ }
+ AND_COMPL_HARD_REG_SET (c->used_spill_regs, saved);
+ IOR_HARD_REG_SET (used_spill_regs, c->used_spill_regs);
+ }
+ c = c->next;
}
}
@@ -2026,7 +2202,7 @@ delete_dead_insn (rtx insn)
can share one stack slot. */
static void
-alter_reg (int i, int from_reg)
+alter_reg (int i, int from_reg, bool dont_share_p)
{
/* When outputting an inline function, this can happen
for a reg that isn't actually used. */
@@ -2059,7 +2235,15 @@ alter_reg (int i, int from_reg)
unsigned int total_size = MAX (inherent_size, reg_max_ref_width[i]);
unsigned int min_align = reg_max_ref_width[i] * BITS_PER_UNIT;
int adjust = 0;
+ bool shared_p = false;
+ if (flag_ira && optimize)
+ /* Mark the spill for IRA. */
+ SET_REGNO_REG_SET (&spilled_pseudos, i);
+ x = (dont_share_p || ! flag_ira || ! optimize
+ ? NULL_RTX : ira_reuse_stack_slot (i, inherent_size, total_size));
+ if (x)
+ shared_p = true;
/* Each pseudo reg has an inherent size which comes from its own mode,
and a total size which provides room for paradoxical subregs
which refer to the pseudo reg in wider modes.
@@ -2068,7 +2252,7 @@ alter_reg (int i, int from_reg)
enough inherent space and enough total space.
Otherwise, we allocate a new slot, making sure that it has no less
inherent space, and no less total space, then the previous slot. */
- if (from_reg == -1)
+ else if (from_reg == -1 || (! dont_share_p && flag_ira && optimize))
{
alias_set_type alias_set = new_alias_set ();
@@ -2086,6 +2270,10 @@ alter_reg (int i, int from_reg)
/* Nothing can alias this slot except this pseudo. */
set_mem_alias_set (x, alias_set);
dse_record_singleton_alias_set (alias_set, mode);
+
+ if (! dont_share_p && flag_ira && optimize)
+ /* Inform IRA about allocation a new stack slot. */
+ ira_mark_new_stack_slot (x, i, total_size);
}
/* Reuse a stack slot if possible. */
@@ -2164,8 +2352,13 @@ alter_reg (int i, int from_reg)
/* If we have a decl for the original register, set it for the
memory. If this is a shared MEM, make a copy. */
- if (REG_EXPR (regno_reg_rtx[i])
- && DECL_P (REG_EXPR (regno_reg_rtx[i])))
+ if (shared_p)
+ {
+ x = copy_rtx (x);
+ set_mem_attrs_from_reg (x, regno_reg_rtx[i]);
+ }
+ else if (REG_EXPR (regno_reg_rtx[i])
+ && DECL_P (REG_EXPR (regno_reg_rtx[i])))
{
rtx decl = DECL_RTL_IF_SET (REG_EXPR (regno_reg_rtx[i]));
@@ -2441,7 +2634,7 @@ eliminate_regs_1 (rtx x, enum machine_mo
/* There exists at least one use of REGNO that cannot be
eliminated. Prevent the defining insn from being deleted. */
reg_equiv_init[regno] = NULL_RTX;
- alter_reg (regno, -1);
+ alter_reg (regno, -1, true);
}
return x;
@@ -3817,18 +4010,22 @@ finish_spills (int global)
spill_reg_order[i] = -1;
EXECUTE_IF_SET_IN_REG_SET (&spilled_pseudos, FIRST_PSEUDO_REGISTER, i, rsi)
- {
- /* Record the current hard register the pseudo is allocated to in
- pseudo_previous_regs so we avoid reallocating it to the same
- hard reg in a later pass. */
- gcc_assert (reg_renumber[i] >= 0);
-
- SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
- /* Mark it as no longer having a hard register home. */
- reg_renumber[i] = -1;
- /* We will need to scan everything again. */
- something_changed = 1;
- }
+ if (! flag_ira || ! optimize || reg_renumber[i] >= 0)
+ {
+ /* Record the current hard register the pseudo is allocated to
+ in pseudo_previous_regs so we avoid reallocating it to the
+ same hard reg in a later pass. */
+ gcc_assert (reg_renumber[i] >= 0);
+
+ SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
+ /* Mark it as no longer having a hard register home. */
+ reg_renumber[i] = -1;
+ if (flag_ira && optimize)
+ /* Inform IRA about the change. */
+ ira_mark_allocation_change (i);
+ /* We will need to scan everything again. */
+ something_changed = 1;
+ }
/* Retry global register allocation if possible. */
if (global)
@@ -3848,29 +4045,56 @@ finish_spills (int global)
EXECUTE_IF_SET_IN_REG_SET
(&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i, rsi)
{
+ gcc_assert (!REGNO_REG_SET_P (&chain->saved, i));
IOR_HARD_REG_SET (pseudo_forbidden_regs[i],
chain->used_spill_regs);
}
}
- /* Retry allocating the spilled pseudos. For each reg, merge the
- various reg sets that indicate which hard regs can't be used,
- and call retry_global_alloc.
- We change spill_pseudos here to only contain pseudos that did not
- get a new hard register. */
- for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
- if (reg_old_renumber[i] != reg_renumber[i])
- {
- HARD_REG_SET forbidden;
- COPY_HARD_REG_SET (forbidden, bad_spill_regs_global);
- IOR_HARD_REG_SET (forbidden, pseudo_forbidden_regs[i]);
- IOR_HARD_REG_SET (forbidden, pseudo_previous_regs[i]);
- retry_global_alloc (i, forbidden);
- if (reg_renumber[i] >= 0)
- CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
- }
- }
+ if (! flag_ira || ! optimize)
+ {
+ /* Retry allocating the spilled pseudos. For each reg,
+ merge the various reg sets that indicate which hard regs
+ can't be used, and call retry_global_alloc. We change
+ spill_pseudos here to only contain pseudos that did not
+ get a new hard register. */
+ for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
+ if (reg_old_renumber[i] != reg_renumber[i])
+ {
+ HARD_REG_SET forbidden;
+
+ COPY_HARD_REG_SET (forbidden, bad_spill_regs_global);
+ IOR_HARD_REG_SET (forbidden, pseudo_forbidden_regs[i]);
+ IOR_HARD_REG_SET (forbidden, pseudo_previous_regs[i]);
+ retry_global_alloc (i, forbidden);
+ if (reg_renumber[i] >= 0)
+ CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
+ }
+ }
+ else
+ {
+ /* Retry allocating the pseudos spilled in IRA and the
+ reload. For each reg, merge the various reg sets that
+ indicate which hard regs can't be used, and call
+ ira_reassign_pseudos. */
+ unsigned int n;
+ for (n = 0, i = FIRST_PSEUDO_REGISTER; i < (unsigned) max_regno; i++)
+ if (reg_old_renumber[i] != reg_renumber[i])
+ {
+ if (reg_renumber[i] < 0)
+ temp_pseudo_reg_arr[n++] = i;
+ else
+ CLEAR_REGNO_REG_SET (&spilled_pseudos, i);
+ }
+ if (ira_reassign_pseudos (temp_pseudo_reg_arr, n,
+ bad_spill_regs_global,
+ pseudo_forbidden_regs, pseudo_previous_regs,
+ &spilled_pseudos))
+ something_changed = 1;
+
+ }
+ }
/* Fix up the register information in the insn chain.
This involves deleting those of the spilled pseudos which did not get
a new hard register home from the live_{before,after} sets. */
@@ -3879,9 +4103,14 @@ finish_spills (int global)
HARD_REG_SET used_by_pseudos;
HARD_REG_SET used_by_pseudos2;
- AND_COMPL_REG_SET (&chain->live_throughout, &spilled_pseudos);
- AND_COMPL_REG_SET (&chain->dead_or_set, &spilled_pseudos);
-
+ if (! flag_ira || ! optimize)
+ {
+ /* Don't do it for IRA because IRA and the reload still can
+ assign hard registers to the spilled pseudos on next
+ reload iterations. */
+ AND_COMPL_REG_SET (&chain->live_throughout, &spilled_pseudos);
+ AND_COMPL_REG_SET (&chain->dead_or_set, &spilled_pseudos);
+ }
/* Mark any unallocated hard regs as available for spills. That
makes inheritance work somewhat better. */
if (chain->need_reload)
@@ -3890,20 +4119,18 @@ finish_spills (int global)
REG_SET_TO_HARD_REG_SET (used_by_pseudos2, &chain->dead_or_set);
IOR_HARD_REG_SET (used_by_pseudos, used_by_pseudos2);
- /* Save the old value for the sanity test below. */
- COPY_HARD_REG_SET (used_by_pseudos2, chain->used_spill_regs);
-
compute_use_by_pseudos (&used_by_pseudos, &chain->live_throughout);
compute_use_by_pseudos (&used_by_pseudos, &chain->dead_or_set);
+ /* Value of chain->used_spill_regs from previous iteration
+ may be not included in the value calculated here because
+ of possible removing caller-saves insns (see function
+ delete_caller_save_insns. */
COMPL_HARD_REG_SET (chain->used_spill_regs, used_by_pseudos);
AND_HARD_REG_SET (chain->used_spill_regs, used_spill_regs);
-
- /* Make sure we only enlarge the set. */
- gcc_assert (hard_reg_set_subset_p (used_by_pseudos2,
- chain->used_spill_regs));
}
}
+ CLEAR_REG_SET (&changed_allocation_pseudos);
/* Let alter_reg modify the reg rtx's for the modified pseudos. */
for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
{
@@ -3911,7 +4138,9 @@ finish_spills (int global)
if (reg_old_renumber[i] == regno)
continue;
- alter_reg (i, reg_old_renumber[i]);
+ SET_REGNO_REG_SET (&changed_allocation_pseudos, i);
+
+ alter_reg (i, reg_old_renumber[i], false);
reg_old_renumber[i] = regno;
if (dump_file)
{
@@ -4295,8 +4524,8 @@ reload_as_needed (int live_known)
be partially clobbered by the call. */
else if (CALL_P (insn))
{
- AND_COMPL_HARD_REG_SET (reg_reloaded_valid, call_used_reg_set);
- AND_COMPL_HARD_REG_SET (reg_reloaded_valid, reg_reloaded_call_part_clobbered);
+ AND_COMPL_HARD_REG_SET (reg_reloaded_valid, call_used_reg_set);
+ AND_COMPL_HARD_REG_SET (reg_reloaded_valid, reg_reloaded_call_part_clobbered);
}
}
@@ -4967,6 +5196,126 @@ reloads_unique_chain_p (int r1, int r2)
return true;
}
+
+/* The recursive function change all occurrences of WHAT in *WHERE
+ onto REPL. */
+static void
+substitute (rtx *where, const_rtx what, rtx repl)
+{
+ const char *fmt;
+ int i;
+ enum rtx_code code;
+
+ if (*where == 0)
+ return;
+
+ if (*where == what || rtx_equal_p (*where, what))
+ {
+ *where = repl;
+ return;
+ }
+
+ code = GET_CODE (*where);
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ int j;
+
+ for (j = XVECLEN (*where, i) - 1; j >= 0; j--)
+ substitute (&XVECEXP (*where, i, j), what, repl);
+ }
+ else if (fmt[i] == 'e')
+ substitute (&XEXP (*where, i), what, repl);
+ }
+}
+
+/* The function returns TRUE if chain of reload R1 and R2 (in any
+ order) can be evaluated without usage of intermediate register for
+ the reload containing another reload. It is important to see
+ gen_reload to understand what the function is trying to do. As an
+ example, let us have reload chain
+
+ r2: const
+ r1: <something> + const
+
+ and reload R2 got reload reg HR. The function returns true if
+ there is a correct insn HR = HR + <something>. Otherwise,
+ gen_reload will use intermediate register (and this is the reload
+ reg for R1) to reload <something>.
+
+ We need this function to find a conflict for chain reloads. In our
+ example, if HR = HR + <something> is incorrect insn, then we cannot
+ use HR as a reload register for R2. If we do use it then we get a
+ wrong code:
+
+ HR = const
+ HR = <something>
+ HR = HR + HR
+
+*/
+static bool
+gen_reload_chain_without_interm_reg_p (int r1, int r2)
+{
+ bool result;
+ int regno, n, code;
+ rtx out, in, tem, insn;
+ rtx last = get_last_insn ();
+
+ /* Make r2 a component of r1. */
+ if (reg_mentioned_p (rld[r1].in, rld[r2].in))
+ {
+ n = r1;
+ r1 = r2;
+ r2 = n;
+ }
+ gcc_assert (reg_mentioned_p (rld[r2].in, rld[r1].in));
+ regno = rld[r1].regno >= 0 ? rld[r1].regno : rld[r2].regno;
+ gcc_assert (regno >= 0);
+ out = gen_rtx_REG (rld[r1].mode, regno);
+ in = copy_rtx (rld[r1].in);
+ substitute (&in, rld[r2].in, gen_rtx_REG (rld[r2].mode, regno));
+
+ /* If IN is a paradoxical SUBREG, remove it and try to put the
+ opposite SUBREG on OUT. Likewise for a paradoxical SUBREG on OUT. */
+ if (GET_CODE (in) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (in))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))
+ && (tem = gen_lowpart_common (GET_MODE (SUBREG_REG (in)), out)) != 0)
+ in = SUBREG_REG (in), out = tem;
+
+ if (GET_CODE (in) == PLUS
+ && (REG_P (XEXP (in, 0))
+ || GET_CODE (XEXP (in, 0)) == SUBREG
+ || MEM_P (XEXP (in, 0)))
+ && (REG_P (XEXP (in, 1))
+ || GET_CODE (XEXP (in, 1)) == SUBREG
+ || CONSTANT_P (XEXP (in, 1))
+ || MEM_P (XEXP (in, 1))))
+ {
+ insn = emit_insn (gen_rtx_SET (VOIDmode, out, in));
+ code = recog_memoized (insn);
+ result = false;
+
+ if (code >= 0)
+ {
+ extract_insn (insn);
+ /* We want constrain operands to treat this insn strictly in
+ its validity determination, i.e., the way it would after
+ reload has completed. */
+ result = constrain_operands (1);
+ }
+
+ delete_insns_since (last);
+ return result;
+ }
+
+ /* It looks like other cases in gen_reload are not possible for
+ chain reloads or do need an intermediate hard registers. */
+ return true;
+}
+
/* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
Return 0 otherwise.
@@ -5016,7 +5365,8 @@ reloads_conflict (int r1, int r2)
case RELOAD_FOR_OPERAND_ADDRESS:
return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
|| (r2_type == RELOAD_FOR_OPERAND_ADDRESS
- && !reloads_unique_chain_p (r1, r2)));
+ && (!reloads_unique_chain_p (r1, r2)
+ || !gen_reload_chain_without_interm_reg_p (r1, r2))));
case RELOAD_FOR_OPADDR_ADDR:
return (r2_type == RELOAD_FOR_INPUT
@@ -6724,7 +7074,10 @@ emit_input_reload_insns (struct insn_cha
&& REG_N_SETS (REGNO (old)) == 1)
{
reg_renumber[REGNO (old)] = REGNO (reloadreg);
- alter_reg (REGNO (old), -1);
+ if (flag_ira && optimize)
+ /* Inform IRA about the change. */
+ ira_mark_allocation_change (REGNO (old));
+ alter_reg (REGNO (old), -1, false);
}
special = 1;
}
@@ -8161,7 +8514,7 @@ delete_output_reload (rtx insn, int j, i
n_occurrences += count_occurrences (PATTERN (insn),
eliminate_regs (substed, 0,
NULL_RTX), 0);
- for (i1 = reg_equiv_alt_mem_list [REGNO (reg)]; i1; i1 = XEXP (i1, 1))
+ for (i1 = reg_equiv_alt_mem_list[REGNO (reg)]; i1; i1 = XEXP (i1, 1))
{
gcc_assert (!rtx_equal_p (XEXP (i1, 0), substed));
n_occurrences += count_occurrences (PATTERN (insn), XEXP (i1, 0), 0);
@@ -8262,7 +8615,10 @@ delete_output_reload (rtx insn, int j, i
/* For the debugging info, say the pseudo lives in this reload reg. */
reg_renumber[REGNO (reg)] = REGNO (new_reload_reg);
- alter_reg (REGNO (reg), -1);
+ if (flag_ira && optimize)
+ /* Inform IRA about the change. */
+ ira_mark_allocation_change (REGNO (reg));
+ alter_reg (REGNO (reg), -1, false);
}
else
{
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/s390/s390.h ./config/s390/s390.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/s390/s390.h 2008-08-21 21:16:18.000000000 -0400
+++ ./config/s390/s390.h 2008-08-22 21:58:42.000000000 -0400
@@ -478,6 +478,30 @@ enum reg_class
{ 0xffffffff, 0x0000003f }, /* ALL_REGS */ \
}
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FP_REGS, CC_REGS, ACCESS_REGS, LIM_REG_CLASSES \
+}
+
+/* In some case register allocation order is not enough for IRA to
+ generate a good code. The following macro (if defined) increases
+ cost of REGNO for a pseudo approximately by pseudo usage frequency
+ multiplied by the macro value.
+
+ We avoid usage of BASE_REGNUM by nonzero macro value because the
+ reload can decide not to use the hard register because some
+ constant was forced to be in memory. */
+#define IRA_HARD_REGNO_ADD_COST_MULTIPLIER(regno) \
+ (regno == BASE_REGNUM ? 0.0 : 0.5)
+
/* Register -> class mapping. */
extern const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER];
#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO])
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sparc/sparc.h ./config/sparc/sparc.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sparc/sparc.h 2008-08-21 21:16:20.000000000 -0400
+++ ./config/sparc/sparc.h 2008-08-22 18:09:36.000000000 -0400
@@ -1078,6 +1078,19 @@ enum reg_class { NO_REGS, FPCC_REGS, I64
{-1, -1, -1, 0x20}, /* GENERAL_OR_EXTRA_FP_REGS */ \
{-1, -1, -1, 0x3f}} /* ALL_REGS */
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, EXTRA_FP_REGS, FPCC_REGS, LIM_REG_CLASSES \
+}
+
/* Defines invalid mode changes. Borrowed from pa64-regs.h.
SImode loads to floating-point registers are not zero-extended.
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/i386/i386.h ./config/i386/i386.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/i386/i386.h 2008-08-21 21:16:23.000000000 -0400
+++ ./config/i386/i386.h 2008-08-22 21:58:50.000000000 -0400
@@ -1274,6 +1274,19 @@ enum reg_class
{ 0xffffffff,0x1fffff } \
}
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FLOAT_REGS, MMX_REGS, SSE_REGS, LIM_REG_CLASSES \
+}
+
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/ia64/ia64.h ./config/ia64/ia64.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/ia64/ia64.h 2008-08-21 21:16:29.000000000 -0400
+++ ./config/ia64/ia64.h 2008-08-22 18:09:44.000000000 -0400
@@ -800,6 +800,19 @@ enum reg_class
0xFFFFFFFF, 0xFFFFFFFF, 0x3FFF }, \
}
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ PR_REGS, BR_REGS, AR_M_REGS, AR_I_REGS, GR_REGS, FR_REGS, LIM_REG_CLASSES \
+}
+
/* A C expression whose value is a register class containing hard register
REGNO. In general there is more than one such class; choose a class which
is "minimal", meaning that no smaller class also contains the register. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/rs6000/rs6000.h ./config/rs6000/rs6000.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/rs6000/rs6000.h 2008-08-21 21:16:34.000000000 -0400
+++ ./config/rs6000/rs6000.h 2008-08-22 21:59:08.000000000 -0400
@@ -1128,6 +1128,22 @@ enum reg_class
{ 0xffffffff, 0xffffffff, 0xffffffff, 0x0003ffff } /* ALL_REGS */ \
}
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, SPECIAL_REGS, FLOAT_REGS, ALTIVEC_REGS, \
+ /*VRSAVE_REGS,*/ VSCR_REGS, SPE_ACC_REGS, SPEFSCR_REGS, \
+ /* MQ_REGS, LINK_REGS, CTR_REGS, */ \
+ CR_REGS, XER_REGS, LIM_REG_CLASSES \
+}
+
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/arm/arm.h ./config/arm/arm.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/arm/arm.h 2008-08-21 21:16:37.000000000 -0400
+++ ./config/arm/arm.h 2008-08-22 21:59:13.000000000 -0400
@@ -1185,6 +1185,20 @@ enum reg_class
or could index an array. */
#define REGNO_REG_CLASS(REGNO) arm_regno_class (REGNO)
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FPA_REGS, CIRRUS_REGS, VFP_REGS, IWMMXT_GR_REGS, IWMMXT_REGS,\
+ LIM_REG_CLASSES \
+}
+
/* FPA registers can't do subreg as all values are reformatted to internal
precision. VFP registers may only be accessed in the mode they
were set. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/alpha/alpha.h ./config/alpha/alpha.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/alpha/alpha.h 2008-08-21 21:16:17.000000000 -0400
+++ ./config/alpha/alpha.h 2008-08-22 21:58:36.000000000 -0400
@@ -553,6 +553,19 @@ enum reg_class {
{0x00000000, 0x7fffffff}, /* FLOAT_REGS */ \
{0xffffffff, 0xffffffff} }
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FLOAT_REGS, LIM_REG_CLASSES \
+}
+
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/avr/avr.h ./config/avr/avr.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/avr/avr.h 2008-08-21 21:16:25.000000000 -0400
+++ ./config/avr/avr.h 2008-08-22 21:58:59.000000000 -0400
@@ -291,6 +291,19 @@ enum reg_class {
#define REGNO_REG_CLASS(R) avr_regno_reg_class(R)
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, LIM_REG_CLASSES \
+}
+
#define BASE_REG_CLASS (reload_completed ? BASE_POINTER_REGS : POINTER_REGS)
#define INDEX_REG_CLASS NO_REGS
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/spu/spu.h ./config/spu/spu.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/spu/spu.h 2008-08-21 21:16:19.000000000 -0400
+++ ./config/spu/spu.h 2008-08-22 21:58:46.000000000 -0400
@@ -196,6 +196,9 @@ enum reg_class {
LIM_REG_CLASSES
};
+/* SPU is simple, it really only has one class of registers. */
+#define IRA_COVER_CLASSES { GENERAL_REGS, LIM_REG_CLASSES }
+
#define N_REG_CLASSES (int) LIM_REG_CLASSES
#define REG_CLASS_NAMES \
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/bfin/bfin.h ./config/bfin/bfin.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/bfin/bfin.h 2008-08-21 21:16:42.000000000 -0400
+++ ./config/bfin/bfin.h 2008-08-22 18:09:56.000000000 -0400
@@ -711,6 +711,19 @@ enum reg_class
: (REGNO) >= REG_RETS ? PROLOGUE_REGS \
: NO_REGS)
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ MOST_REGS, AREGS, CCREGS, LIM_REG_CLASSES \
+}
+
/* When defined, the compiler allows registers explicitly used in the
rtl to be used as spill registers but prevents the compiler from
extending the lifetime of these registers. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sh/sh.h ./config/sh/sh.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sh/sh.h 2008-08-21 21:16:25.000000000 -0400
+++ ./config/sh/sh.h 2008-08-22 18:09:41.000000000 -0400
@@ -1499,6 +1499,20 @@ enum reg_class
extern enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
#define REGNO_REG_CLASS(REGNO) regno_reg_class[(REGNO)]
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FP_REGS, PR_REGS, T_REGS, MAC_REGS, TARGET_REGS, \
+ LIM_REG_CLASSES \
+}
+
/* When defined, the compiler allows registers explicitly used in the
rtl to be used as spill registers but prevents the compiler from
extending the lifetime of these registers. */
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sh/sh.md ./config/sh/sh.md
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/sh/sh.md 2008-08-21 21:16:25.000000000 -0400
+++ ./config/sh/sh.md 2008-08-22 18:09:41.000000000 -0400
@@ -1143,7 +1143,7 @@
(set (match_dup 4) (match_dup 5))]
"
{
- rtx set1, set2;
+ rtx set1, set2, insn2;
rtx replacements[4];
/* We want to replace occurrences of operands[0] with operands[1] and
@@ -1173,7 +1173,10 @@
extract_insn (emit_insn (set1));
if (! constrain_operands (1))
goto failure;
- extract_insn (emit (set2));
+ insn2 = emit (set2);
+ if (GET_CODE (insn2) == BARRIER)
+ goto failure;
+ extract_insn (insn2);
if (! constrain_operands (1))
{
rtx tmp;
diff -r -x .svn -up /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/mn10300/mn10300.h ./config/mn10300/mn10300.h
--- /home/cygnus/vmakarov/build/trunk/gcc/gcc/config/mn10300/mn10300.h 2008-08-21 21:16:28.000000000 -0400
+++ ./config/mn10300/mn10300.h 2008-08-22 18:09:43.000000000 -0400
@@ -295,6 +295,19 @@ enum reg_class {
{ 0xffffffff, 0x3ffff } /* ALL_REGS */ \
}
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FP_REGS, LIM_REG_CLASSES \
+}
+
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression