| Index: gcc/gcc/gcse.c
|
| diff --git a/gcc/gcc/gcse.c b/gcc/gcc/gcse.c
|
| index 1869ea716a8e9d885dbdfb56836b8da400f621c4..8e31ee11a589cfe5e39262ffd34db6f2064aa2b6 100644
|
| --- a/gcc/gcc/gcse.c
|
| +++ b/gcc/gcc/gcse.c
|
| @@ -1,7 +1,7 @@
|
| /* Global common subexpression elimination/Partial redundancy elimination
|
| and global constant/copy propagation for GNU compiler.
|
| Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
| - 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
| + 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
|
|
|
| This file is part of GCC.
|
|
|
| @@ -27,9 +27,6 @@ along with GCC; see the file COPYING3. If not see
|
| - a store to the same address as a load does not kill the load if the
|
| source of the store is also the destination of the load. Handling this
|
| allows more load motion, particularly out of loops.
|
| - - ability to realloc sbitmap vectors would allow one initial computation
|
| - of reg_set_in_block with only subsequent additions, rather than
|
| - recomputing it for each pass
|
|
|
| */
|
|
|
| @@ -172,34 +169,23 @@ along with GCC; see the file COPYING3. If not see
|
| #include "hashtab.h"
|
| #include "df.h"
|
| #include "dbgcnt.h"
|
| -
|
| -/* Propagate flow information through back edges and thus enable PRE's
|
| - moving loop invariant calculations out of loops.
|
| -
|
| - Originally this tended to create worse overall code, but several
|
| - improvements during the development of PRE seem to have made following
|
| - back edges generally a win.
|
| -
|
| - Note much of the loop invariant code motion done here would normally
|
| - be done by loop.c, which has more heuristics for when to move invariants
|
| - out of loops. At some point we might need to move some of those
|
| - heuristics into gcse.c. */
|
| +#include "target.h"
|
|
|
| /* We support GCSE via Partial Redundancy Elimination. PRE optimizations
|
| - are a superset of those done by GCSE.
|
| + are a superset of those done by classic GCSE.
|
|
|
| We perform the following steps:
|
|
|
| - 1) Compute basic block information.
|
| -
|
| - 2) Compute table of places where registers are set.
|
| + 1) Compute table of places where registers are set.
|
|
|
| - 3) Perform copy/constant propagation.
|
| + 2) Perform copy/constant propagation.
|
|
|
| - 4) Perform global cse using lazy code motion if not optimizing
|
| + 3) Perform global cse using lazy code motion if not optimizing
|
| for size, or code hoisting if we are.
|
|
|
| - 5) Perform another pass of copy/constant propagation.
|
| + 4) Perform another pass of copy/constant propagation. Try to bypass
|
| + conditional jumps if the condition can be computed from a value of
|
| + an incoming edge.
|
|
|
| Two passes of copy/constant propagation are done because the first one
|
| enables more GCSE and the second one helps to clean up the copies that
|
| @@ -212,13 +198,14 @@ along with GCC; see the file COPYING3. If not see
|
| (set (pseudo-reg) (expression)).
|
| Function want_to_gcse_p says what these are.
|
|
|
| + In addition, expressions in REG_EQUAL notes are candidates for GCSE-ing.
|
| + This allows PRE to hoist expressions that are expressed in multiple insns,
|
| + such as complex address calculations (e.g. for PIC code, or loads with a
|
| + high part and a low part).
|
| +
|
| PRE handles moving invariant expressions out of loops (by treating them as
|
| partially redundant).
|
|
|
| - Eventually it would be nice to replace cse.c/gcse.c with SSA (static single
|
| - assignment) based GVN (global value numbering). L. T. Simpson's paper
|
| - (Rice University) on value numbering is a useful reference for this.
|
| -
|
| **********************
|
|
|
| We used to support multiple passes but there are diminishing returns in
|
| @@ -235,9 +222,13 @@ along with GCC; see the file COPYING3. If not see
|
| It was found doing copy propagation between each pass enables further
|
| substitutions.
|
|
|
| + This study was done before expressions in REG_EQUAL notes were added as
|
| + candidate expressions for optimization, and before the GIMPLE optimizers
|
| + were added. Probably, multiple passes is even less efficient now than
|
| + at the time when the study was conducted.
|
| +
|
| PRE is quite expensive in complicated functions because the DFA can take
|
| - a while to converge. Hence we only perform one pass. The parameter
|
| - max-gcse-passes can be modified if one wants to experiment.
|
| + a while to converge. Hence we only perform one pass.
|
|
|
| **********************
|
|
|
| @@ -262,7 +253,7 @@ along with GCC; see the file COPYING3. If not see
|
| argue it is not. The number of iterations for the algorithm to converge
|
| is typically 2-4 so I don't view it as that expensive (relatively speaking).
|
|
|
| - PRE GCSE depends heavily on the second CSE pass to clean up the copies
|
| + PRE GCSE depends heavily on the second CPROP pass to clean up the copies
|
| we create. To make an expression reach the place where it's redundant,
|
| the result of the expression is copied to a new register, and the redundant
|
| expression is deleted by replacing it with this new register. Classic GCSE
|
| @@ -272,13 +263,8 @@ along with GCC; see the file COPYING3. If not see
|
|
|
| /* GCSE global vars. */
|
|
|
| -/* Note whether or not we should run jump optimization after gcse. We
|
| - want to do this for two cases.
|
| -
|
| - * If we changed any jumps via cprop.
|
| -
|
| - * If we added any labels via edge splitting. */
|
| -static int run_jump_opt_after_gcse;
|
| +/* Set to non-zero if CSE should run after all GCSE optimizations are done. */
|
| +int flag_rerun_cse_after_global_opts;
|
|
|
| /* An obstack for our working variables. */
|
| static struct obstack gcse_obstack;
|
| @@ -340,7 +326,7 @@ struct occr
|
| [one could build a mapping table without holes afterwards though].
|
| Someday I'll perform the computation and figure it out. */
|
|
|
| -struct hash_table
|
| +struct hash_table_d
|
| {
|
| /* The table itself.
|
| This is an array of `expr_hash_table_size' elements. */
|
| @@ -357,74 +343,10 @@ struct hash_table
|
| };
|
|
|
| /* Expression hash table. */
|
| -static struct hash_table expr_hash_table;
|
| +static struct hash_table_d expr_hash_table;
|
|
|
| /* Copy propagation hash table. */
|
| -static struct hash_table set_hash_table;
|
| -
|
| -/* Mapping of uids to cuids.
|
| - Only real insns get cuids. */
|
| -static int *uid_cuid;
|
| -
|
| -/* Highest UID in UID_CUID. */
|
| -static int max_uid;
|
| -
|
| -/* Get the cuid of an insn. */
|
| -#ifdef ENABLE_CHECKING
|
| -#define INSN_CUID(INSN) \
|
| - (gcc_assert (INSN_UID (INSN) <= max_uid), uid_cuid[INSN_UID (INSN)])
|
| -#else
|
| -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
|
| -#endif
|
| -
|
| -/* Number of cuids. */
|
| -static int max_cuid;
|
| -
|
| -/* Maximum register number in function prior to doing gcse + 1.
|
| - Registers created during this pass have regno >= max_gcse_regno.
|
| - This is named with "gcse" to not collide with global of same name. */
|
| -static unsigned int max_gcse_regno;
|
| -
|
| -/* Table of registers that are modified.
|
| -
|
| - For each register, each element is a list of places where the pseudo-reg
|
| - is set.
|
| -
|
| - For simplicity, GCSE is done on sets of pseudo-regs only. PRE GCSE only
|
| - requires knowledge of which blocks kill which regs [and thus could use
|
| - a bitmap instead of the lists `reg_set_table' uses].
|
| -
|
| - `reg_set_table' and could be turned into an array of bitmaps (num-bbs x
|
| - num-regs) [however perhaps it may be useful to keep the data as is]. One
|
| - advantage of recording things this way is that `reg_set_table' is fairly
|
| - sparse with respect to pseudo regs but for hard regs could be fairly dense
|
| - [relatively speaking]. And recording sets of pseudo-regs in lists speeds
|
| - up functions like compute_transp since in the case of pseudo-regs we only
|
| - need to iterate over the number of times a pseudo-reg is set, not over the
|
| - number of basic blocks [clearly there is a bit of a slow down in the cases
|
| - where a pseudo is set more than once in a block, however it is believed
|
| - that the net effect is to speed things up]. This isn't done for hard-regs
|
| - because recording call-clobbered hard-regs in `reg_set_table' at each
|
| - function call can consume a fair bit of memory, and iterating over
|
| - hard-regs stored this way in compute_transp will be more expensive. */
|
| -
|
| -typedef struct reg_set
|
| -{
|
| - /* The next setting of this register. */
|
| - struct reg_set *next;
|
| - /* The index of the block where it was set. */
|
| - int bb_index;
|
| -} reg_set;
|
| -
|
| -static reg_set **reg_set_table;
|
| -
|
| -/* Size of `reg_set_table'.
|
| - The table starts out at max_gcse_regno + slop, and is enlarged as
|
| - necessary. */
|
| -static int reg_set_table_size;
|
| -
|
| -/* Amount to grow `reg_set_table' by when it's full. */
|
| -#define REG_SET_TABLE_SLOP 100
|
| +static struct hash_table_d set_hash_table;
|
|
|
| /* This is a list of expressions which are MEMs and will be used by load
|
| or store motion.
|
| @@ -465,13 +387,6 @@ static htab_t pre_ldst_table = NULL;
|
| the start of the basic block. */
|
| static regset reg_set_bitmap;
|
|
|
| -/* For each block, a bitmap of registers set in the block.
|
| - This is used by compute_transp.
|
| - It is computed during hash table computation and not by compute_sets
|
| - as it includes registers added since the last pass (or between cprop and
|
| - gcse) and it's currently not easy to realloc sbitmap vectors. */
|
| -static sbitmap *reg_set_in_block;
|
| -
|
| /* Array, indexed by basic block number for a list of insns which modify
|
| memory within that block. */
|
| static rtx * modify_mem_list;
|
| @@ -505,45 +420,38 @@ static int global_const_prop_count;
|
| static int global_copy_prop_count;
|
|
|
| /* For available exprs */
|
| -static sbitmap *ae_kill, *ae_gen;
|
| +static sbitmap *ae_kill;
|
|
|
| static void compute_can_copy (void);
|
| static void *gmalloc (size_t) ATTRIBUTE_MALLOC;
|
| static void *gcalloc (size_t, size_t) ATTRIBUTE_MALLOC;
|
| -static void *grealloc (void *, size_t);
|
| static void *gcse_alloc (unsigned long);
|
| static void alloc_gcse_mem (void);
|
| static void free_gcse_mem (void);
|
| -static void alloc_reg_set_mem (int);
|
| -static void free_reg_set_mem (void);
|
| -static void record_one_set (int, rtx);
|
| -static void record_set_info (rtx, const_rtx, void *);
|
| -static void compute_sets (void);
|
| -static void hash_scan_insn (rtx, struct hash_table *);
|
| -static void hash_scan_set (rtx, rtx, struct hash_table *);
|
| -static void hash_scan_clobber (rtx, rtx, struct hash_table *);
|
| -static void hash_scan_call (rtx, rtx, struct hash_table *);
|
| +static void hash_scan_insn (rtx, struct hash_table_d *);
|
| +static void hash_scan_set (rtx, rtx, struct hash_table_d *);
|
| +static void hash_scan_clobber (rtx, rtx, struct hash_table_d *);
|
| +static void hash_scan_call (rtx, rtx, struct hash_table_d *);
|
| static int want_to_gcse_p (rtx);
|
| -static bool can_assign_to_reg_p (rtx);
|
| static bool gcse_constant_p (const_rtx);
|
| static int oprs_unchanged_p (const_rtx, const_rtx, int);
|
| static int oprs_anticipatable_p (const_rtx, const_rtx);
|
| static int oprs_available_p (const_rtx, const_rtx);
|
| static void insert_expr_in_table (rtx, enum machine_mode, rtx, int, int,
|
| - struct hash_table *);
|
| -static void insert_set_in_table (rtx, rtx, struct hash_table *);
|
| + struct hash_table_d *);
|
| +static void insert_set_in_table (rtx, rtx, struct hash_table_d *);
|
| static unsigned int hash_expr (const_rtx, enum machine_mode, int *, int);
|
| static unsigned int hash_set (int, int);
|
| static int expr_equiv_p (const_rtx, const_rtx);
|
| static void record_last_reg_set_info (rtx, int);
|
| static void record_last_mem_set_info (rtx);
|
| static void record_last_set_info (rtx, const_rtx, void *);
|
| -static void compute_hash_table (struct hash_table *);
|
| -static void alloc_hash_table (int, struct hash_table *, int);
|
| -static void free_hash_table (struct hash_table *);
|
| -static void compute_hash_table_work (struct hash_table *);
|
| -static void dump_hash_table (FILE *, const char *, struct hash_table *);
|
| -static struct expr *lookup_set (unsigned int, struct hash_table *);
|
| +static void compute_hash_table (struct hash_table_d *);
|
| +static void alloc_hash_table (struct hash_table_d *, int);
|
| +static void free_hash_table (struct hash_table_d *);
|
| +static void compute_hash_table_work (struct hash_table_d *);
|
| +static void dump_hash_table (FILE *, const char *, struct hash_table_d *);
|
| +static struct expr *lookup_set (unsigned int, struct hash_table_d *);
|
| static struct expr *next_set (unsigned int, struct expr *);
|
| static void reset_opr_set_tables (void);
|
| static int oprs_not_set_p (const_rtx, const_rtx);
|
| @@ -556,7 +464,7 @@ static void free_cprop_mem (void);
|
| static void compute_transp (const_rtx, int, sbitmap *, int);
|
| static void compute_transpout (void);
|
| static void compute_local_properties (sbitmap *, sbitmap *, sbitmap *,
|
| - struct hash_table *);
|
| + struct hash_table_d *);
|
| static void compute_cprop_data (void);
|
| static void find_used_regs (rtx *, void *);
|
| static int try_replace_reg (rtx, rtx, rtx);
|
| @@ -565,11 +473,10 @@ static int cprop_jump (basic_block, rtx, rtx, rtx, rtx);
|
| static void mems_conflict_for_gcse_p (rtx, const_rtx, void *);
|
| static int load_killed_in_block_p (const_basic_block, int, const_rtx, int);
|
| static void canon_list_insert (rtx, const_rtx, void *);
|
| -static int cprop_insn (rtx, int);
|
| -static int cprop (int);
|
| +static int cprop_insn (rtx);
|
| static void find_implicit_sets (void);
|
| -static int one_cprop_pass (int, bool, bool);
|
| -static bool constprop_register (rtx, rtx, rtx, bool);
|
| +static int one_cprop_pass (void);
|
| +static bool constprop_register (rtx, rtx, rtx);
|
| static struct expr *find_bypass_set (int, int);
|
| static bool reg_killed_on_edge (const_rtx, const_edge);
|
| static int bypass_block (basic_block, rtx, rtx);
|
| @@ -584,14 +491,14 @@ static void pre_insert_copy_insn (struct expr *, rtx);
|
| static void pre_insert_copies (void);
|
| static int pre_delete (void);
|
| static int pre_gcse (void);
|
| -static int one_pre_gcse_pass (int);
|
| +static int one_pre_gcse_pass (void);
|
| static void add_label_notes (rtx, rtx);
|
| static void alloc_code_hoist_mem (int, int);
|
| static void free_code_hoist_mem (void);
|
| static void compute_code_hoist_vbeinout (void);
|
| static void compute_code_hoist_data (void);
|
| static int hoist_expr_reaches_here_p (basic_block, int, basic_block, char *);
|
| -static void hoist_code (void);
|
| +static int hoist_code (void);
|
| static int one_code_hoisting_pass (void);
|
| static rtx process_insert_insn (struct expr *);
|
| static int pre_edge_insert (struct edge_list *, struct expr **);
|
| @@ -602,7 +509,6 @@ static void free_ldst_entry (struct ls_expr *);
|
| static void free_ldst_mems (void);
|
| static void print_ldst_list (FILE *);
|
| static struct ls_expr * find_rtx_in_ldst (rtx);
|
| -static int enumerate_ldsts (void);
|
| static inline struct ls_expr * first_ls_expr (void);
|
| static inline struct ls_expr * next_ls_expr (struct ls_expr *);
|
| static int simple_mem (const_rtx);
|
| @@ -610,33 +516,13 @@ static void invalidate_any_buried_refs (rtx);
|
| static void compute_ld_motion_mems (void);
|
| static void trim_ld_motion_mems (void);
|
| static void update_ld_motion_stores (struct expr *);
|
| -static void reg_set_info (rtx, const_rtx, void *);
|
| -static void reg_clear_last_set (rtx, const_rtx, void *);
|
| -static bool store_ops_ok (const_rtx, int *);
|
| -static rtx extract_mentioned_regs (rtx);
|
| -static rtx extract_mentioned_regs_helper (rtx, rtx);
|
| -static void find_moveable_store (rtx, int *, int *);
|
| -static int compute_store_table (void);
|
| -static bool load_kills_store (const_rtx, const_rtx, int);
|
| -static bool find_loads (const_rtx, const_rtx, int);
|
| -static bool store_killed_in_insn (const_rtx, const_rtx, const_rtx, int);
|
| -static bool store_killed_after (const_rtx, const_rtx, const_rtx, const_basic_block, int *, rtx *);
|
| -static bool store_killed_before (const_rtx, const_rtx, const_rtx, const_basic_block, int *);
|
| -static void build_store_vectors (void);
|
| -static void insert_insn_start_basic_block (rtx, basic_block);
|
| -static int insert_store (struct ls_expr *, edge);
|
| -static void remove_reachable_equiv_notes (basic_block, struct ls_expr *);
|
| -static void replace_store_insn (rtx, rtx, basic_block, struct ls_expr *);
|
| -static void delete_store (struct ls_expr *, basic_block);
|
| -static void free_store_memory (void);
|
| -static void store_motion (void);
|
| static void free_insn_expr_list_list (rtx *);
|
| static void clear_modify_mem_tables (void);
|
| static void free_modify_mem_tables (void);
|
| static rtx gcse_emit_move_after (rtx, rtx, rtx);
|
| static void local_cprop_find_used_regs (rtx *, void *);
|
| -static bool do_local_cprop (rtx, rtx, bool);
|
| -static void local_cprop_pass (bool);
|
| +static bool do_local_cprop (rtx, rtx);
|
| +static int local_cprop_pass (void);
|
| static bool is_too_expensive (const char *);
|
|
|
| #define GNEW(T) ((T *) gmalloc (sizeof (T)))
|
| @@ -644,196 +530,13 @@ static bool is_too_expensive (const char *);
|
|
|
| #define GNEWVEC(T, N) ((T *) gmalloc (sizeof (T) * (N)))
|
| #define GCNEWVEC(T, N) ((T *) gcalloc ((N), sizeof (T)))
|
| -#define GRESIZEVEC(T, P, N) ((T *) grealloc ((void *) (P), sizeof (T) * (N)))
|
|
|
| #define GNEWVAR(T, S) ((T *) gmalloc ((S)))
|
| #define GCNEWVAR(T, S) ((T *) gcalloc (1, (S)))
|
| -#define GRESIZEVAR(T, P, S) ((T *) grealloc ((P), (S)))
|
|
|
| #define GOBNEW(T) ((T *) gcse_alloc (sizeof (T)))
|
| #define GOBNEWVAR(T, S) ((T *) gcse_alloc ((S)))
|
|
|
| -
|
| -/* Entry point for global common subexpression elimination.
|
| - F is the first instruction in the function. Return nonzero if a
|
| - change is mode. */
|
| -
|
| -static int
|
| -gcse_main (rtx f ATTRIBUTE_UNUSED)
|
| -{
|
| - int changed, pass;
|
| - /* Bytes used at start of pass. */
|
| - int initial_bytes_used;
|
| - /* Maximum number of bytes used by a pass. */
|
| - int max_pass_bytes;
|
| - /* Point to release obstack data from for each pass. */
|
| - char *gcse_obstack_bottom;
|
| -
|
| - /* We do not construct an accurate cfg in functions which call
|
| - setjmp, so just punt to be safe. */
|
| - if (cfun->calls_setjmp)
|
| - return 0;
|
| -
|
| - /* Assume that we do not need to run jump optimizations after gcse. */
|
| - run_jump_opt_after_gcse = 0;
|
| -
|
| - /* Identify the basic block information for this function, including
|
| - successors and predecessors. */
|
| - max_gcse_regno = max_reg_num ();
|
| -
|
| - df_note_add_problem ();
|
| - df_analyze ();
|
| -
|
| - if (dump_file)
|
| - dump_flow_info (dump_file, dump_flags);
|
| -
|
| - /* Return if there's nothing to do, or it is too expensive. */
|
| - if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|
| - || is_too_expensive (_("GCSE disabled")))
|
| - return 0;
|
| -
|
| - gcc_obstack_init (&gcse_obstack);
|
| - bytes_used = 0;
|
| -
|
| - /* We need alias. */
|
| - init_alias_analysis ();
|
| - /* Record where pseudo-registers are set. This data is kept accurate
|
| - during each pass. ??? We could also record hard-reg information here
|
| - [since it's unchanging], however it is currently done during hash table
|
| - computation.
|
| -
|
| - It may be tempting to compute MEM set information here too, but MEM sets
|
| - will be subject to code motion one day and thus we need to compute
|
| - information about memory sets when we build the hash tables. */
|
| -
|
| - alloc_reg_set_mem (max_gcse_regno);
|
| - compute_sets ();
|
| -
|
| - pass = 0;
|
| - initial_bytes_used = bytes_used;
|
| - max_pass_bytes = 0;
|
| - gcse_obstack_bottom = GOBNEWVAR (char, 1);
|
| - changed = 1;
|
| - while (changed && pass < MAX_GCSE_PASSES)
|
| - {
|
| - changed = 0;
|
| - if (dump_file)
|
| - fprintf (dump_file, "GCSE pass %d\n\n", pass + 1);
|
| -
|
| - /* Initialize bytes_used to the space for the pred/succ lists,
|
| - and the reg_set_table data. */
|
| - bytes_used = initial_bytes_used;
|
| -
|
| - /* Each pass may create new registers, so recalculate each time. */
|
| - max_gcse_regno = max_reg_num ();
|
| -
|
| - alloc_gcse_mem ();
|
| -
|
| - /* Don't allow constant propagation to modify jumps
|
| - during this pass. */
|
| - if (dbg_cnt (cprop1))
|
| - {
|
| - timevar_push (TV_CPROP1);
|
| - changed = one_cprop_pass (pass + 1, false, false);
|
| - timevar_pop (TV_CPROP1);
|
| - }
|
| -
|
| - if (optimize_function_for_speed_p (cfun))
|
| - {
|
| - timevar_push (TV_PRE);
|
| - changed |= one_pre_gcse_pass (pass + 1);
|
| - /* We may have just created new basic blocks. Release and
|
| - recompute various things which are sized on the number of
|
| - basic blocks. */
|
| - if (changed)
|
| - {
|
| - free_modify_mem_tables ();
|
| - modify_mem_list = GCNEWVEC (rtx, last_basic_block);
|
| - canon_modify_mem_list = GCNEWVEC (rtx, last_basic_block);
|
| - }
|
| - free_reg_set_mem ();
|
| - alloc_reg_set_mem (max_reg_num ());
|
| - compute_sets ();
|
| - run_jump_opt_after_gcse = 1;
|
| - timevar_pop (TV_PRE);
|
| - }
|
| -
|
| - if (max_pass_bytes < bytes_used)
|
| - max_pass_bytes = bytes_used;
|
| -
|
| - /* Free up memory, then reallocate for code hoisting. We can
|
| - not re-use the existing allocated memory because the tables
|
| - will not have info for the insns or registers created by
|
| - partial redundancy elimination. */
|
| - free_gcse_mem ();
|
| -
|
| - /* It does not make sense to run code hoisting unless we are optimizing
|
| - for code size -- it rarely makes programs faster, and can make
|
| - them bigger if we did partial redundancy elimination (when optimizing
|
| - for space, we don't run the partial redundancy algorithms). */
|
| - if (optimize_function_for_size_p (cfun))
|
| - {
|
| - timevar_push (TV_HOIST);
|
| - max_gcse_regno = max_reg_num ();
|
| - alloc_gcse_mem ();
|
| - changed |= one_code_hoisting_pass ();
|
| - free_gcse_mem ();
|
| -
|
| - if (max_pass_bytes < bytes_used)
|
| - max_pass_bytes = bytes_used;
|
| - timevar_pop (TV_HOIST);
|
| - }
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "\n");
|
| - fflush (dump_file);
|
| - }
|
| -
|
| - obstack_free (&gcse_obstack, gcse_obstack_bottom);
|
| - pass++;
|
| - }
|
| -
|
| - /* Do one last pass of copy propagation, including cprop into
|
| - conditional jumps. */
|
| -
|
| - if (dbg_cnt (cprop2))
|
| - {
|
| - max_gcse_regno = max_reg_num ();
|
| - alloc_gcse_mem ();
|
| -
|
| - /* This time, go ahead and allow cprop to alter jumps. */
|
| - timevar_push (TV_CPROP2);
|
| - one_cprop_pass (pass + 1, true, true);
|
| - timevar_pop (TV_CPROP2);
|
| - free_gcse_mem ();
|
| - }
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "GCSE of %s: %d basic blocks, ",
|
| - current_function_name (), n_basic_blocks);
|
| - fprintf (dump_file, "%d pass%s, %d bytes\n\n",
|
| - pass, pass > 1 ? "es" : "", max_pass_bytes);
|
| - }
|
| -
|
| - obstack_free (&gcse_obstack, NULL);
|
| - free_reg_set_mem ();
|
| -
|
| - /* We are finished with alias. */
|
| - end_alias_analysis ();
|
| -
|
| - if (optimize_function_for_speed_p (cfun) && flag_gcse_sm)
|
| - {
|
| - timevar_push (TV_LSM);
|
| - store_motion ();
|
| - timevar_pop (TV_LSM);
|
| - }
|
| -
|
| - /* Record where pseudo-registers are set. */
|
| - return run_jump_opt_after_gcse;
|
| -}
|
| -
|
| /* Misc. utilities. */
|
|
|
| /* Nonzero for each mode that supports (set (reg) (reg)).
|
| @@ -886,6 +589,7 @@ can_copy_p (enum machine_mode mode)
|
|
|
| return can_copy[mode] != 0;
|
| }
|
| +
|
|
|
| /* Cover function to xmalloc to record bytes allocated. */
|
|
|
| @@ -905,16 +609,6 @@ gcalloc (size_t nelem, size_t elsize)
|
| return xcalloc (nelem, elsize);
|
| }
|
|
|
| -/* Cover function to xrealloc.
|
| - We don't record the additional size since we don't know it.
|
| - It won't affect memory usage stats much anyway. */
|
| -
|
| -static void *
|
| -grealloc (void *ptr, size_t size)
|
| -{
|
| - return xrealloc (ptr, size);
|
| -}
|
| -
|
| /* Cover function to obstack_alloc. */
|
|
|
| static void *
|
| @@ -924,43 +618,15 @@ gcse_alloc (unsigned long size)
|
| return obstack_alloc (&gcse_obstack, size);
|
| }
|
|
|
| -/* Allocate memory for the cuid mapping array,
|
| - and reg/memory set tracking tables.
|
| -
|
| +/* Allocate memory for the reg/memory set tracking tables.
|
| This is called at the start of each pass. */
|
|
|
| static void
|
| alloc_gcse_mem (void)
|
| {
|
| - int i;
|
| - basic_block bb;
|
| - rtx insn;
|
| -
|
| - /* Find the largest UID and create a mapping from UIDs to CUIDs.
|
| - CUIDs are like UIDs except they increase monotonically, have no gaps,
|
| - and only apply to real insns.
|
| - (Actually, there are gaps, for insn that are not inside a basic block.
|
| - but we should never see those anyway, so this is OK.) */
|
| -
|
| - max_uid = get_max_uid ();
|
| - uid_cuid = GCNEWVEC (int, max_uid + 1);
|
| - i = 0;
|
| - FOR_EACH_BB (bb)
|
| - FOR_BB_INSNS (bb, insn)
|
| - {
|
| - if (INSN_P (insn))
|
| - uid_cuid[INSN_UID (insn)] = i++;
|
| - else
|
| - uid_cuid[INSN_UID (insn)] = i;
|
| - }
|
| -
|
| - max_cuid = i;
|
| -
|
| /* Allocate vars to track sets of regs. */
|
| reg_set_bitmap = BITMAP_ALLOC (NULL);
|
|
|
| - /* Allocate vars to track sets of regs, memory per block. */
|
| - reg_set_in_block = sbitmap_vector_alloc (last_basic_block, max_gcse_regno);
|
| /* Allocate array to keep a list of insns which modify memory in each
|
| basic block. */
|
| modify_mem_list = GCNEWVEC (rtx, last_basic_block);
|
| @@ -974,11 +640,6 @@ alloc_gcse_mem (void)
|
| static void
|
| free_gcse_mem (void)
|
| {
|
| - free (uid_cuid);
|
| -
|
| - BITMAP_FREE (reg_set_bitmap);
|
| -
|
| - sbitmap_vector_free (reg_set_in_block);
|
| free_modify_mem_tables ();
|
| BITMAP_FREE (modify_mem_list_set);
|
| BITMAP_FREE (blocks_with_calls);
|
| @@ -1013,7 +674,7 @@ free_gcse_mem (void)
|
|
|
| static void
|
| compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
|
| - struct hash_table *table)
|
| + struct hash_table_d *table)
|
| {
|
| unsigned int i;
|
|
|
| @@ -1051,7 +712,7 @@ compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
|
| if (antloc)
|
| for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
|
| {
|
| - SET_BIT (antloc[BLOCK_NUM (occr->insn)], indx);
|
| + SET_BIT (antloc[BLOCK_FOR_INSN (occr->insn)->index], indx);
|
|
|
| /* While we're scanning the table, this is a good place to
|
| initialize this. */
|
| @@ -1063,7 +724,7 @@ compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
|
| if (comp)
|
| for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
|
| {
|
| - SET_BIT (comp[BLOCK_NUM (occr->insn)], indx);
|
| + SET_BIT (comp[BLOCK_FOR_INSN (occr->insn)->index], indx);
|
|
|
| /* While we're scanning the table, this is a good place to
|
| initialize this. */
|
| @@ -1077,85 +738,6 @@ compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
|
| }
|
| }
|
|
|
| -/* Register set information.
|
| -
|
| - `reg_set_table' records where each register is set or otherwise
|
| - modified. */
|
| -
|
| -static struct obstack reg_set_obstack;
|
| -
|
| -static void
|
| -alloc_reg_set_mem (int n_regs)
|
| -{
|
| - reg_set_table_size = n_regs + REG_SET_TABLE_SLOP;
|
| - reg_set_table = GCNEWVEC (struct reg_set *, reg_set_table_size);
|
| -
|
| - gcc_obstack_init (®_set_obstack);
|
| -}
|
| -
|
| -static void
|
| -free_reg_set_mem (void)
|
| -{
|
| - free (reg_set_table);
|
| - obstack_free (®_set_obstack, NULL);
|
| -}
|
| -
|
| -/* Record REGNO in the reg_set table. */
|
| -
|
| -static void
|
| -record_one_set (int regno, rtx insn)
|
| -{
|
| - /* Allocate a new reg_set element and link it onto the list. */
|
| - struct reg_set *new_reg_info;
|
| -
|
| - /* If the table isn't big enough, enlarge it. */
|
| - if (regno >= reg_set_table_size)
|
| - {
|
| - int new_size = regno + REG_SET_TABLE_SLOP;
|
| -
|
| - reg_set_table = GRESIZEVEC (struct reg_set *, reg_set_table, new_size);
|
| - memset (reg_set_table + reg_set_table_size, 0,
|
| - (new_size - reg_set_table_size) * sizeof (struct reg_set *));
|
| - reg_set_table_size = new_size;
|
| - }
|
| -
|
| - new_reg_info = XOBNEW (®_set_obstack, struct reg_set);
|
| - bytes_used += sizeof (struct reg_set);
|
| - new_reg_info->bb_index = BLOCK_NUM (insn);
|
| - new_reg_info->next = reg_set_table[regno];
|
| - reg_set_table[regno] = new_reg_info;
|
| -}
|
| -
|
| -/* Called from compute_sets via note_stores to handle one SET or CLOBBER in
|
| - an insn. The DATA is really the instruction in which the SET is
|
| - occurring. */
|
| -
|
| -static void
|
| -record_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED, void *data)
|
| -{
|
| - rtx record_set_insn = (rtx) data;
|
| -
|
| - if (REG_P (dest) && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
|
| - record_one_set (REGNO (dest), record_set_insn);
|
| -}
|
| -
|
| -/* Scan the function and record each set of each pseudo-register.
|
| -
|
| - This is called once, at the start of the gcse pass. See the comments for
|
| - `reg_set_table' for further documentation. */
|
| -
|
| -static void
|
| -compute_sets (void)
|
| -{
|
| - basic_block bb;
|
| - rtx insn;
|
| -
|
| - FOR_EACH_BB (bb)
|
| - FOR_BB_INSNS (bb, insn)
|
| - if (INSN_P (insn))
|
| - note_stores (PATTERN (insn), record_set_info, insn);
|
| -}
|
| -
|
| /* Hash table support. */
|
|
|
| struct reg_avail_info
|
| @@ -1195,18 +777,28 @@ want_to_gcse_p (rtx x)
|
| return 0;
|
|
|
| default:
|
| - return can_assign_to_reg_p (x);
|
| + return can_assign_to_reg_without_clobbers_p (x);
|
| }
|
| }
|
|
|
| -/* Used internally by can_assign_to_reg_p. */
|
| +/* Used internally by can_assign_to_reg_without_clobbers_p. */
|
|
|
| static GTY(()) rtx test_insn;
|
|
|
| -/* Return true if we can assign X to a pseudo register. */
|
| +/* Return true if we can assign X to a pseudo register such that the
|
| + resulting insn does not result in clobbering a hard register as a
|
| + side-effect.
|
|
|
| -static bool
|
| -can_assign_to_reg_p (rtx x)
|
| + Additionally, if the target requires it, check that the resulting insn
|
| + can be copied. If it cannot, this means that X is special and probably
|
| + has hidden side-effects we don't want to mess with.
|
| +
|
| + This function is typically used by code motion passes, to verify
|
| + that it is safe to insert an insn without worrying about clobbering
|
| + maybe live hard regs. */
|
| +
|
| +bool
|
| +can_assign_to_reg_without_clobbers_p (rtx x)
|
| {
|
| int num_clobbers = 0;
|
| int icode;
|
| @@ -1233,8 +825,18 @@ can_assign_to_reg_p (rtx x)
|
| valid. */
|
| PUT_MODE (SET_DEST (PATTERN (test_insn)), GET_MODE (x));
|
| SET_SRC (PATTERN (test_insn)) = x;
|
| - return ((icode = recog (PATTERN (test_insn), test_insn, &num_clobbers)) >= 0
|
| - && (num_clobbers == 0 || ! added_clobbers_hard_reg_p (icode)));
|
| +
|
| + icode = recog (PATTERN (test_insn), test_insn, &num_clobbers);
|
| + if (icode < 0)
|
| + return false;
|
| +
|
| + if (num_clobbers > 0 && added_clobbers_hard_reg_p (icode))
|
| + return false;
|
| +
|
| + if (targetm.cannot_copy_insn_p && targetm.cannot_copy_insn_p (test_insn))
|
| + return false;
|
| +
|
| + return true;
|
| }
|
|
|
| /* Return nonzero if the operands of expression X are unchanged from the
|
| @@ -1261,13 +863,13 @@ oprs_unchanged_p (const_rtx x, const_rtx insn, int avail_p)
|
| if (info->last_bb != current_bb)
|
| return 1;
|
| if (avail_p)
|
| - return info->last_set < INSN_CUID (insn);
|
| + return info->last_set < DF_INSN_LUID (insn);
|
| else
|
| - return info->first_set >= INSN_CUID (insn);
|
| + return info->first_set >= DF_INSN_LUID (insn);
|
| }
|
|
|
| case MEM:
|
| - if (load_killed_in_block_p (current_bb, INSN_CUID (insn),
|
| + if (load_killed_in_block_p (current_bb, DF_INSN_LUID (insn),
|
| x, avail_p))
|
| return 0;
|
| else
|
| @@ -1366,7 +968,7 @@ mems_conflict_for_gcse_p (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
|
| }
|
|
|
| /* Return nonzero if the expression in X (a memory reference) is killed
|
| - in block BB before or after the insn with the CUID in UID_LIMIT.
|
| + in block BB before or after the insn with the LUID in UID_LIMIT.
|
| AVAIL_P is nonzero for kills after UID_LIMIT, and zero for kills
|
| before UID_LIMIT.
|
|
|
| @@ -1387,9 +989,9 @@ load_killed_in_block_p (const_basic_block bb, int uid_limit, const_rtx x, int av
|
| rtx setter;
|
| /* Ignore entries in the list that do not apply. */
|
| if ((avail_p
|
| - && INSN_CUID (XEXP (list_entry, 0)) < uid_limit)
|
| + && DF_INSN_LUID (XEXP (list_entry, 0)) < uid_limit)
|
| || (! avail_p
|
| - && INSN_CUID (XEXP (list_entry, 0)) > uid_limit))
|
| + && DF_INSN_LUID (XEXP (list_entry, 0)) > uid_limit))
|
| {
|
| list_entry = XEXP (list_entry, 1);
|
| continue;
|
| @@ -1492,7 +1094,7 @@ expr_equiv_p (const_rtx x, const_rtx y)
|
|
|
| static void
|
| insert_expr_in_table (rtx x, enum machine_mode mode, rtx insn, int antic_p,
|
| - int avail_p, struct hash_table *table)
|
| + int avail_p, struct hash_table_d *table)
|
| {
|
| int found, do_not_record_p;
|
| unsigned int hash;
|
| @@ -1542,7 +1144,8 @@ insert_expr_in_table (rtx x, enum machine_mode mode, rtx insn, int antic_p,
|
| {
|
| antic_occr = cur_expr->antic_occr;
|
|
|
| - if (antic_occr && BLOCK_NUM (antic_occr->insn) != BLOCK_NUM (insn))
|
| + if (antic_occr
|
| + && BLOCK_FOR_INSN (antic_occr->insn) != BLOCK_FOR_INSN (insn))
|
| antic_occr = NULL;
|
|
|
| if (antic_occr)
|
| @@ -1566,7 +1169,8 @@ insert_expr_in_table (rtx x, enum machine_mode mode, rtx insn, int antic_p,
|
| {
|
| avail_occr = cur_expr->avail_occr;
|
|
|
| - if (avail_occr && BLOCK_NUM (avail_occr->insn) == BLOCK_NUM (insn))
|
| + if (avail_occr
|
| + && BLOCK_FOR_INSN (avail_occr->insn) == BLOCK_FOR_INSN (insn))
|
| {
|
| /* Found another instance of the expression in the same basic block.
|
| Prefer this occurrence to the currently recorded one. We want
|
| @@ -1593,7 +1197,7 @@ insert_expr_in_table (rtx x, enum machine_mode mode, rtx insn, int antic_p,
|
| basic block. */
|
|
|
| static void
|
| -insert_set_in_table (rtx x, rtx insn, struct hash_table *table)
|
| +insert_set_in_table (rtx x, rtx insn, struct hash_table_d *table)
|
| {
|
| int found;
|
| unsigned int hash;
|
| @@ -1639,7 +1243,8 @@ insert_set_in_table (rtx x, rtx insn, struct hash_table *table)
|
| /* Now record the occurrence. */
|
| cur_occr = cur_expr->avail_occr;
|
|
|
| - if (cur_occr && BLOCK_NUM (cur_occr->insn) == BLOCK_NUM (insn))
|
| + if (cur_occr
|
| + && BLOCK_FOR_INSN (cur_occr->insn) == BLOCK_FOR_INSN (insn))
|
| {
|
| /* Found another instance of the expression in the same basic block.
|
| Prefer this occurrence to the currently recorded one. We want
|
| @@ -1652,11 +1257,10 @@ insert_set_in_table (rtx x, rtx insn, struct hash_table *table)
|
| /* First occurrence of this expression in this basic block. */
|
| cur_occr = GOBNEW (struct occr);
|
| bytes_used += sizeof (struct occr);
|
| -
|
| - cur_occr->insn = insn;
|
| - cur_occr->next = cur_expr->avail_occr;
|
| - cur_occr->deleted_p = 0;
|
| - cur_expr->avail_occr = cur_occr;
|
| + cur_occr->insn = insn;
|
| + cur_occr->next = cur_expr->avail_occr;
|
| + cur_occr->deleted_p = 0;
|
| + cur_expr->avail_occr = cur_occr;
|
| }
|
| }
|
|
|
| @@ -1668,8 +1272,8 @@ gcse_constant_p (const_rtx x)
|
| {
|
| /* Consider a COMPARE of two integers constant. */
|
| if (GET_CODE (x) == COMPARE
|
| - && GET_CODE (XEXP (x, 0)) == CONST_INT
|
| - && GET_CODE (XEXP (x, 1)) == CONST_INT)
|
| + && CONST_INT_P (XEXP (x, 0))
|
| + && CONST_INT_P (XEXP (x, 1)))
|
| return true;
|
|
|
| /* Consider a COMPARE of the same registers is a constant
|
| @@ -1681,14 +1285,16 @@ gcse_constant_p (const_rtx x)
|
| && ! FLOAT_MODE_P (GET_MODE (XEXP (x, 1))))
|
| return true;
|
|
|
| - return CONSTANT_P (x);
|
| + /* Since X might be inserted more than once we have to take care that it
|
| + is sharable. */
|
| + return CONSTANT_P (x) && (GET_CODE (x) != CONST || shared_const_p (x));
|
| }
|
|
|
| /* Scan pattern PAT of INSN and add an entry to the hash TABLE (set or
|
| expression one). */
|
|
|
| static void
|
| -hash_scan_set (rtx pat, rtx insn, struct hash_table *table)
|
| +hash_scan_set (rtx pat, rtx insn, struct hash_table_d *table)
|
| {
|
| rtx src = SET_SRC (pat);
|
| rtx dest = SET_DEST (pat);
|
| @@ -1732,9 +1338,11 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table *table)
|
| /* Don't GCSE something if we can't do a reg/reg copy. */
|
| && can_copy_p (GET_MODE (dest))
|
| /* GCSE commonly inserts instruction after the insn. We can't
|
| - do that easily for EH_REGION notes so disable GCSE on these
|
| - for now. */
|
| - && !find_reg_note (insn, REG_EH_REGION, NULL_RTX)
|
| + do that easily for EH edges so disable GCSE on these for now. */
|
| + /* ??? We can now easily create new EH landing pads at the
|
| + gimple level, for splitting edges; there's no reason we
|
| + can't do the same thing at the rtl level. */
|
| + && !can_throw_internal (insn)
|
| /* Is SET_SRC something we want to gcse? */
|
| && want_to_gcse_p (src)
|
| /* Don't CSE a nop. */
|
| @@ -1794,9 +1402,8 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table *table)
|
| /* Don't GCSE something if we can't do a reg/reg copy. */
|
| && can_copy_p (GET_MODE (src))
|
| /* GCSE commonly inserts instruction after the insn. We can't
|
| - do that easily for EH_REGION notes so disable GCSE on these
|
| - for now. */
|
| - && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
|
| + do that easily for EH edges so disable GCSE on these for now. */
|
| + && !can_throw_internal (insn)
|
| /* Is SET_DEST something we want to gcse? */
|
| && want_to_gcse_p (dest)
|
| /* Don't CSE a nop. */
|
| @@ -1827,14 +1434,14 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table *table)
|
|
|
| static void
|
| hash_scan_clobber (rtx x ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED,
|
| - struct hash_table *table ATTRIBUTE_UNUSED)
|
| + struct hash_table_d *table ATTRIBUTE_UNUSED)
|
| {
|
| /* Currently nothing to do. */
|
| }
|
|
|
| static void
|
| hash_scan_call (rtx x ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED,
|
| - struct hash_table *table ATTRIBUTE_UNUSED)
|
| + struct hash_table_d *table ATTRIBUTE_UNUSED)
|
| {
|
| /* Currently nothing to do. */
|
| }
|
| @@ -1851,7 +1458,7 @@ hash_scan_call (rtx x ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED,
|
| otherwise it is for the expression hash table. */
|
|
|
| static void
|
| -hash_scan_insn (rtx insn, struct hash_table *table)
|
| +hash_scan_insn (rtx insn, struct hash_table_d *table)
|
| {
|
| rtx pat = PATTERN (insn);
|
| int i;
|
| @@ -1881,7 +1488,7 @@ hash_scan_insn (rtx insn, struct hash_table *table)
|
| }
|
|
|
| static void
|
| -dump_hash_table (FILE *file, const char *name, struct hash_table *table)
|
| +dump_hash_table (FILE *file, const char *name, struct hash_table_d *table)
|
| {
|
| int i;
|
| /* Flattened out table, so it's printed in proper order. */
|
| @@ -1927,23 +1534,19 @@ dump_hash_table (FILE *file, const char *name, struct hash_table *table)
|
| is set and is used to compute "availability".
|
|
|
| last_bb records the block for which first_set and last_set are
|
| - valid, as a quick test to invalidate them.
|
| -
|
| - reg_set_in_block records whether the register is set in the block
|
| - and is used to compute "transparency". */
|
| + valid, as a quick test to invalidate them. */
|
|
|
| static void
|
| record_last_reg_set_info (rtx insn, int regno)
|
| {
|
| struct reg_avail_info *info = ®_avail_info[regno];
|
| - int cuid = INSN_CUID (insn);
|
| + int luid = DF_INSN_LUID (insn);
|
|
|
| - info->last_set = cuid;
|
| + info->last_set = luid;
|
| if (info->last_bb != current_bb)
|
| {
|
| info->last_bb = current_bb;
|
| - info->first_set = cuid;
|
| - SET_BIT (reg_set_in_block[current_bb->index], regno);
|
| + info->first_set = luid;
|
| }
|
| }
|
|
|
| @@ -1974,7 +1577,7 @@ canon_list_insert (rtx dest ATTRIBUTE_UNUSED, const_rtx unused1 ATTRIBUTE_UNUSED
|
| dest_addr = get_addr (XEXP (dest, 0));
|
| dest_addr = canon_rtx (dest_addr);
|
| insn = (rtx) v_insn;
|
| - bb = BLOCK_NUM (insn);
|
| + bb = BLOCK_FOR_INSN (insn)->index;
|
|
|
| canon_modify_mem_list[bb] =
|
| alloc_EXPR_LIST (VOIDmode, dest_addr, canon_modify_mem_list[bb]);
|
| @@ -1989,7 +1592,7 @@ canon_list_insert (rtx dest ATTRIBUTE_UNUSED, const_rtx unused1 ATTRIBUTE_UNUSED
|
| static void
|
| record_last_mem_set_info (rtx insn)
|
| {
|
| - int bb = BLOCK_NUM (insn);
|
| + int bb = BLOCK_FOR_INSN (insn)->index;
|
|
|
| /* load_killed_in_block_p will handle the case of calls clobbering
|
| everything. */
|
| @@ -2046,22 +1649,16 @@ record_last_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED, void *data)
|
| TABLE is the table computed. */
|
|
|
| static void
|
| -compute_hash_table_work (struct hash_table *table)
|
| +compute_hash_table_work (struct hash_table_d *table)
|
| {
|
| - unsigned int i;
|
| -
|
| - /* While we compute the hash table we also compute a bit array of which
|
| - registers are set in which blocks.
|
| - ??? This isn't needed during const/copy propagation, but it's cheap to
|
| - compute. Later. */
|
| - sbitmap_vector_zero (reg_set_in_block, last_basic_block);
|
| + int i;
|
|
|
| /* re-Cache any INSN_LIST nodes we have allocated. */
|
| clear_modify_mem_tables ();
|
| /* Some working arrays used to track first and last set in each block. */
|
| - reg_avail_info = GNEWVEC (struct reg_avail_info, max_gcse_regno);
|
| + reg_avail_info = GNEWVEC (struct reg_avail_info, max_reg_num ());
|
|
|
| - for (i = 0; i < max_gcse_regno; ++i)
|
| + for (i = 0; i < max_reg_num (); ++i)
|
| reg_avail_info[i].last_bb = NULL;
|
|
|
| FOR_EACH_BB (current_bb)
|
| @@ -2070,10 +1667,7 @@ compute_hash_table_work (struct hash_table *table)
|
| unsigned int regno;
|
|
|
| /* First pass over the instructions records information used to
|
| - determine when registers and memory are first and last set.
|
| - ??? hard-reg reg_set_in_block computation
|
| - could be moved to compute_sets since they currently don't change. */
|
| -
|
| + determine when registers and memory are first and last set. */
|
| FOR_BB_INSNS (current_bb, insn)
|
| {
|
| if (! INSN_P (insn))
|
| @@ -2108,17 +1702,18 @@ compute_hash_table_work (struct hash_table *table)
|
| }
|
|
|
| /* Allocate space for the set/expr hash TABLE.
|
| - N_INSNS is the number of instructions in the function.
|
| It is used to determine the number of buckets to use.
|
| SET_P determines whether set or expression table will
|
| be created. */
|
|
|
| static void
|
| -alloc_hash_table (int n_insns, struct hash_table *table, int set_p)
|
| +alloc_hash_table (struct hash_table_d *table, int set_p)
|
| {
|
| int n;
|
|
|
| - table->size = n_insns / 4;
|
| + n = get_max_insn_count ();
|
| +
|
| + table->size = n / 4;
|
| if (table->size < 11)
|
| table->size = 11;
|
|
|
| @@ -2134,7 +1729,7 @@ alloc_hash_table (int n_insns, struct hash_table *table, int set_p)
|
| /* Free things allocated by alloc_hash_table. */
|
|
|
| static void
|
| -free_hash_table (struct hash_table *table)
|
| +free_hash_table (struct hash_table_d *table)
|
| {
|
| free (table->table);
|
| }
|
| @@ -2143,7 +1738,7 @@ free_hash_table (struct hash_table *table)
|
| expression hash table. */
|
|
|
| static void
|
| -compute_hash_table (struct hash_table *table)
|
| +compute_hash_table (struct hash_table_d *table)
|
| {
|
| /* Initialize count of number of entries in hash table. */
|
| table->n_elems = 0;
|
| @@ -2158,7 +1753,7 @@ compute_hash_table (struct hash_table *table)
|
| table entry, or NULL if not found. */
|
|
|
| static struct expr *
|
| -lookup_set (unsigned int regno, struct hash_table *table)
|
| +lookup_set (unsigned int regno, struct hash_table_d *table)
|
| {
|
| unsigned int hash = hash_set (regno, table->size);
|
| struct expr *expr;
|
| @@ -2278,7 +1873,7 @@ oprs_not_set_p (const_rtx x, const_rtx insn)
|
|
|
| case MEM:
|
| if (load_killed_in_block_p (BLOCK_FOR_INSN (insn),
|
| - INSN_CUID (insn), x, 0))
|
| + DF_INSN_LUID (insn), x, 0))
|
| return 0;
|
| else
|
| return oprs_not_set_p (XEXP (x, 0), insn);
|
| @@ -2433,9 +2028,7 @@ static void
|
| compute_transp (const_rtx x, int indx, sbitmap *bmap, int set_p)
|
| {
|
| int i, j;
|
| - basic_block bb;
|
| enum rtx_code code;
|
| - reg_set *r;
|
| const char *fmt;
|
|
|
| /* repeat is used to turn tail-recursion into iteration since GCC
|
| @@ -2451,31 +2044,19 @@ compute_transp (const_rtx x, int indx, sbitmap *bmap, int set_p)
|
| case REG:
|
| if (set_p)
|
| {
|
| - if (REGNO (x) < FIRST_PSEUDO_REGISTER)
|
| - {
|
| - FOR_EACH_BB (bb)
|
| - if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
|
| - SET_BIT (bmap[bb->index], indx);
|
| - }
|
| - else
|
| - {
|
| - for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
|
| - SET_BIT (bmap[r->bb_index], indx);
|
| - }
|
| + df_ref def;
|
| + for (def = DF_REG_DEF_CHAIN (REGNO (x));
|
| + def;
|
| + def = DF_REF_NEXT_REG (def))
|
| + SET_BIT (bmap[DF_REF_BB (def)->index], indx);
|
| }
|
| else
|
| {
|
| - if (REGNO (x) < FIRST_PSEUDO_REGISTER)
|
| - {
|
| - FOR_EACH_BB (bb)
|
| - if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
|
| - RESET_BIT (bmap[bb->index], indx);
|
| - }
|
| - else
|
| - {
|
| - for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
|
| - RESET_BIT (bmap[r->bb_index], indx);
|
| - }
|
| + df_ref def;
|
| + for (def = DF_REG_DEF_CHAIN (REGNO (x));
|
| + def;
|
| + def = DF_REF_NEXT_REG (def))
|
| + RESET_BIT (bmap[DF_REF_BB (def)->index], indx);
|
| }
|
|
|
| return;
|
| @@ -2498,7 +2079,7 @@ compute_transp (const_rtx x, int indx, sbitmap *bmap, int set_p)
|
|
|
| /* Now iterate over the blocks which have memory modifications
|
| but which do not have any calls. */
|
| - EXECUTE_IF_AND_COMPL_IN_BITMAP (modify_mem_list_set,
|
| + EXECUTE_IF_AND_COMPL_IN_BITMAP (modify_mem_list_set,
|
| blocks_with_calls,
|
| 0, bb_index, bi)
|
| {
|
| @@ -2680,8 +2261,7 @@ try_replace_reg (rtx from, rtx to, rtx insn)
|
| with our replacement. */
|
| if (note != 0 && REG_NOTE_KIND (note) == REG_EQUAL)
|
| set_unique_reg_note (insn, REG_EQUAL,
|
| - simplify_replace_rtx (XEXP (note, 0), from,
|
| - copy_rtx (to)));
|
| + simplify_replace_rtx (XEXP (note, 0), from, to));
|
| if (!success && set && reg_mentioned_p (from, SET_SRC (set)))
|
| {
|
| /* If above failed and this is a single set, try to simplify the source of
|
| @@ -2740,7 +2320,8 @@ find_avail_set (int regno, rtx insn)
|
| which contains INSN. */
|
| while (set)
|
| {
|
| - if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
|
| + if (TEST_BIT (cprop_avin[BLOCK_FOR_INSN (insn)->index],
|
| + set->bitmap_index))
|
| break;
|
| set = next_set (regno, set);
|
| }
|
| @@ -2863,8 +2444,6 @@ cprop_jump (basic_block bb, rtx setcc, rtx jump, rtx from, rtx src)
|
| delete_insn (setcc);
|
| #endif
|
|
|
| - run_jump_opt_after_gcse = 1;
|
| -
|
| global_const_prop_count++;
|
| if (dump_file != NULL)
|
| {
|
| @@ -2898,14 +2477,13 @@ cprop_jump (basic_block bb, rtx setcc, rtx jump, rtx from, rtx src)
|
| }
|
|
|
| static bool
|
| -constprop_register (rtx insn, rtx from, rtx to, bool alter_jumps)
|
| +constprop_register (rtx insn, rtx from, rtx to)
|
| {
|
| rtx sset;
|
|
|
| /* Check for reg or cc0 setting instructions followed by
|
| conditional branch instructions first. */
|
| - if (alter_jumps
|
| - && (sset = single_set (insn)) != NULL
|
| + if ((sset = single_set (insn)) != NULL
|
| && NEXT_INSN (insn)
|
| && any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
|
| {
|
| @@ -2926,7 +2504,7 @@ constprop_register (rtx insn, rtx from, rtx to, bool alter_jumps)
|
|
|
| Right now the insn in question must look like
|
| (set (pc) (if_then_else ...)) */
|
| - else if (alter_jumps && any_condjump_p (insn) && onlyjump_p (insn))
|
| + else if (any_condjump_p (insn) && onlyjump_p (insn))
|
| return cprop_jump (BLOCK_FOR_INSN (insn), NULL, insn, from, to);
|
| return 0;
|
| }
|
| @@ -2935,7 +2513,7 @@ constprop_register (rtx insn, rtx from, rtx to, bool alter_jumps)
|
| The result is nonzero if a change was made. */
|
|
|
| static int
|
| -cprop_insn (rtx insn, int alter_jumps)
|
| +cprop_insn (rtx insn)
|
| {
|
| struct reg_use *reg_used;
|
| int changed = 0;
|
| @@ -2960,11 +2538,6 @@ cprop_insn (rtx insn, int alter_jumps)
|
| rtx pat, src;
|
| struct expr *set;
|
|
|
| - /* Ignore registers created by GCSE.
|
| - We do this because ... */
|
| - if (regno >= max_gcse_regno)
|
| - continue;
|
| -
|
| /* If the register has already been set in this block, there's
|
| nothing we can do. */
|
| if (! oprs_not_set_p (reg_used->reg_rtx, insn))
|
| @@ -2985,7 +2558,7 @@ cprop_insn (rtx insn, int alter_jumps)
|
| /* Constant propagation. */
|
| if (gcse_constant_p (src))
|
| {
|
| - if (constprop_register (insn, reg_used->reg_rtx, src, alter_jumps))
|
| + if (constprop_register (insn, reg_used->reg_rtx, src))
|
| {
|
| changed = 1;
|
| global_const_prop_count++;
|
| @@ -3024,6 +2597,9 @@ cprop_insn (rtx insn, int alter_jumps)
|
| }
|
| }
|
|
|
| + if (changed && DEBUG_INSN_P (insn))
|
| + return 0;
|
| +
|
| return changed;
|
| }
|
|
|
| @@ -3072,11 +2648,10 @@ local_cprop_find_used_regs (rtx *xptr, void *data)
|
| find_used_regs (xptr, data);
|
| }
|
|
|
| -/* Try to perform local const/copy propagation on X in INSN.
|
| - If ALTER_JUMPS is false, changing jump insns is not allowed. */
|
| +/* Try to perform local const/copy propagation on X in INSN. */
|
|
|
| static bool
|
| -do_local_cprop (rtx x, rtx insn, bool alter_jumps)
|
| +do_local_cprop (rtx x, rtx insn)
|
| {
|
| rtx newreg = NULL, newcnst = NULL;
|
|
|
| @@ -3109,7 +2684,7 @@ do_local_cprop (rtx x, rtx insn, bool alter_jumps)
|
| || ! MEM_P (XEXP (note, 0))))
|
| newreg = this_rtx;
|
| }
|
| - if (newcnst && constprop_register (insn, x, newcnst, alter_jumps))
|
| + if (newcnst && constprop_register (insn, x, newcnst))
|
| {
|
| if (dump_file != NULL)
|
| {
|
| @@ -3139,19 +2714,17 @@ do_local_cprop (rtx x, rtx insn, bool alter_jumps)
|
| return false;
|
| }
|
|
|
| -/* Do local const/copy propagation (i.e. within each basic block).
|
| - If ALTER_JUMPS is true, allow propagating into jump insns, which
|
| - could modify the CFG. */
|
| +/* Do local const/copy propagation (i.e. within each basic block). */
|
|
|
| -static void
|
| -local_cprop_pass (bool alter_jumps)
|
| +static int
|
| +local_cprop_pass (void)
|
| {
|
| basic_block bb;
|
| rtx insn;
|
| struct reg_use *reg_used;
|
| bool changed = false;
|
|
|
| - cselib_init (false);
|
| + cselib_init (0);
|
| FOR_EACH_BB (bb)
|
| {
|
| FOR_BB_INSNS (bb, insn)
|
| @@ -3170,7 +2743,7 @@ local_cprop_pass (bool alter_jumps)
|
| for (reg_used = ®_use_table[0]; reg_use_count > 0;
|
| reg_used++, reg_use_count--)
|
| {
|
| - if (do_local_cprop (reg_used->reg_rtx, insn, alter_jumps))
|
| + if (do_local_cprop (reg_used->reg_rtx, insn))
|
| {
|
| changed = true;
|
| break;
|
| @@ -3190,57 +2763,6 @@ local_cprop_pass (bool alter_jumps)
|
|
|
| cselib_finish ();
|
|
|
| - /* Global analysis may get into infinite loops for unreachable blocks. */
|
| - if (changed && alter_jumps)
|
| - {
|
| - delete_unreachable_blocks ();
|
| - free_reg_set_mem ();
|
| - alloc_reg_set_mem (max_reg_num ());
|
| - compute_sets ();
|
| - }
|
| -}
|
| -
|
| -/* Forward propagate copies. This includes copies and constants. Return
|
| - nonzero if a change was made. */
|
| -
|
| -static int
|
| -cprop (int alter_jumps)
|
| -{
|
| - int changed;
|
| - basic_block bb;
|
| - rtx insn;
|
| -
|
| - /* Note we start at block 1. */
|
| - if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
|
| - {
|
| - if (dump_file != NULL)
|
| - fprintf (dump_file, "\n");
|
| - return 0;
|
| - }
|
| -
|
| - changed = 0;
|
| - FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
|
| - {
|
| - /* Reset tables used to keep track of what's still valid [since the
|
| - start of the block]. */
|
| - reset_opr_set_tables ();
|
| -
|
| - FOR_BB_INSNS (bb, insn)
|
| - if (INSN_P (insn))
|
| - {
|
| - changed |= cprop_insn (insn, alter_jumps);
|
| -
|
| - /* Keep track of everything modified by this insn. */
|
| - /* ??? Need to be careful w.r.t. mods done to INSN. Don't
|
| - call mark_oprs_set if we turned the insn into a NOTE. */
|
| - if (! NOTE_P (insn))
|
| - mark_oprs_set (insn);
|
| - }
|
| - }
|
| -
|
| - if (dump_file != NULL)
|
| - fprintf (dump_file, "\n");
|
| -
|
| return changed;
|
| }
|
|
|
| @@ -3297,7 +2819,12 @@ implicit_set_cond_p (const_rtx cond)
|
| following "if (x == 2)", the then branch may be optimized as though the
|
| conditional performed an "explicit set", in this example, "x = 2". This
|
| function records the set patterns that are implicit at the start of each
|
| - basic block. */
|
| + basic block.
|
| +
|
| + FIXME: This would be more effective if critical edges are pre-split. As
|
| + it is now, we can't record implicit sets for blocks that have
|
| + critical successor edges. This results in missed optimizations
|
| + and in more (unnecessary) work in cfgcleanup.c:thread_jump(). */
|
|
|
| static void
|
| find_implicit_sets (void)
|
| @@ -3322,7 +2849,9 @@ find_implicit_sets (void)
|
| dest = GET_CODE (cond) == EQ ? BRANCH_EDGE (bb)->dest
|
| : FALLTHRU_EDGE (bb)->dest;
|
|
|
| - if (dest && single_pred_p (dest)
|
| + if (dest
|
| + /* Record nothing for a critical edge. */
|
| + && single_pred_p (dest)
|
| && dest != EXIT_BLOCK_PTR)
|
| {
|
| new_rtx = gen_rtx_SET (VOIDmode, XEXP (cond, 0),
|
| @@ -3343,63 +2872,6 @@ find_implicit_sets (void)
|
| fprintf (dump_file, "Found %d implicit sets\n", count);
|
| }
|
|
|
| -/* Perform one copy/constant propagation pass.
|
| - PASS is the pass count. If CPROP_JUMPS is true, perform constant
|
| - propagation into conditional jumps. If BYPASS_JUMPS is true,
|
| - perform conditional jump bypassing optimizations. */
|
| -
|
| -static int
|
| -one_cprop_pass (int pass, bool cprop_jumps, bool bypass_jumps)
|
| -{
|
| - int changed = 0;
|
| -
|
| - global_const_prop_count = local_const_prop_count = 0;
|
| - global_copy_prop_count = local_copy_prop_count = 0;
|
| -
|
| - if (cprop_jumps)
|
| - local_cprop_pass (cprop_jumps);
|
| -
|
| - /* Determine implicit sets. */
|
| - implicit_sets = XCNEWVEC (rtx, last_basic_block);
|
| - find_implicit_sets ();
|
| -
|
| - alloc_hash_table (max_cuid, &set_hash_table, 1);
|
| - compute_hash_table (&set_hash_table);
|
| -
|
| - /* Free implicit_sets before peak usage. */
|
| - free (implicit_sets);
|
| - implicit_sets = NULL;
|
| -
|
| - if (dump_file)
|
| - dump_hash_table (dump_file, "SET", &set_hash_table);
|
| - if (set_hash_table.n_elems > 0)
|
| - {
|
| - alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
|
| - compute_cprop_data ();
|
| - changed = cprop (cprop_jumps);
|
| - if (bypass_jumps)
|
| - changed |= bypass_conditional_jumps ();
|
| - free_cprop_mem ();
|
| - }
|
| -
|
| - free_hash_table (&set_hash_table);
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "CPROP of %s, pass %d: %d bytes needed, ",
|
| - current_function_name (), pass, bytes_used);
|
| - fprintf (dump_file, "%d local const props, %d local copy props, ",
|
| - local_const_prop_count, local_copy_prop_count);
|
| - fprintf (dump_file, "%d global const props, %d global copy props\n\n",
|
| - global_const_prop_count, global_copy_prop_count);
|
| - }
|
| - /* Global analysis may get into infinite loops for unreachable blocks. */
|
| - if (changed && cprop_jumps)
|
| - delete_unreachable_blocks ();
|
| -
|
| - return changed;
|
| -}
|
| -
|
| /* Bypass conditional jumps. */
|
|
|
| /* The value of last_basic_block at the beginning of the jump_bypass
|
| @@ -3507,7 +2979,7 @@ bypass_block (basic_block bb, rtx setcc, rtx jump)
|
| for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
|
| {
|
| removed_p = 0;
|
| -
|
| +
|
| if (e->flags & EDGE_COMPLEX)
|
| {
|
| ei_next (&ei);
|
| @@ -3539,9 +3011,6 @@ bypass_block (basic_block bb, rtx setcc, rtx jump)
|
| struct expr *set;
|
| rtx src, new_rtx;
|
|
|
| - if (regno >= max_gcse_regno)
|
| - continue;
|
| -
|
| set = find_bypass_set (regno, e->src->index);
|
|
|
| if (! set)
|
| @@ -3554,12 +3023,12 @@ bypass_block (basic_block bb, rtx setcc, rtx jump)
|
| src = SET_SRC (pc_set (jump));
|
|
|
| if (setcc != NULL)
|
| - src = simplify_replace_rtx (src,
|
| - SET_DEST (PATTERN (setcc)),
|
| - SET_SRC (PATTERN (setcc)));
|
| + src = simplify_replace_rtx (src,
|
| + SET_DEST (PATTERN (setcc)),
|
| + SET_SRC (PATTERN (setcc)));
|
|
|
| new_rtx = simplify_replace_rtx (src, reg_used->reg_rtx,
|
| - SET_SRC (set->expr));
|
| + SET_SRC (set->expr));
|
|
|
| /* Jump bypassing may have already placed instructions on
|
| edges of the CFG. We can't bypass an outgoing edge that
|
| @@ -3658,7 +3127,9 @@ bypass_conditional_jumps (void)
|
| {
|
| setcc = NULL_RTX;
|
| FOR_BB_INSNS (bb, insn)
|
| - if (NONJUMP_INSN_P (insn))
|
| + if (DEBUG_INSN_P (insn))
|
| + continue;
|
| + else if (NONJUMP_INSN_P (insn))
|
| {
|
| if (setcc)
|
| break;
|
| @@ -3724,9 +3195,6 @@ static sbitmap *pre_delete_map;
|
| /* Contains the edge_list returned by pre_edge_lcm. */
|
| static struct edge_list *edge_list;
|
|
|
| -/* Redundant insns. */
|
| -static sbitmap pre_redundant_insns;
|
| -
|
| /* Allocate vars used for PRE analysis. */
|
|
|
| static void
|
| @@ -3846,7 +3314,7 @@ pre_expr_reaches_here_p_work (basic_block occr_bb, struct expr *expr, basic_bloc
|
| {
|
| edge pred;
|
| edge_iterator ei;
|
| -
|
| +
|
| FOR_EACH_EDGE (pred, ei, bb->preds)
|
| {
|
| basic_block pred_bb = pred->src;
|
| @@ -3928,7 +3396,7 @@ process_insert_insn (struct expr *expr)
|
| if (insn_invalid_p (insn))
|
| gcc_unreachable ();
|
| }
|
| -
|
| +
|
|
|
| pat = get_insns ();
|
| end_sequence ();
|
| @@ -4049,10 +3517,7 @@ insert_insn_end_basic_block (struct expr *expr, basic_block bb, int pre)
|
| while (1)
|
| {
|
| if (INSN_P (pat))
|
| - {
|
| - add_label_notes (PATTERN (pat), new_insn);
|
| - note_stores (PATTERN (pat), record_set_info, pat);
|
| - }
|
| + add_label_notes (PATTERN (pat), new_insn);
|
| if (pat == pat_end)
|
| break;
|
| pat = NEXT_INSN (pat);
|
| @@ -4131,7 +3596,7 @@ pre_edge_insert (struct edge_list *edge_list, struct expr **index_map)
|
|
|
| if (dump_file)
|
| {
|
| - fprintf (dump_file, "PRE/HOIST: edge (%d,%d), ",
|
| + fprintf (dump_file, "PRE: edge (%d,%d), ",
|
| bb->index,
|
| INDEX_EDGE_SUCC_BB (edge_list, e)->index);
|
| fprintf (dump_file, "copy expression %d\n",
|
| @@ -4225,17 +3690,11 @@ pre_insert_copy_insn (struct expr *expr, rtx insn)
|
| {
|
| new_insn = gen_move_insn (old_reg, reg);
|
| new_insn = emit_insn_after (new_insn, insn);
|
| -
|
| - /* Keep register set table up to date. */
|
| - record_one_set (regno, insn);
|
| }
|
| else
|
| {
|
| new_insn = gen_move_insn (reg, old_reg);
|
| new_insn = emit_insn_after (new_insn, insn);
|
| -
|
| - /* Keep register set table up to date. */
|
| - record_one_set (regno, new_insn);
|
| }
|
| }
|
| else /* This is possible only in case of a store to memory. */
|
| @@ -4248,9 +3707,6 @@ pre_insert_copy_insn (struct expr *expr, rtx insn)
|
| new_insn = emit_insn_before (new_insn, insn);
|
| else
|
| new_insn = emit_insn_after (new_insn, insn);
|
| -
|
| - /* Keep register set table up to date. */
|
| - record_one_set (regno, new_insn);
|
| }
|
|
|
| gcse_create_count++;
|
| @@ -4258,7 +3714,7 @@ pre_insert_copy_insn (struct expr *expr, rtx insn)
|
| if (dump_file)
|
| fprintf (dump_file,
|
| "PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
|
| - BLOCK_NUM (insn), INSN_UID (new_insn), indx,
|
| + BLOCK_FOR_INSN (insn)->index, INSN_UID (new_insn), indx,
|
| INSN_UID (insn), regno);
|
| }
|
|
|
| @@ -4307,7 +3763,7 @@ pre_insert_copies (void)
|
| continue;
|
|
|
| /* Don't handle this one if it's a redundant one. */
|
| - if (TEST_BIT (pre_redundant_insns, INSN_CUID (insn)))
|
| + if (INSN_DELETED_P (insn))
|
| continue;
|
|
|
| /* Or if the expression doesn't reach the deleted one. */
|
| @@ -4404,7 +3860,6 @@ pre_delete (void)
|
| gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
|
| delete_insn (insn);
|
| occr->deleted_p = 1;
|
| - SET_BIT (pre_redundant_insns, INSN_CUID (insn));
|
| changed = 1;
|
| gcse_subst_count++;
|
|
|
| @@ -4459,10 +3914,6 @@ pre_gcse (void)
|
| for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
|
| index_map[expr->bitmap_index] = expr;
|
|
|
| - /* Reset bitmap used to track which insns are redundant. */
|
| - pre_redundant_insns = sbitmap_alloc (max_cuid);
|
| - sbitmap_zero (pre_redundant_insns);
|
| -
|
| /* Delete the redundant insns first so that
|
| - we know what register to use for the new insns and for the other
|
| ones with reaching expressions
|
| @@ -4481,7 +3932,6 @@ pre_gcse (void)
|
| }
|
|
|
| free (index_map);
|
| - sbitmap_free (pre_redundant_insns);
|
| return changed;
|
| }
|
|
|
| @@ -4490,14 +3940,26 @@ pre_gcse (void)
|
| Return nonzero if a change was made. */
|
|
|
| static int
|
| -one_pre_gcse_pass (int pass)
|
| +one_pre_gcse_pass (void)
|
| {
|
| int changed = 0;
|
|
|
| gcse_subst_count = 0;
|
| gcse_create_count = 0;
|
|
|
| - alloc_hash_table (max_cuid, &expr_hash_table, 0);
|
| + /* Return if there's nothing to do, or it is too expensive. */
|
| + if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|
| + || is_too_expensive (_("PRE disabled")))
|
| + return 0;
|
| +
|
| + /* We need alias. */
|
| + init_alias_analysis ();
|
| +
|
| + bytes_used = 0;
|
| + gcc_obstack_init (&gcse_obstack);
|
| + alloc_gcse_mem ();
|
| +
|
| + alloc_hash_table (&expr_hash_table, 0);
|
| add_noreturn_fake_exit_edges ();
|
| if (flag_gcse_lm)
|
| compute_ld_motion_mems ();
|
| @@ -4520,10 +3982,16 @@ one_pre_gcse_pass (int pass)
|
| remove_fake_exit_edges ();
|
| free_hash_table (&expr_hash_table);
|
|
|
| + free_gcse_mem ();
|
| + obstack_free (&gcse_obstack, NULL);
|
| +
|
| + /* We are finished with alias. */
|
| + end_alias_analysis ();
|
| +
|
| if (dump_file)
|
| {
|
| - fprintf (dump_file, "\nPRE GCSE of %s, pass %d: %d bytes needed, ",
|
| - current_function_name (), pass, bytes_used);
|
| + fprintf (dump_file, "PRE GCSE of %s, %d basic blocks, %d bytes needed, ",
|
| + current_function_name (), n_basic_blocks, bytes_used);
|
| fprintf (dump_file, "%d substs, %d insns created\n",
|
| gcse_subst_count, gcse_create_count);
|
| }
|
| @@ -4788,7 +4256,7 @@ hoist_expr_reaches_here_p (basic_block expr_bb, int expr_index, basic_block bb,
|
|
|
| /* Actually perform code hoisting. */
|
|
|
| -static void
|
| +static int
|
| hoist_code (void)
|
| {
|
| basic_block bb, dominated;
|
| @@ -4796,6 +4264,7 @@ hoist_code (void)
|
| unsigned int i,j;
|
| struct expr **index_map;
|
| struct expr *expr;
|
| + int changed = 0;
|
|
|
| sbitmap_vector_zero (hoist_exprs, last_basic_block);
|
|
|
| @@ -4927,6 +4396,9 @@ hoist_code (void)
|
| gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
|
| delete_insn (insn);
|
| occr->deleted_p = 1;
|
| + changed = 1;
|
| + gcse_subst_count++;
|
| +
|
| if (!insn_inserted_p)
|
| {
|
| insert_insn_end_basic_block (index_map[i], bb, 0);
|
| @@ -4940,6 +4412,8 @@ hoist_code (void)
|
| }
|
|
|
| free (index_map);
|
| +
|
| + return changed;
|
| }
|
|
|
| /* Top level routine to perform one code hoisting (aka unification) pass
|
| @@ -4951,7 +4425,22 @@ one_code_hoisting_pass (void)
|
| {
|
| int changed = 0;
|
|
|
| - alloc_hash_table (max_cuid, &expr_hash_table, 0);
|
| + gcse_subst_count = 0;
|
| + gcse_create_count = 0;
|
| +
|
| + /* Return if there's nothing to do, or it is too expensive. */
|
| + if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|
| + || is_too_expensive (_("GCSE disabled")))
|
| + return 0;
|
| +
|
| + /* We need alias. */
|
| + init_alias_analysis ();
|
| +
|
| + bytes_used = 0;
|
| + gcc_obstack_init (&gcse_obstack);
|
| + alloc_gcse_mem ();
|
| +
|
| + alloc_hash_table (&expr_hash_table, 0);
|
| compute_hash_table (&expr_hash_table);
|
| if (dump_file)
|
| dump_hash_table (dump_file, "Code Hosting Expressions", &expr_hash_table);
|
| @@ -4960,11 +4449,24 @@ one_code_hoisting_pass (void)
|
| {
|
| alloc_code_hoist_mem (last_basic_block, expr_hash_table.n_elems);
|
| compute_code_hoist_data ();
|
| - hoist_code ();
|
| + changed = hoist_code ();
|
| free_code_hoist_mem ();
|
| }
|
|
|
| free_hash_table (&expr_hash_table);
|
| + free_gcse_mem ();
|
| + obstack_free (&gcse_obstack, NULL);
|
| +
|
| + /* We are finished with alias. */
|
| + end_alias_analysis ();
|
| +
|
| + if (dump_file)
|
| + {
|
| + fprintf (dump_file, "HOIST of %s, %d basic blocks, %d bytes needed, ",
|
| + current_function_name (), n_basic_blocks, bytes_used);
|
| + fprintf (dump_file, "%d substs, %d insns created\n",
|
| + gcse_subst_count, gcse_create_count);
|
| + }
|
|
|
| return changed;
|
| }
|
| @@ -5131,20 +4633,6 @@ find_rtx_in_ldst (rtx x)
|
| return (struct ls_expr *) *slot;
|
| }
|
|
|
| -/* Assign each element of the list of mems a monotonically increasing value. */
|
| -
|
| -static int
|
| -enumerate_ldsts (void)
|
| -{
|
| - struct ls_expr * ptr;
|
| - int n = 0;
|
| -
|
| - for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
|
| - ptr->index = n++;
|
| -
|
| - return n;
|
| -}
|
| -
|
| /* Return first item in the list. */
|
|
|
| static inline struct ls_expr *
|
| @@ -5256,7 +4744,7 @@ compute_ld_motion_mems (void)
|
| {
|
| FOR_BB_INSNS (bb, insn)
|
| {
|
| - if (INSN_P (insn))
|
| + if (NONDEBUG_INSN_P (insn))
|
| {
|
| if (GET_CODE (PATTERN (insn)) == SET)
|
| {
|
| @@ -5290,7 +4778,7 @@ compute_ld_motion_mems (void)
|
| && GET_CODE (src) != ASM_OPERANDS
|
| /* Check for REG manually since want_to_gcse_p
|
| returns 0 for all REGs. */
|
| - && can_assign_to_reg_p (src))
|
| + && can_assign_to_reg_without_clobbers_p (src))
|
| ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
|
| else
|
| ptr->invalid = 1;
|
| @@ -5382,7 +4870,7 @@ update_ld_motion_stores (struct expr * expr)
|
| rtx pat = PATTERN (insn);
|
| rtx src = SET_SRC (pat);
|
| rtx reg = expr->reaching_reg;
|
| - rtx copy, new_rtx;
|
| + rtx copy;
|
|
|
| /* If we've already copied it, continue. */
|
| if (expr->reaching_reg == src)
|
| @@ -5397,9 +4885,8 @@ update_ld_motion_stores (struct expr * expr)
|
| fprintf (dump_file, "\n");
|
| }
|
|
|
| - copy = gen_move_insn ( reg, copy_rtx (SET_SRC (pat)));
|
| - new_rtx = emit_insn_before (copy, insn);
|
| - record_one_set (REGNO (reg), new_rtx);
|
| + copy = gen_move_insn (reg, copy_rtx (SET_SRC (pat)));
|
| + emit_insn_before (copy, insn);
|
| SET_SRC (pat) = reg;
|
| df_insn_rescan (insn);
|
|
|
| @@ -5410,1289 +4897,268 @@ update_ld_motion_stores (struct expr * expr)
|
| }
|
| }
|
|
|
| -/* Store motion code. */
|
| -
|
| -#define ANTIC_STORE_LIST(x) ((x)->loads)
|
| -#define AVAIL_STORE_LIST(x) ((x)->stores)
|
| -#define LAST_AVAIL_CHECK_FAILURE(x) ((x)->reaching_reg)
|
| -
|
| -/* This is used to communicate the target bitvector we want to use in the
|
| - reg_set_info routine when called via the note_stores mechanism. */
|
| -static int * regvec;
|
| -
|
| -/* And current insn, for the same routine. */
|
| -static rtx compute_store_table_current_insn;
|
| -
|
| -/* Used in computing the reverse edge graph bit vectors. */
|
| -static sbitmap * st_antloc;
|
| -
|
| -/* Global holding the number of store expressions we are dealing with. */
|
| -static int num_stores;
|
| -
|
| -/* Checks to set if we need to mark a register set. Called from
|
| - note_stores. */
|
| -
|
| -static void
|
| -reg_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
|
| - void *data)
|
| -{
|
| - sbitmap bb_reg = (sbitmap) data;
|
| -
|
| - if (GET_CODE (dest) == SUBREG)
|
| - dest = SUBREG_REG (dest);
|
| -
|
| - if (REG_P (dest))
|
| - {
|
| - regvec[REGNO (dest)] = INSN_UID (compute_store_table_current_insn);
|
| - if (bb_reg)
|
| - SET_BIT (bb_reg, REGNO (dest));
|
| - }
|
| -}
|
| -
|
| -/* Clear any mark that says that this insn sets dest. Called from
|
| - note_stores. */
|
| -
|
| -static void
|
| -reg_clear_last_set (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
|
| - void *data)
|
| -{
|
| - int *dead_vec = (int *) data;
|
| -
|
| - if (GET_CODE (dest) == SUBREG)
|
| - dest = SUBREG_REG (dest);
|
| -
|
| - if (REG_P (dest) &&
|
| - dead_vec[REGNO (dest)] == INSN_UID (compute_store_table_current_insn))
|
| - dead_vec[REGNO (dest)] = 0;
|
| -}
|
| -
|
| -/* Return zero if some of the registers in list X are killed
|
| - due to set of registers in bitmap REGS_SET. */
|
| -
|
| -static bool
|
| -store_ops_ok (const_rtx x, int *regs_set)
|
| -{
|
| - const_rtx reg;
|
| -
|
| - for (; x; x = XEXP (x, 1))
|
| - {
|
| - reg = XEXP (x, 0);
|
| - if (regs_set[REGNO(reg)])
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -/* Returns a list of registers mentioned in X. */
|
| -static rtx
|
| -extract_mentioned_regs (rtx x)
|
| -{
|
| - return extract_mentioned_regs_helper (x, NULL_RTX);
|
| -}
|
| -
|
| -/* Helper for extract_mentioned_regs; ACCUM is used to accumulate used
|
| - registers. */
|
| -static rtx
|
| -extract_mentioned_regs_helper (rtx x, rtx accum)
|
| -{
|
| - int i;
|
| - enum rtx_code code;
|
| - const char * fmt;
|
| -
|
| - /* Repeat is used to turn tail-recursion into iteration. */
|
| - repeat:
|
| -
|
| - if (x == 0)
|
| - return accum;
|
| -
|
| - code = GET_CODE (x);
|
| - switch (code)
|
| - {
|
| - case REG:
|
| - return alloc_EXPR_LIST (0, x, accum);
|
| -
|
| - case MEM:
|
| - x = XEXP (x, 0);
|
| - goto repeat;
|
| -
|
| - case PRE_DEC:
|
| - case PRE_INC:
|
| - case PRE_MODIFY:
|
| - case POST_DEC:
|
| - case POST_INC:
|
| - case POST_MODIFY:
|
| - /* We do not run this function with arguments having side effects. */
|
| - gcc_unreachable ();
|
| -
|
| - case PC:
|
| - case CC0: /*FIXME*/
|
| - case CONST:
|
| - case CONST_INT:
|
| - case CONST_DOUBLE:
|
| - case CONST_FIXED:
|
| - case CONST_VECTOR:
|
| - case SYMBOL_REF:
|
| - case LABEL_REF:
|
| - case ADDR_VEC:
|
| - case ADDR_DIFF_VEC:
|
| - return accum;
|
| -
|
| - default:
|
| - break;
|
| - }
|
| -
|
| - i = GET_RTX_LENGTH (code) - 1;
|
| - fmt = GET_RTX_FORMAT (code);
|
| -
|
| - for (; i >= 0; i--)
|
| - {
|
| - if (fmt[i] == 'e')
|
| - {
|
| - rtx tem = XEXP (x, i);
|
| -
|
| - /* If we are about to do the last recursive call
|
| - needed at this level, change it into iteration. */
|
| - if (i == 0)
|
| - {
|
| - x = tem;
|
| - goto repeat;
|
| - }
|
| -
|
| - accum = extract_mentioned_regs_helper (tem, accum);
|
| - }
|
| - else if (fmt[i] == 'E')
|
| - {
|
| - int j;
|
| -
|
| - for (j = 0; j < XVECLEN (x, i); j++)
|
| - accum = extract_mentioned_regs_helper (XVECEXP (x, i, j), accum);
|
| - }
|
| - }
|
| -
|
| - return accum;
|
| -}
|
| -
|
| -/* Determine whether INSN is MEM store pattern that we will consider moving.
|
| - REGS_SET_BEFORE is bitmap of registers set before (and including) the
|
| - current insn, REGS_SET_AFTER is bitmap of registers set after (and
|
| - including) the insn in this basic block. We must be passing through BB from
|
| - head to end, as we are using this fact to speed things up.
|
| -
|
| - The results are stored this way:
|
| -
|
| - -- the first anticipatable expression is added into ANTIC_STORE_LIST
|
| - -- if the processed expression is not anticipatable, NULL_RTX is added
|
| - there instead, so that we can use it as indicator that no further
|
| - expression of this type may be anticipatable
|
| - -- if the expression is available, it is added as head of AVAIL_STORE_LIST;
|
| - consequently, all of them but this head are dead and may be deleted.
|
| - -- if the expression is not available, the insn due to that it fails to be
|
| - available is stored in reaching_reg.
|
| -
|
| - The things are complicated a bit by fact that there already may be stores
|
| - to the same MEM from other blocks; also caller must take care of the
|
| - necessary cleanup of the temporary markers after end of the basic block.
|
| - */
|
| -
|
| -static void
|
| -find_moveable_store (rtx insn, int *regs_set_before, int *regs_set_after)
|
| -{
|
| - struct ls_expr * ptr;
|
| - rtx dest, set, tmp;
|
| - int check_anticipatable, check_available;
|
| - basic_block bb = BLOCK_FOR_INSN (insn);
|
| -
|
| - set = single_set (insn);
|
| - if (!set)
|
| - return;
|
| -
|
| - dest = SET_DEST (set);
|
| -
|
| - if (! MEM_P (dest) || MEM_VOLATILE_P (dest)
|
| - || GET_MODE (dest) == BLKmode)
|
| - return;
|
| -
|
| - if (side_effects_p (dest))
|
| - return;
|
| -
|
| - /* If we are handling exceptions, we must be careful with memory references
|
| - that may trap. If we are not, the behavior is undefined, so we may just
|
| - continue. */
|
| - if (flag_non_call_exceptions && may_trap_p (dest))
|
| - return;
|
| -
|
| - /* Even if the destination cannot trap, the source may. In this case we'd
|
| - need to handle updating the REG_EH_REGION note. */
|
| - if (find_reg_note (insn, REG_EH_REGION, NULL_RTX))
|
| - return;
|
| -
|
| - /* Make sure that the SET_SRC of this store insns can be assigned to
|
| - a register, or we will fail later on in replace_store_insn, which
|
| - assumes that we can do this. But sometimes the target machine has
|
| - oddities like MEM read-modify-write instruction. See for example
|
| - PR24257. */
|
| - if (!can_assign_to_reg_p (SET_SRC (set)))
|
| - return;
|
| -
|
| - ptr = ldst_entry (dest);
|
| - if (!ptr->pattern_regs)
|
| - ptr->pattern_regs = extract_mentioned_regs (dest);
|
| -
|
| - /* Do not check for anticipatability if we either found one anticipatable
|
| - store already, or tested for one and found out that it was killed. */
|
| - check_anticipatable = 0;
|
| - if (!ANTIC_STORE_LIST (ptr))
|
| - check_anticipatable = 1;
|
| - else
|
| - {
|
| - tmp = XEXP (ANTIC_STORE_LIST (ptr), 0);
|
| - if (tmp != NULL_RTX
|
| - && BLOCK_FOR_INSN (tmp) != bb)
|
| - check_anticipatable = 1;
|
| - }
|
| - if (check_anticipatable)
|
| - {
|
| - if (store_killed_before (dest, ptr->pattern_regs, insn, bb, regs_set_before))
|
| - tmp = NULL_RTX;
|
| - else
|
| - tmp = insn;
|
| - ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (tmp,
|
| - ANTIC_STORE_LIST (ptr));
|
| - }
|
| -
|
| - /* It is not necessary to check whether store is available if we did
|
| - it successfully before; if we failed before, do not bother to check
|
| - until we reach the insn that caused us to fail. */
|
| - check_available = 0;
|
| - if (!AVAIL_STORE_LIST (ptr))
|
| - check_available = 1;
|
| - else
|
| - {
|
| - tmp = XEXP (AVAIL_STORE_LIST (ptr), 0);
|
| - if (BLOCK_FOR_INSN (tmp) != bb)
|
| - check_available = 1;
|
| - }
|
| - if (check_available)
|
| - {
|
| - /* Check that we have already reached the insn at that the check
|
| - failed last time. */
|
| - if (LAST_AVAIL_CHECK_FAILURE (ptr))
|
| - {
|
| - for (tmp = BB_END (bb);
|
| - tmp != insn && tmp != LAST_AVAIL_CHECK_FAILURE (ptr);
|
| - tmp = PREV_INSN (tmp))
|
| - continue;
|
| - if (tmp == insn)
|
| - check_available = 0;
|
| - }
|
| - else
|
| - check_available = store_killed_after (dest, ptr->pattern_regs, insn,
|
| - bb, regs_set_after,
|
| - &LAST_AVAIL_CHECK_FAILURE (ptr));
|
| - }
|
| - if (!check_available)
|
| - AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn, AVAIL_STORE_LIST (ptr));
|
| -}
|
| -
|
| -/* Find available and anticipatable stores. */
|
| -
|
| -static int
|
| -compute_store_table (void)
|
| -{
|
| - int ret;
|
| - basic_block bb;
|
| - unsigned regno;
|
| - rtx insn, pat, tmp;
|
| - int *last_set_in, *already_set;
|
| - struct ls_expr * ptr, **prev_next_ptr_ptr;
|
| -
|
| - max_gcse_regno = max_reg_num ();
|
| -
|
| - reg_set_in_block = sbitmap_vector_alloc (last_basic_block,
|
| - max_gcse_regno);
|
| - sbitmap_vector_zero (reg_set_in_block, last_basic_block);
|
| - pre_ldst_mems = 0;
|
| - pre_ldst_table = htab_create (13, pre_ldst_expr_hash,
|
| - pre_ldst_expr_eq, NULL);
|
| - last_set_in = XCNEWVEC (int, max_gcse_regno);
|
| - already_set = XNEWVEC (int, max_gcse_regno);
|
| -
|
| - /* Find all the stores we care about. */
|
| - FOR_EACH_BB (bb)
|
| - {
|
| - /* First compute the registers set in this block. */
|
| - regvec = last_set_in;
|
| -
|
| - FOR_BB_INSNS (bb, insn)
|
| - {
|
| - if (! INSN_P (insn))
|
| - continue;
|
| -
|
| - if (CALL_P (insn))
|
| - {
|
| - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
| - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
|
| - {
|
| - last_set_in[regno] = INSN_UID (insn);
|
| - SET_BIT (reg_set_in_block[bb->index], regno);
|
| - }
|
| - }
|
| -
|
| - pat = PATTERN (insn);
|
| - compute_store_table_current_insn = insn;
|
| - note_stores (pat, reg_set_info, reg_set_in_block[bb->index]);
|
| - }
|
| -
|
| - /* Now find the stores. */
|
| - memset (already_set, 0, sizeof (int) * max_gcse_regno);
|
| - regvec = already_set;
|
| - FOR_BB_INSNS (bb, insn)
|
| - {
|
| - if (! INSN_P (insn))
|
| - continue;
|
| -
|
| - if (CALL_P (insn))
|
| - {
|
| - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
| - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
|
| - already_set[regno] = 1;
|
| - }
|
| -
|
| - pat = PATTERN (insn);
|
| - note_stores (pat, reg_set_info, NULL);
|
| -
|
| - /* Now that we've marked regs, look for stores. */
|
| - find_moveable_store (insn, already_set, last_set_in);
|
| -
|
| - /* Unmark regs that are no longer set. */
|
| - compute_store_table_current_insn = insn;
|
| - note_stores (pat, reg_clear_last_set, last_set_in);
|
| - if (CALL_P (insn))
|
| - {
|
| - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
| - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)
|
| - && last_set_in[regno] == INSN_UID (insn))
|
| - last_set_in[regno] = 0;
|
| - }
|
| - }
|
| -
|
| -#ifdef ENABLE_CHECKING
|
| - /* last_set_in should now be all-zero. */
|
| - for (regno = 0; regno < max_gcse_regno; regno++)
|
| - gcc_assert (!last_set_in[regno]);
|
| -#endif
|
| -
|
| - /* Clear temporary marks. */
|
| - for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
|
| - {
|
| - LAST_AVAIL_CHECK_FAILURE(ptr) = NULL_RTX;
|
| - if (ANTIC_STORE_LIST (ptr)
|
| - && (tmp = XEXP (ANTIC_STORE_LIST (ptr), 0)) == NULL_RTX)
|
| - ANTIC_STORE_LIST (ptr) = XEXP (ANTIC_STORE_LIST (ptr), 1);
|
| - }
|
| - }
|
| -
|
| - /* Remove the stores that are not available anywhere, as there will
|
| - be no opportunity to optimize them. */
|
| - for (ptr = pre_ldst_mems, prev_next_ptr_ptr = &pre_ldst_mems;
|
| - ptr != NULL;
|
| - ptr = *prev_next_ptr_ptr)
|
| - {
|
| - if (!AVAIL_STORE_LIST (ptr))
|
| - {
|
| - *prev_next_ptr_ptr = ptr->next;
|
| - htab_remove_elt_with_hash (pre_ldst_table, ptr, ptr->hash_index);
|
| - free_ldst_entry (ptr);
|
| - }
|
| - else
|
| - prev_next_ptr_ptr = &ptr->next;
|
| - }
|
| -
|
| - ret = enumerate_ldsts ();
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "ST_avail and ST_antic (shown under loads..)\n");
|
| - print_ldst_list (dump_file);
|
| - }
|
| -
|
| - free (last_set_in);
|
| - free (already_set);
|
| - return ret;
|
| -}
|
| -
|
| -/* Check to see if the load X is aliased with STORE_PATTERN.
|
| - AFTER is true if we are checking the case when STORE_PATTERN occurs
|
| - after the X. */
|
| -
|
| -static bool
|
| -load_kills_store (const_rtx x, const_rtx store_pattern, int after)
|
| -{
|
| - if (after)
|
| - return anti_dependence (x, store_pattern);
|
| - else
|
| - return true_dependence (store_pattern, GET_MODE (store_pattern), x,
|
| - rtx_addr_varies_p);
|
| -}
|
| -
|
| -/* Go through the entire insn X, looking for any loads which might alias
|
| - STORE_PATTERN. Return true if found.
|
| - AFTER is true if we are checking the case when STORE_PATTERN occurs
|
| - after the insn X. */
|
| -
|
| -static bool
|
| -find_loads (const_rtx x, const_rtx store_pattern, int after)
|
| -{
|
| - const char * fmt;
|
| - int i, j;
|
| - int ret = false;
|
| -
|
| - if (!x)
|
| - return false;
|
| -
|
| - if (GET_CODE (x) == SET)
|
| - x = SET_SRC (x);
|
| -
|
| - if (MEM_P (x))
|
| - {
|
| - if (load_kills_store (x, store_pattern, after))
|
| - return true;
|
| - }
|
| -
|
| - /* Recursively process the insn. */
|
| - fmt = GET_RTX_FORMAT (GET_CODE (x));
|
| -
|
| - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
|
| - {
|
| - if (fmt[i] == 'e')
|
| - ret |= find_loads (XEXP (x, i), store_pattern, after);
|
| - else if (fmt[i] == 'E')
|
| - for (j = XVECLEN (x, i) - 1; j >= 0; j--)
|
| - ret |= find_loads (XVECEXP (x, i, j), store_pattern, after);
|
| - }
|
| - return ret;
|
| -}
|
| -
|
| -static inline bool
|
| -store_killed_in_pat (const_rtx x, const_rtx pat, int after)
|
| -{
|
| - if (GET_CODE (pat) == SET)
|
| - {
|
| - rtx dest = SET_DEST (pat);
|
| -
|
| - if (GET_CODE (dest) == ZERO_EXTRACT)
|
| - dest = XEXP (dest, 0);
|
| -
|
| - /* Check for memory stores to aliased objects. */
|
| - if (MEM_P (dest)
|
| - && !expr_equiv_p (dest, x))
|
| - {
|
| - if (after)
|
| - {
|
| - if (output_dependence (dest, x))
|
| - return true;
|
| - }
|
| - else
|
| - {
|
| - if (output_dependence (x, dest))
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (find_loads (pat, x, after))
|
| - return true;
|
| -
|
| - return false;
|
| -}
|
| -
|
| -/* Check if INSN kills the store pattern X (is aliased with it).
|
| - AFTER is true if we are checking the case when store X occurs
|
| - after the insn. Return true if it does. */
|
| +/* Return true if the graph is too expensive to optimize. PASS is the
|
| + optimization about to be performed. */
|
|
|
| static bool
|
| -store_killed_in_insn (const_rtx x, const_rtx x_regs, const_rtx insn, int after)
|
| +is_too_expensive (const char *pass)
|
| {
|
| - const_rtx reg, base, note, pat;
|
| -
|
| - if (!INSN_P (insn))
|
| - return false;
|
| + /* Trying to perform global optimizations on flow graphs which have
|
| + a high connectivity will take a long time and is unlikely to be
|
| + particularly useful.
|
|
|
| - if (CALL_P (insn))
|
| + In normal circumstances a cfg should have about twice as many
|
| + edges as blocks. But we do not want to punish small functions
|
| + which have a couple switch statements. Rather than simply
|
| + threshold the number of blocks, uses something with a more
|
| + graceful degradation. */
|
| + if (n_edges > 20000 + n_basic_blocks * 4)
|
| {
|
| - /* A normal or pure call might read from pattern,
|
| - but a const call will not. */
|
| - if (!RTL_CONST_CALL_P (insn))
|
| - return true;
|
| -
|
| - /* But even a const call reads its parameters. Check whether the
|
| - base of some of registers used in mem is stack pointer. */
|
| - for (reg = x_regs; reg; reg = XEXP (reg, 1))
|
| - {
|
| - base = find_base_term (XEXP (reg, 0));
|
| - if (!base
|
| - || (GET_CODE (base) == ADDRESS
|
| - && GET_MODE (base) == Pmode
|
| - && XEXP (base, 0) == stack_pointer_rtx))
|
| - return true;
|
| - }
|
| + warning (OPT_Wdisabled_optimization,
|
| + "%s: %d basic blocks and %d edges/basic block",
|
| + pass, n_basic_blocks, n_edges / n_basic_blocks);
|
|
|
| - return false;
|
| + return true;
|
| }
|
|
|
| - pat = PATTERN (insn);
|
| - if (GET_CODE (pat) == SET)
|
| - {
|
| - if (store_killed_in_pat (x, pat, after))
|
| - return true;
|
| - }
|
| - else if (GET_CODE (pat) == PARALLEL)
|
| + /* If allocating memory for the cprop bitmap would take up too much
|
| + storage it's better just to disable the optimization. */
|
| + if ((n_basic_blocks
|
| + * SBITMAP_SET_SIZE (max_reg_num ())
|
| + * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
|
| {
|
| - int i;
|
| -
|
| - for (i = 0; i < XVECLEN (pat, 0); i++)
|
| - if (store_killed_in_pat (x, XVECEXP (pat, 0, i), after))
|
| - return true;
|
| - }
|
| - else if (find_loads (PATTERN (insn), x, after))
|
| - return true;
|
| -
|
| - /* If this insn has a REG_EQUAL or REG_EQUIV note referencing a memory
|
| - location aliased with X, then this insn kills X. */
|
| - note = find_reg_equal_equiv_note (insn);
|
| - if (! note)
|
| - return false;
|
| - note = XEXP (note, 0);
|
| -
|
| - /* However, if the note represents a must alias rather than a may
|
| - alias relationship, then it does not kill X. */
|
| - if (expr_equiv_p (note, x))
|
| - return false;
|
| -
|
| - /* See if there are any aliased loads in the note. */
|
| - return find_loads (note, x, after);
|
| -}
|
| -
|
| -/* Returns true if the expression X is loaded or clobbered on or after INSN
|
| - within basic block BB. REGS_SET_AFTER is bitmap of registers set in
|
| - or after the insn. X_REGS is list of registers mentioned in X. If the store
|
| - is killed, return the last insn in that it occurs in FAIL_INSN. */
|
| -
|
| -static bool
|
| -store_killed_after (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
|
| - int *regs_set_after, rtx *fail_insn)
|
| -{
|
| - rtx last = BB_END (bb), act;
|
| + warning (OPT_Wdisabled_optimization,
|
| + "%s: %d basic blocks and %d registers",
|
| + pass, n_basic_blocks, max_reg_num ());
|
|
|
| - if (!store_ops_ok (x_regs, regs_set_after))
|
| - {
|
| - /* We do not know where it will happen. */
|
| - if (fail_insn)
|
| - *fail_insn = NULL_RTX;
|
| return true;
|
| }
|
|
|
| - /* Scan from the end, so that fail_insn is determined correctly. */
|
| - for (act = last; act != PREV_INSN (insn); act = PREV_INSN (act))
|
| - if (store_killed_in_insn (x, x_regs, act, false))
|
| - {
|
| - if (fail_insn)
|
| - *fail_insn = act;
|
| - return true;
|
| - }
|
| -
|
| return false;
|
| }
|
|
|
| -/* Returns true if the expression X is loaded or clobbered on or before INSN
|
| - within basic block BB. X_REGS is list of registers mentioned in X.
|
| - REGS_SET_BEFORE is bitmap of registers set before or in this insn. */
|
| -static bool
|
| -store_killed_before (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
|
| - int *regs_set_before)
|
| -{
|
| - rtx first = BB_HEAD (bb);
|
| -
|
| - if (!store_ops_ok (x_regs, regs_set_before))
|
| - return true;
|
| -
|
| - for ( ; insn != PREV_INSN (first); insn = PREV_INSN (insn))
|
| - if (store_killed_in_insn (x, x_regs, insn, true))
|
| - return true;
|
| -
|
| - return false;
|
| -}
|
| -
|
| -/* Fill in available, anticipatable, transparent and kill vectors in
|
| - STORE_DATA, based on lists of available and anticipatable stores. */
|
| -static void
|
| -build_store_vectors (void)
|
| -{
|
| - basic_block bb;
|
| - int *regs_set_in_block;
|
| - rtx insn, st;
|
| - struct ls_expr * ptr;
|
| - unsigned regno;
|
| -
|
| - /* Build the gen_vector. This is any store in the table which is not killed
|
| - by aliasing later in its block. */
|
| - ae_gen = sbitmap_vector_alloc (last_basic_block, num_stores);
|
| - sbitmap_vector_zero (ae_gen, last_basic_block);
|
| -
|
| - st_antloc = sbitmap_vector_alloc (last_basic_block, num_stores);
|
| - sbitmap_vector_zero (st_antloc, last_basic_block);
|
| -
|
| - for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
|
| - {
|
| - for (st = AVAIL_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
|
| - {
|
| - insn = XEXP (st, 0);
|
| - bb = BLOCK_FOR_INSN (insn);
|
| -
|
| - /* If we've already seen an available expression in this block,
|
| - we can delete this one (It occurs earlier in the block). We'll
|
| - copy the SRC expression to an unused register in case there
|
| - are any side effects. */
|
| - if (TEST_BIT (ae_gen[bb->index], ptr->index))
|
| - {
|
| - rtx r = gen_reg_rtx_and_attrs (ptr->pattern);
|
| - if (dump_file)
|
| - fprintf (dump_file, "Removing redundant store:\n");
|
| - replace_store_insn (r, XEXP (st, 0), bb, ptr);
|
| - continue;
|
| - }
|
| - SET_BIT (ae_gen[bb->index], ptr->index);
|
| - }
|
| -
|
| - for (st = ANTIC_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
|
| - {
|
| - insn = XEXP (st, 0);
|
| - bb = BLOCK_FOR_INSN (insn);
|
| - SET_BIT (st_antloc[bb->index], ptr->index);
|
| - }
|
| - }
|
| -
|
| - ae_kill = sbitmap_vector_alloc (last_basic_block, num_stores);
|
| - sbitmap_vector_zero (ae_kill, last_basic_block);
|
| -
|
| - transp = sbitmap_vector_alloc (last_basic_block, num_stores);
|
| - sbitmap_vector_zero (transp, last_basic_block);
|
| - regs_set_in_block = XNEWVEC (int, max_gcse_regno);
|
| -
|
| - FOR_EACH_BB (bb)
|
| - {
|
| - for (regno = 0; regno < max_gcse_regno; regno++)
|
| - regs_set_in_block[regno] = TEST_BIT (reg_set_in_block[bb->index], regno);
|
| -
|
| - for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
|
| - {
|
| - if (store_killed_after (ptr->pattern, ptr->pattern_regs, BB_HEAD (bb),
|
| - bb, regs_set_in_block, NULL))
|
| - {
|
| - /* It should not be necessary to consider the expression
|
| - killed if it is both anticipatable and available. */
|
| - if (!TEST_BIT (st_antloc[bb->index], ptr->index)
|
| - || !TEST_BIT (ae_gen[bb->index], ptr->index))
|
| - SET_BIT (ae_kill[bb->index], ptr->index);
|
| - }
|
| - else
|
| - SET_BIT (transp[bb->index], ptr->index);
|
| - }
|
| - }
|
| -
|
| - free (regs_set_in_block);
|
| -
|
| - if (dump_file)
|
| - {
|
| - dump_sbitmap_vector (dump_file, "st_antloc", "", st_antloc, last_basic_block);
|
| - dump_sbitmap_vector (dump_file, "st_kill", "", ae_kill, last_basic_block);
|
| - dump_sbitmap_vector (dump_file, "Transpt", "", transp, last_basic_block);
|
| - dump_sbitmap_vector (dump_file, "st_avloc", "", ae_gen, last_basic_block);
|
| - }
|
| -}
|
| -
|
| -/* Insert an instruction at the beginning of a basic block, and update
|
| - the BB_HEAD if needed. */
|
| -
|
| -static void
|
| -insert_insn_start_basic_block (rtx insn, basic_block bb)
|
| -{
|
| - /* Insert at start of successor block. */
|
| - rtx prev = PREV_INSN (BB_HEAD (bb));
|
| - rtx before = BB_HEAD (bb);
|
| - while (before != 0)
|
| - {
|
| - if (! LABEL_P (before)
|
| - && !NOTE_INSN_BASIC_BLOCK_P (before))
|
| - break;
|
| - prev = before;
|
| - if (prev == BB_END (bb))
|
| - break;
|
| - before = NEXT_INSN (before);
|
| - }
|
| -
|
| - insn = emit_insn_after_noloc (insn, prev, bb);
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "STORE_MOTION insert store at start of BB %d:\n",
|
| - bb->index);
|
| - print_inline_rtx (dump_file, insn, 6);
|
| - fprintf (dump_file, "\n");
|
| - }
|
| -}
|
| -
|
| -/* This routine will insert a store on an edge. EXPR is the ldst entry for
|
| - the memory reference, and E is the edge to insert it on. Returns nonzero
|
| - if an edge insertion was performed. */
|
| +
|
| +/* Main function for the CPROP pass. */
|
|
|
| static int
|
| -insert_store (struct ls_expr * expr, edge e)
|
| +one_cprop_pass (void)
|
| {
|
| - rtx reg, insn;
|
| - basic_block bb;
|
| - edge tmp;
|
| - edge_iterator ei;
|
| -
|
| - /* We did all the deleted before this insert, so if we didn't delete a
|
| - store, then we haven't set the reaching reg yet either. */
|
| - if (expr->reaching_reg == NULL_RTX)
|
| - return 0;
|
| + int changed = 0;
|
|
|
| - if (e->flags & EDGE_FAKE)
|
| + /* Return if there's nothing to do, or it is too expensive. */
|
| + if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|
| + || is_too_expensive (_ ("const/copy propagation disabled")))
|
| return 0;
|
|
|
| - reg = expr->reaching_reg;
|
| - insn = gen_move_insn (copy_rtx (expr->pattern), reg);
|
| -
|
| - /* If we are inserting this expression on ALL predecessor edges of a BB,
|
| - insert it at the start of the BB, and reset the insert bits on the other
|
| - edges so we don't try to insert it on the other edges. */
|
| - bb = e->dest;
|
| - FOR_EACH_EDGE (tmp, ei, e->dest->preds)
|
| - if (!(tmp->flags & EDGE_FAKE))
|
| - {
|
| - int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
|
| -
|
| - gcc_assert (index != EDGE_INDEX_NO_EDGE);
|
| - if (! TEST_BIT (pre_insert_map[index], expr->index))
|
| - break;
|
| - }
|
| + global_const_prop_count = local_const_prop_count = 0;
|
| + global_copy_prop_count = local_copy_prop_count = 0;
|
|
|
| - /* If tmp is NULL, we found an insertion on every edge, blank the
|
| - insertion vector for these edges, and insert at the start of the BB. */
|
| - if (!tmp && bb != EXIT_BLOCK_PTR)
|
| - {
|
| - FOR_EACH_EDGE (tmp, ei, e->dest->preds)
|
| - {
|
| - int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
|
| - RESET_BIT (pre_insert_map[index], expr->index);
|
| - }
|
| - insert_insn_start_basic_block (insn, bb);
|
| - return 0;
|
| - }
|
| + bytes_used = 0;
|
| + gcc_obstack_init (&gcse_obstack);
|
| + alloc_gcse_mem ();
|
|
|
| - /* We can't put stores in the front of blocks pointed to by abnormal
|
| - edges since that may put a store where one didn't used to be. */
|
| - gcc_assert (!(e->flags & EDGE_ABNORMAL));
|
| + /* Do a local const/copy propagation pass first. The global pass
|
| + only handles global opportunities.
|
| + If the local pass changes something, remove any unreachable blocks
|
| + because the CPROP global dataflow analysis may get into infinite
|
| + loops for CFGs with unreachable blocks.
|
|
|
| - insert_insn_on_edge (insn, e);
|
| + FIXME: This local pass should not be necessary after CSE (but for
|
| + some reason it still is). It is also (proven) not necessary
|
| + to run the local pass right after FWPWOP.
|
|
|
| - if (dump_file)
|
| + FIXME: The global analysis would not get into infinite loops if it
|
| + would use the DF solver (via df_simple_dataflow) instead of
|
| + the solver implemented in this file. */
|
| + if (local_cprop_pass ())
|
| {
|
| - fprintf (dump_file, "STORE_MOTION insert insn on edge (%d, %d):\n",
|
| - e->src->index, e->dest->index);
|
| - print_inline_rtx (dump_file, insn, 6);
|
| - fprintf (dump_file, "\n");
|
| + delete_unreachable_blocks ();
|
| + df_analyze ();
|
| }
|
|
|
| - return 1;
|
| -}
|
| -
|
| -/* Remove any REG_EQUAL or REG_EQUIV notes containing a reference to the
|
| - memory location in SMEXPR set in basic block BB.
|
| -
|
| - This could be rather expensive. */
|
| -
|
| -static void
|
| -remove_reachable_equiv_notes (basic_block bb, struct ls_expr *smexpr)
|
| -{
|
| - edge_iterator *stack, ei;
|
| - int sp;
|
| - edge act;
|
| - sbitmap visited = sbitmap_alloc (last_basic_block);
|
| - rtx last, insn, note;
|
| - rtx mem = smexpr->pattern;
|
| + /* Determine implicit sets. */
|
| + implicit_sets = XCNEWVEC (rtx, last_basic_block);
|
| + find_implicit_sets ();
|
|
|
| - stack = XNEWVEC (edge_iterator, n_basic_blocks);
|
| - sp = 0;
|
| - ei = ei_start (bb->succs);
|
| + alloc_hash_table (&set_hash_table, 1);
|
| + compute_hash_table (&set_hash_table);
|
|
|
| - sbitmap_zero (visited);
|
| + /* Free implicit_sets before peak usage. */
|
| + free (implicit_sets);
|
| + implicit_sets = NULL;
|
|
|
| - act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
|
| - while (1)
|
| + if (dump_file)
|
| + dump_hash_table (dump_file, "SET", &set_hash_table);
|
| + if (set_hash_table.n_elems > 0)
|
| {
|
| - if (!act)
|
| - {
|
| - if (!sp)
|
| - {
|
| - free (stack);
|
| - sbitmap_free (visited);
|
| - return;
|
| - }
|
| - act = ei_edge (stack[--sp]);
|
| - }
|
| - bb = act->dest;
|
| + basic_block bb;
|
| + rtx insn;
|
|
|
| - if (bb == EXIT_BLOCK_PTR
|
| - || TEST_BIT (visited, bb->index))
|
| - {
|
| - if (!ei_end_p (ei))
|
| - ei_next (&ei);
|
| - act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
|
| - continue;
|
| - }
|
| - SET_BIT (visited, bb->index);
|
| + alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
|
| + compute_cprop_data ();
|
|
|
| - if (TEST_BIT (st_antloc[bb->index], smexpr->index))
|
| + FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
|
| {
|
| - for (last = ANTIC_STORE_LIST (smexpr);
|
| - BLOCK_FOR_INSN (XEXP (last, 0)) != bb;
|
| - last = XEXP (last, 1))
|
| - continue;
|
| - last = XEXP (last, 0);
|
| - }
|
| - else
|
| - last = NEXT_INSN (BB_END (bb));
|
| + /* Reset tables used to keep track of what's still valid [since
|
| + the start of the block]. */
|
| + reset_opr_set_tables ();
|
|
|
| - for (insn = BB_HEAD (bb); insn != last; insn = NEXT_INSN (insn))
|
| - if (INSN_P (insn))
|
| - {
|
| - note = find_reg_equal_equiv_note (insn);
|
| - if (!note || !expr_equiv_p (XEXP (note, 0), mem))
|
| - continue;
|
| -
|
| - if (dump_file)
|
| - fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
|
| - INSN_UID (insn));
|
| - remove_note (insn, note);
|
| - }
|
| -
|
| - if (!ei_end_p (ei))
|
| - ei_next (&ei);
|
| - act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
|
| -
|
| - if (EDGE_COUNT (bb->succs) > 0)
|
| - {
|
| - if (act)
|
| - stack[sp++] = ei;
|
| - ei = ei_start (bb->succs);
|
| - act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
|
| + FOR_BB_INSNS (bb, insn)
|
| + if (INSN_P (insn))
|
| + {
|
| + changed |= cprop_insn (insn);
|
| +
|
| + /* Keep track of everything modified by this insn. */
|
| + /* ??? Need to be careful w.r.t. mods done to INSN.
|
| + Don't call mark_oprs_set if we turned the
|
| + insn into a NOTE. */
|
| + if (! NOTE_P (insn))
|
| + mark_oprs_set (insn);
|
| + }
|
| }
|
| - }
|
| -}
|
| -
|
| -/* This routine will replace a store with a SET to a specified register. */
|
| -
|
| -static void
|
| -replace_store_insn (rtx reg, rtx del, basic_block bb, struct ls_expr *smexpr)
|
| -{
|
| - rtx insn, mem, note, set, ptr;
|
| -
|
| - mem = smexpr->pattern;
|
| - insn = gen_move_insn (reg, SET_SRC (single_set (del)));
|
| -
|
| - for (ptr = ANTIC_STORE_LIST (smexpr); ptr; ptr = XEXP (ptr, 1))
|
| - if (XEXP (ptr, 0) == del)
|
| - {
|
| - XEXP (ptr, 0) = insn;
|
| - break;
|
| - }
|
|
|
| - /* Move the notes from the deleted insn to its replacement. */
|
| - REG_NOTES (insn) = REG_NOTES (del);
|
| + changed |= bypass_conditional_jumps ();
|
| + free_cprop_mem ();
|
| + }
|
|
|
| - /* Emit the insn AFTER all the notes are transferred.
|
| - This is cheaper since we avoid df rescanning for the note change. */
|
| - insn = emit_insn_after (insn, del);
|
| + free_hash_table (&set_hash_table);
|
| + free_gcse_mem ();
|
| + obstack_free (&gcse_obstack, NULL);
|
|
|
| if (dump_file)
|
| {
|
| - fprintf (dump_file,
|
| - "STORE_MOTION delete insn in BB %d:\n ", bb->index);
|
| - print_inline_rtx (dump_file, del, 6);
|
| - fprintf (dump_file, "\nSTORE MOTION replaced with insn:\n ");
|
| - print_inline_rtx (dump_file, insn, 6);
|
| - fprintf (dump_file, "\n");
|
| + fprintf (dump_file, "CPROP of %s, %d basic blocks, %d bytes needed, ",
|
| + current_function_name (), n_basic_blocks, bytes_used);
|
| + fprintf (dump_file, "%d local const props, %d local copy props, ",
|
| + local_const_prop_count, local_copy_prop_count);
|
| + fprintf (dump_file, "%d global const props, %d global copy props\n\n",
|
| + global_const_prop_count, global_copy_prop_count);
|
| }
|
|
|
| - delete_insn (del);
|
| -
|
| - /* Now we must handle REG_EQUAL notes whose contents is equal to the mem;
|
| - they are no longer accurate provided that they are reached by this
|
| - definition, so drop them. */
|
| - for (; insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn))
|
| - if (INSN_P (insn))
|
| - {
|
| - set = single_set (insn);
|
| - if (!set)
|
| - continue;
|
| - if (expr_equiv_p (SET_DEST (set), mem))
|
| - return;
|
| - note = find_reg_equal_equiv_note (insn);
|
| - if (!note || !expr_equiv_p (XEXP (note, 0), mem))
|
| - continue;
|
| -
|
| - if (dump_file)
|
| - fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
|
| - INSN_UID (insn));
|
| - remove_note (insn, note);
|
| - }
|
| - remove_reachable_equiv_notes (bb, smexpr);
|
| + return changed;
|
| }
|
|
|
| +
|
| +/* All the passes implemented in this file. Each pass has its
|
| + own gate and execute function, and at the end of the file a
|
| + pass definition for passes.c.
|
|
|
| -/* Delete a store, but copy the value that would have been stored into
|
| - the reaching_reg for later storing. */
|
| + We do not construct an accurate cfg in functions which call
|
| + setjmp, so none of these passes runs if the function calls
|
| + setjmp.
|
| + FIXME: Should just handle setjmp via REG_SETJMP notes. */
|
|
|
| -static void
|
| -delete_store (struct ls_expr * expr, basic_block bb)
|
| +static bool
|
| +gate_rtl_cprop (void)
|
| {
|
| - rtx reg, i, del;
|
| -
|
| - if (expr->reaching_reg == NULL_RTX)
|
| - expr->reaching_reg = gen_reg_rtx_and_attrs (expr->pattern);
|
| -
|
| - reg = expr->reaching_reg;
|
| -
|
| - for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
|
| - {
|
| - del = XEXP (i, 0);
|
| - if (BLOCK_FOR_INSN (del) == bb)
|
| - {
|
| - /* We know there is only one since we deleted redundant
|
| - ones during the available computation. */
|
| - replace_store_insn (reg, del, bb, expr);
|
| - break;
|
| - }
|
| - }
|
| + return optimize > 0 && flag_gcse
|
| + && !cfun->calls_setjmp
|
| + && dbg_cnt (cprop);
|
| }
|
|
|
| -/* Free memory used by store motion. */
|
| -
|
| -static void
|
| -free_store_memory (void)
|
| +static unsigned int
|
| +execute_rtl_cprop (void)
|
| {
|
| - free_ldst_mems ();
|
| -
|
| - if (ae_gen)
|
| - sbitmap_vector_free (ae_gen);
|
| - if (ae_kill)
|
| - sbitmap_vector_free (ae_kill);
|
| - if (transp)
|
| - sbitmap_vector_free (transp);
|
| - if (st_antloc)
|
| - sbitmap_vector_free (st_antloc);
|
| - if (pre_insert_map)
|
| - sbitmap_vector_free (pre_insert_map);
|
| - if (pre_delete_map)
|
| - sbitmap_vector_free (pre_delete_map);
|
| - if (reg_set_in_block)
|
| - sbitmap_vector_free (reg_set_in_block);
|
| -
|
| - ae_gen = ae_kill = transp = st_antloc = NULL;
|
| - pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
|
| + delete_unreachable_blocks ();
|
| + df_set_flags (DF_LR_RUN_DCE);
|
| + df_analyze ();
|
| + flag_rerun_cse_after_global_opts |= one_cprop_pass ();
|
| + return 0;
|
| }
|
|
|
| -/* Perform store motion. Much like gcse, except we move expressions the
|
| - other way by looking at the flowgraph in reverse. */
|
| -
|
| -static void
|
| -store_motion (void)
|
| +static bool
|
| +gate_rtl_pre (void)
|
| {
|
| - basic_block bb;
|
| - int x;
|
| - struct ls_expr * ptr;
|
| - int update_flow = 0;
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "before store motion\n");
|
| - print_rtl (dump_file, get_insns ());
|
| - }
|
| -
|
| - init_alias_analysis ();
|
| -
|
| - /* Find all the available and anticipatable stores. */
|
| - num_stores = compute_store_table ();
|
| - if (num_stores == 0)
|
| - {
|
| - htab_delete (pre_ldst_table);
|
| - pre_ldst_table = NULL;
|
| - sbitmap_vector_free (reg_set_in_block);
|
| - end_alias_analysis ();
|
| - return;
|
| - }
|
| -
|
| - /* Now compute kill & transp vectors. */
|
| - build_store_vectors ();
|
| - add_noreturn_fake_exit_edges ();
|
| - connect_infinite_loops_to_exit ();
|
| -
|
| - edge_list = pre_edge_rev_lcm (num_stores, transp, ae_gen,
|
| - st_antloc, ae_kill, &pre_insert_map,
|
| - &pre_delete_map);
|
| -
|
| - /* Now we want to insert the new stores which are going to be needed. */
|
| - for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
|
| - {
|
| - /* If any of the edges we have above are abnormal, we can't move this
|
| - store. */
|
| - for (x = NUM_EDGES (edge_list) - 1; x >= 0; x--)
|
| - if (TEST_BIT (pre_insert_map[x], ptr->index)
|
| - && (INDEX_EDGE (edge_list, x)->flags & EDGE_ABNORMAL))
|
| - break;
|
| -
|
| - if (x >= 0)
|
| - {
|
| - if (dump_file != NULL)
|
| - fprintf (dump_file,
|
| - "Can't replace store %d: abnormal edge from %d to %d\n",
|
| - ptr->index, INDEX_EDGE (edge_list, x)->src->index,
|
| - INDEX_EDGE (edge_list, x)->dest->index);
|
| - continue;
|
| - }
|
| -
|
| - /* Now we want to insert the new stores which are going to be needed. */
|
| -
|
| - FOR_EACH_BB (bb)
|
| - if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
|
| - delete_store (ptr, bb);
|
| -
|
| - for (x = 0; x < NUM_EDGES (edge_list); x++)
|
| - if (TEST_BIT (pre_insert_map[x], ptr->index))
|
| - update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
|
| - }
|
| -
|
| - if (update_flow)
|
| - commit_edge_insertions ();
|
| -
|
| - free_store_memory ();
|
| - free_edge_list (edge_list);
|
| - remove_fake_exit_edges ();
|
| - end_alias_analysis ();
|
| + return optimize > 0 && flag_gcse
|
| + && !cfun->calls_setjmp
|
| + && optimize_function_for_speed_p (cfun)
|
| + && dbg_cnt (pre);
|
| }
|
|
|
| -
|
| -/* Entry point for jump bypassing optimization pass. */
|
| -
|
| -static int
|
| -bypass_jumps (void)
|
| +static unsigned int
|
| +execute_rtl_pre (void)
|
| {
|
| - int changed;
|
| -
|
| - /* We do not construct an accurate cfg in functions which call
|
| - setjmp, so just punt to be safe. */
|
| - if (cfun->calls_setjmp)
|
| - return 0;
|
| -
|
| - /* Identify the basic block information for this function, including
|
| - successors and predecessors. */
|
| - max_gcse_regno = max_reg_num ();
|
| -
|
| - if (dump_file)
|
| - dump_flow_info (dump_file, dump_flags);
|
| -
|
| - /* Return if there's nothing to do, or it is too expensive. */
|
| - if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|
| - || is_too_expensive (_ ("jump bypassing disabled")))
|
| - return 0;
|
| -
|
| - gcc_obstack_init (&gcse_obstack);
|
| - bytes_used = 0;
|
| -
|
| - /* We need alias. */
|
| - init_alias_analysis ();
|
| -
|
| - /* Record where pseudo-registers are set. This data is kept accurate
|
| - during each pass. ??? We could also record hard-reg information here
|
| - [since it's unchanging], however it is currently done during hash table
|
| - computation.
|
| -
|
| - It may be tempting to compute MEM set information here too, but MEM sets
|
| - will be subject to code motion one day and thus we need to compute
|
| - information about memory sets when we build the hash tables. */
|
| -
|
| - alloc_reg_set_mem (max_gcse_regno);
|
| - compute_sets ();
|
| -
|
| - max_gcse_regno = max_reg_num ();
|
| - alloc_gcse_mem ();
|
| - changed = one_cprop_pass (MAX_GCSE_PASSES + 2, true, true);
|
| - free_gcse_mem ();
|
| -
|
| - if (dump_file)
|
| - {
|
| - fprintf (dump_file, "BYPASS of %s: %d basic blocks, ",
|
| - current_function_name (), n_basic_blocks);
|
| - fprintf (dump_file, "%d bytes\n\n", bytes_used);
|
| - }
|
| -
|
| - obstack_free (&gcse_obstack, NULL);
|
| - free_reg_set_mem ();
|
| -
|
| - /* We are finished with alias. */
|
| - end_alias_analysis ();
|
| -
|
| - return changed;
|
| + delete_unreachable_blocks ();
|
| + df_analyze ();
|
| + flag_rerun_cse_after_global_opts |= one_pre_gcse_pass ();
|
| + return 0;
|
| }
|
|
|
| -/* Return true if the graph is too expensive to optimize. PASS is the
|
| - optimization about to be performed. */
|
| -
|
| -static bool
|
| -is_too_expensive (const char *pass)
|
| -{
|
| - /* Trying to perform global optimizations on flow graphs which have
|
| - a high connectivity will take a long time and is unlikely to be
|
| - particularly useful.
|
| -
|
| - In normal circumstances a cfg should have about twice as many
|
| - edges as blocks. But we do not want to punish small functions
|
| - which have a couple switch statements. Rather than simply
|
| - threshold the number of blocks, uses something with a more
|
| - graceful degradation. */
|
| - if (n_edges > 20000 + n_basic_blocks * 4)
|
| - {
|
| - warning (OPT_Wdisabled_optimization,
|
| - "%s: %d basic blocks and %d edges/basic block",
|
| - pass, n_basic_blocks, n_edges / n_basic_blocks);
|
| -
|
| - return true;
|
| - }
|
| -
|
| - /* If allocating memory for the cprop bitmap would take up too much
|
| - storage it's better just to disable the optimization. */
|
| - if ((n_basic_blocks
|
| - * SBITMAP_SET_SIZE (max_reg_num ())
|
| - * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
|
| - {
|
| - warning (OPT_Wdisabled_optimization,
|
| - "%s: %d basic blocks and %d registers",
|
| - pass, n_basic_blocks, max_reg_num ());
|
| -
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| static bool
|
| -gate_handle_jump_bypass (void)
|
| +gate_rtl_hoist (void)
|
| {
|
| return optimize > 0 && flag_gcse
|
| - && dbg_cnt (jump_bypass);
|
| + && !cfun->calls_setjmp
|
| + /* It does not make sense to run code hoisting unless we are optimizing
|
| + for code size -- it rarely makes programs faster, and can make then
|
| + bigger if we did PRE (when optimizing for space, we don't run PRE). */
|
| + && optimize_function_for_size_p (cfun)
|
| + && dbg_cnt (hoist);
|
| }
|
|
|
| -/* Perform jump bypassing and control flow optimizations. */
|
| static unsigned int
|
| -rest_of_handle_jump_bypass (void)
|
| +execute_rtl_hoist (void)
|
| {
|
| delete_unreachable_blocks ();
|
| - if (bypass_jumps ())
|
| - {
|
| - delete_trivially_dead_insns (get_insns (), max_reg_num ());
|
| - rebuild_jump_labels (get_insns ());
|
| - cleanup_cfg (0);
|
| - }
|
| + df_analyze ();
|
| + flag_rerun_cse_after_global_opts |= one_code_hoisting_pass ();
|
| return 0;
|
| }
|
|
|
| -struct rtl_opt_pass pass_jump_bypass =
|
| +struct rtl_opt_pass pass_rtl_cprop =
|
| {
|
| {
|
| RTL_PASS,
|
| - "bypass", /* name */
|
| - gate_handle_jump_bypass, /* gate */
|
| - rest_of_handle_jump_bypass, /* execute */
|
| + "cprop", /* name */
|
| + gate_rtl_cprop, /* gate */
|
| + execute_rtl_cprop, /* execute */
|
| NULL, /* sub */
|
| NULL, /* next */
|
| 0, /* static_pass_number */
|
| - TV_BYPASS, /* tv_id */
|
| - 0, /* properties_required */
|
| + TV_CPROP, /* tv_id */
|
| + PROP_cfglayout, /* properties_required */
|
| 0, /* properties_provided */
|
| 0, /* properties_destroyed */
|
| 0, /* todo_flags_start */
|
| + TODO_df_finish | TODO_verify_rtl_sharing |
|
| TODO_dump_func |
|
| - TODO_ggc_collect | TODO_verify_flow /* todo_flags_finish */
|
| + TODO_verify_flow | TODO_ggc_collect /* todo_flags_finish */
|
| }
|
| };
|
|
|
| -
|
| -static bool
|
| -gate_handle_gcse (void)
|
| -{
|
| - return optimize > 0 && flag_gcse
|
| - && dbg_cnt (gcse);
|
| -}
|
| -
|
| -
|
| -static unsigned int
|
| -rest_of_handle_gcse (void)
|
| +struct rtl_opt_pass pass_rtl_pre =
|
| {
|
| - int save_csb, save_cfj;
|
| - int tem2 = 0, tem;
|
| - tem = gcse_main (get_insns ());
|
| - delete_trivially_dead_insns (get_insns (), max_reg_num ());
|
| - rebuild_jump_labels (get_insns ());
|
| - save_csb = flag_cse_skip_blocks;
|
| - save_cfj = flag_cse_follow_jumps;
|
| - flag_cse_skip_blocks = flag_cse_follow_jumps = 0;
|
| -
|
| - /* If -fexpensive-optimizations, re-run CSE to clean up things done
|
| - by gcse. */
|
| - if (flag_expensive_optimizations)
|
| - {
|
| - timevar_push (TV_CSE);
|
| - tem2 = cse_main (get_insns (), max_reg_num ());
|
| - df_finish_pass (false);
|
| - purge_all_dead_edges ();
|
| - delete_trivially_dead_insns (get_insns (), max_reg_num ());
|
| - timevar_pop (TV_CSE);
|
| - cse_not_expected = !flag_rerun_cse_after_loop;
|
| - }
|
| -
|
| - /* If gcse or cse altered any jumps, rerun jump optimizations to clean
|
| - things up. */
|
| - if (tem || tem2 == 2)
|
| - {
|
| - timevar_push (TV_JUMP);
|
| - rebuild_jump_labels (get_insns ());
|
| - cleanup_cfg (0);
|
| - timevar_pop (TV_JUMP);
|
| - }
|
| - else if (tem2 == 1)
|
| - cleanup_cfg (0);
|
| -
|
| - flag_cse_skip_blocks = save_csb;
|
| - flag_cse_follow_jumps = save_cfj;
|
| - return 0;
|
| -}
|
| + {
|
| + RTL_PASS,
|
| + "rtl pre", /* name */
|
| + gate_rtl_pre, /* gate */
|
| + execute_rtl_pre, /* execute */
|
| + NULL, /* sub */
|
| + NULL, /* next */
|
| + 0, /* static_pass_number */
|
| + TV_PRE, /* tv_id */
|
| + PROP_cfglayout, /* properties_required */
|
| + 0, /* properties_provided */
|
| + 0, /* properties_destroyed */
|
| + 0, /* todo_flags_start */
|
| + TODO_df_finish | TODO_verify_rtl_sharing |
|
| + TODO_dump_func |
|
| + TODO_verify_flow | TODO_ggc_collect /* todo_flags_finish */
|
| + }
|
| +};
|
|
|
| -struct rtl_opt_pass pass_gcse =
|
| +struct rtl_opt_pass pass_rtl_hoist =
|
| {
|
| {
|
| RTL_PASS,
|
| - "gcse1", /* name */
|
| - gate_handle_gcse, /* gate */
|
| - rest_of_handle_gcse, /* execute */
|
| + "hoist", /* name */
|
| + gate_rtl_hoist, /* gate */
|
| + execute_rtl_hoist, /* execute */
|
| NULL, /* sub */
|
| NULL, /* next */
|
| 0, /* static_pass_number */
|
| - TV_GCSE, /* tv_id */
|
| - 0, /* properties_required */
|
| + TV_HOIST, /* tv_id */
|
| + PROP_cfglayout, /* properties_required */
|
| 0, /* properties_provided */
|
| 0, /* properties_destroyed */
|
| 0, /* todo_flags_start */
|
| @@ -6702,5 +5168,4 @@ struct rtl_opt_pass pass_gcse =
|
| }
|
| };
|
|
|
| -
|
| #include "gt-gcse.h"
|
|
|