xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/postreload-gcse.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
11debfc3dSmrg /* Post reload partially redundant load elimination
2*8feb0f0bSmrg    Copyright (C) 2004-2020 Free Software Foundation, Inc.
31debfc3dSmrg 
41debfc3dSmrg This file is part of GCC.
51debfc3dSmrg 
61debfc3dSmrg GCC is free software; you can redistribute it and/or modify it under
71debfc3dSmrg the terms of the GNU General Public License as published by the Free
81debfc3dSmrg Software Foundation; either version 3, or (at your option) any later
91debfc3dSmrg version.
101debfc3dSmrg 
111debfc3dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
121debfc3dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
131debfc3dSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141debfc3dSmrg for more details.
151debfc3dSmrg 
161debfc3dSmrg You should have received a copy of the GNU General Public License
171debfc3dSmrg along with GCC; see the file COPYING3.  If not see
181debfc3dSmrg <http://www.gnu.org/licenses/>.  */
191debfc3dSmrg 
201debfc3dSmrg #include "config.h"
211debfc3dSmrg #include "system.h"
221debfc3dSmrg #include "coretypes.h"
231debfc3dSmrg #include "backend.h"
241debfc3dSmrg #include "target.h"
251debfc3dSmrg #include "rtl.h"
261debfc3dSmrg #include "tree.h"
271debfc3dSmrg #include "predict.h"
281debfc3dSmrg #include "df.h"
291debfc3dSmrg #include "memmodel.h"
301debfc3dSmrg #include "tm_p.h"
311debfc3dSmrg #include "insn-config.h"
321debfc3dSmrg #include "emit-rtl.h"
331debfc3dSmrg #include "recog.h"
341debfc3dSmrg 
351debfc3dSmrg #include "cfgrtl.h"
361debfc3dSmrg #include "profile.h"
371debfc3dSmrg #include "expr.h"
381debfc3dSmrg #include "tree-pass.h"
391debfc3dSmrg #include "dbgcnt.h"
40*8feb0f0bSmrg #include "intl.h"
411debfc3dSmrg #include "gcse-common.h"
42*8feb0f0bSmrg #include "gcse.h"
43*8feb0f0bSmrg #include "regs.h"
44*8feb0f0bSmrg #include "function-abi.h"
451debfc3dSmrg 
461debfc3dSmrg /* The following code implements gcse after reload, the purpose of this
471debfc3dSmrg    pass is to cleanup redundant loads generated by reload and other
481debfc3dSmrg    optimizations that come after gcse. It searches for simple inter-block
491debfc3dSmrg    redundancies and tries to eliminate them by adding moves and loads
501debfc3dSmrg    in cold places.
511debfc3dSmrg 
521debfc3dSmrg    Perform partially redundant load elimination, try to eliminate redundant
531debfc3dSmrg    loads created by the reload pass.  We try to look for full or partial
541debfc3dSmrg    redundant loads fed by one or more loads/stores in predecessor BBs,
551debfc3dSmrg    and try adding loads to make them fully redundant.  We also check if
561debfc3dSmrg    it's worth adding loads to be able to delete the redundant load.
571debfc3dSmrg 
581debfc3dSmrg    Algorithm:
591debfc3dSmrg    1. Build available expressions hash table:
601debfc3dSmrg        For each load/store instruction, if the loaded/stored memory didn't
611debfc3dSmrg        change until the end of the basic block add this memory expression to
621debfc3dSmrg        the hash table.
631debfc3dSmrg    2. Perform Redundancy elimination:
641debfc3dSmrg       For each load instruction do the following:
651debfc3dSmrg 	 perform partial redundancy elimination, check if it's worth adding
661debfc3dSmrg 	 loads to make the load fully redundant.  If so add loads and
671debfc3dSmrg 	 register copies and delete the load.
681debfc3dSmrg    3. Delete instructions made redundant in step 2.
691debfc3dSmrg 
701debfc3dSmrg    Future enhancement:
711debfc3dSmrg      If the loaded register is used/defined between load and some store,
721debfc3dSmrg      look for some other free register between load and all its stores,
731debfc3dSmrg      and replace the load with a copy from this register to the loaded
741debfc3dSmrg      register.
751debfc3dSmrg */
761debfc3dSmrg 
771debfc3dSmrg 
781debfc3dSmrg /* Keep statistics of this pass.  */
791debfc3dSmrg static struct
801debfc3dSmrg {
811debfc3dSmrg   int moves_inserted;
821debfc3dSmrg   int copies_inserted;
831debfc3dSmrg   int insns_deleted;
841debfc3dSmrg } stats;
851debfc3dSmrg 
861debfc3dSmrg /* We need to keep a hash table of expressions.  The table entries are of
871debfc3dSmrg    type 'struct expr', and for each expression there is a single linked
881debfc3dSmrg    list of occurrences.  */
891debfc3dSmrg 
901debfc3dSmrg /* Expression elements in the hash table.  */
911debfc3dSmrg struct expr
921debfc3dSmrg {
931debfc3dSmrg   /* The expression (SET_SRC for expressions, PATTERN for assignments).  */
941debfc3dSmrg   rtx expr;
951debfc3dSmrg 
961debfc3dSmrg   /* The same hash for this entry.  */
971debfc3dSmrg   hashval_t hash;
981debfc3dSmrg 
991debfc3dSmrg   /* Index in the transparent bitmaps.  */
1001debfc3dSmrg   unsigned int bitmap_index;
1011debfc3dSmrg 
1021debfc3dSmrg   /* List of available occurrence in basic blocks in the function.  */
1031debfc3dSmrg   struct occr *avail_occr;
1041debfc3dSmrg };
1051debfc3dSmrg 
1061debfc3dSmrg /* Hashtable helpers.  */
1071debfc3dSmrg 
1081debfc3dSmrg struct expr_hasher : nofree_ptr_hash <expr>
1091debfc3dSmrg {
1101debfc3dSmrg   static inline hashval_t hash (const expr *);
1111debfc3dSmrg   static inline bool equal (const expr *, const expr *);
1121debfc3dSmrg };
1131debfc3dSmrg 
1141debfc3dSmrg 
1151debfc3dSmrg /* Hash expression X.
1161debfc3dSmrg    DO_NOT_RECORD_P is a boolean indicating if a volatile operand is found
1171debfc3dSmrg    or if the expression contains something we don't want to insert in the
1181debfc3dSmrg    table.  */
1191debfc3dSmrg 
1201debfc3dSmrg static hashval_t
hash_expr(rtx x,int * do_not_record_p)1211debfc3dSmrg hash_expr (rtx x, int *do_not_record_p)
1221debfc3dSmrg {
1231debfc3dSmrg   *do_not_record_p = 0;
1241debfc3dSmrg   return hash_rtx (x, GET_MODE (x), do_not_record_p,
1251debfc3dSmrg 		   NULL,  /*have_reg_qty=*/false);
1261debfc3dSmrg }
1271debfc3dSmrg 
1281debfc3dSmrg /* Callback for hashtab.
1291debfc3dSmrg    Return the hash value for expression EXP.  We don't actually hash
1301debfc3dSmrg    here, we just return the cached hash value.  */
1311debfc3dSmrg 
1321debfc3dSmrg inline hashval_t
hash(const expr * exp)1331debfc3dSmrg expr_hasher::hash (const expr *exp)
1341debfc3dSmrg {
1351debfc3dSmrg   return exp->hash;
1361debfc3dSmrg }
1371debfc3dSmrg 
1381debfc3dSmrg /* Callback for hashtab.
1391debfc3dSmrg    Return nonzero if exp1 is equivalent to exp2.  */
1401debfc3dSmrg 
1411debfc3dSmrg inline bool
equal(const expr * exp1,const expr * exp2)1421debfc3dSmrg expr_hasher::equal (const expr *exp1, const expr *exp2)
1431debfc3dSmrg {
1441debfc3dSmrg   int equiv_p = exp_equiv_p (exp1->expr, exp2->expr, 0, true);
1451debfc3dSmrg 
1461debfc3dSmrg   gcc_assert (!equiv_p || exp1->hash == exp2->hash);
1471debfc3dSmrg   return equiv_p;
1481debfc3dSmrg }
1491debfc3dSmrg 
1501debfc3dSmrg /* The table itself.  */
1511debfc3dSmrg static hash_table<expr_hasher> *expr_table;
1521debfc3dSmrg 
1531debfc3dSmrg 
1541debfc3dSmrg static struct obstack expr_obstack;
1551debfc3dSmrg 
1561debfc3dSmrg /* Occurrence of an expression.
1571debfc3dSmrg    There is at most one occurrence per basic block.  If a pattern appears
1581debfc3dSmrg    more than once, the last appearance is used.  */
1591debfc3dSmrg 
1601debfc3dSmrg struct occr
1611debfc3dSmrg {
1621debfc3dSmrg   /* Next occurrence of this expression.  */
1631debfc3dSmrg   struct occr *next;
1641debfc3dSmrg   /* The insn that computes the expression.  */
1651debfc3dSmrg   rtx_insn *insn;
1661debfc3dSmrg   /* Nonzero if this [anticipatable] occurrence has been deleted.  */
1671debfc3dSmrg   char deleted_p;
1681debfc3dSmrg };
1691debfc3dSmrg 
1701debfc3dSmrg static struct obstack occr_obstack;
1711debfc3dSmrg 
1721debfc3dSmrg /* The following structure holds the information about the occurrences of
1731debfc3dSmrg    the redundant instructions.  */
1741debfc3dSmrg struct unoccr
1751debfc3dSmrg {
1761debfc3dSmrg   struct unoccr *next;
1771debfc3dSmrg   edge pred;
1781debfc3dSmrg   rtx_insn *insn;
1791debfc3dSmrg };
1801debfc3dSmrg 
1811debfc3dSmrg static struct obstack unoccr_obstack;
1821debfc3dSmrg 
1831debfc3dSmrg /* Array where each element is the CUID if the insn that last set the hard
1841debfc3dSmrg    register with the number of the element, since the start of the current
1851debfc3dSmrg    basic block.
1861debfc3dSmrg 
1871debfc3dSmrg    This array is used during the building of the hash table (step 1) to
1881debfc3dSmrg    determine if a reg is killed before the end of a basic block.
1891debfc3dSmrg 
1901debfc3dSmrg    It is also used when eliminating partial redundancies (step 2) to see
1911debfc3dSmrg    if a reg was modified since the start of a basic block.  */
1921debfc3dSmrg static int *reg_avail_info;
1931debfc3dSmrg 
1941debfc3dSmrg /* A list of insns that may modify memory within the current basic block.  */
1951debfc3dSmrg struct modifies_mem
1961debfc3dSmrg {
1971debfc3dSmrg   rtx_insn *insn;
1981debfc3dSmrg   struct modifies_mem *next;
1991debfc3dSmrg };
2001debfc3dSmrg static struct modifies_mem *modifies_mem_list;
2011debfc3dSmrg 
2021debfc3dSmrg /* The modifies_mem structs also go on an obstack, only this obstack is
2031debfc3dSmrg    freed each time after completing the analysis or transformations on
2041debfc3dSmrg    a basic block.  So we allocate a dummy modifies_mem_obstack_bottom
2051debfc3dSmrg    object on the obstack to keep track of the bottom of the obstack.  */
2061debfc3dSmrg static struct obstack modifies_mem_obstack;
2071debfc3dSmrg static struct modifies_mem  *modifies_mem_obstack_bottom;
2081debfc3dSmrg 
2091debfc3dSmrg /* Mapping of insn UIDs to CUIDs.
2101debfc3dSmrg    CUIDs are like UIDs except they increase monotonically in each basic
2111debfc3dSmrg    block, have no gaps, and only apply to real insns.  */
2121debfc3dSmrg static int *uid_cuid;
2131debfc3dSmrg #define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
2141debfc3dSmrg 
2151debfc3dSmrg /* Bitmap of blocks which have memory stores.  */
2161debfc3dSmrg static bitmap modify_mem_list_set;
2171debfc3dSmrg 
2181debfc3dSmrg /* Bitmap of blocks which have calls.  */
2191debfc3dSmrg static bitmap blocks_with_calls;
2201debfc3dSmrg 
2211debfc3dSmrg /* Vector indexed by block # with a list of all the insns that
2221debfc3dSmrg    modify memory within the block.  */
2231debfc3dSmrg static vec<rtx_insn *> *modify_mem_list;
2241debfc3dSmrg 
2251debfc3dSmrg /* Vector indexed by block # with a canonicalized list of insns
2261debfc3dSmrg    that modify memory in the block.  */
2271debfc3dSmrg static vec<modify_pair> *canon_modify_mem_list;
2281debfc3dSmrg 
2291debfc3dSmrg /* Vector of simple bitmaps indexed by block number.  Each component sbitmap
2301debfc3dSmrg    indicates which expressions are transparent through the block.  */
2311debfc3dSmrg static sbitmap *transp;
2321debfc3dSmrg 
2331debfc3dSmrg 
2341debfc3dSmrg /* Helpers for memory allocation/freeing.  */
2351debfc3dSmrg static void alloc_mem (void);
2361debfc3dSmrg static void free_mem (void);
2371debfc3dSmrg 
2381debfc3dSmrg /* Support for hash table construction and transformations.  */
2391debfc3dSmrg static bool oprs_unchanged_p (rtx, rtx_insn *, bool);
2401debfc3dSmrg static void record_last_reg_set_info (rtx_insn *, rtx);
2411debfc3dSmrg static void record_last_reg_set_info_regno (rtx_insn *, int);
2421debfc3dSmrg static void record_last_mem_set_info (rtx_insn *);
2431debfc3dSmrg static void record_last_set_info (rtx, const_rtx, void *);
2441debfc3dSmrg static void record_opr_changes (rtx_insn *);
2451debfc3dSmrg 
2461debfc3dSmrg static void find_mem_conflicts (rtx, const_rtx, void *);
2471debfc3dSmrg static int load_killed_in_block_p (int, rtx, bool);
2481debfc3dSmrg static void reset_opr_set_tables (void);
2491debfc3dSmrg 
2501debfc3dSmrg /* Hash table support.  */
2511debfc3dSmrg static hashval_t hash_expr (rtx, int *);
2521debfc3dSmrg static void insert_expr_in_table (rtx, rtx_insn *);
2531debfc3dSmrg static struct expr *lookup_expr_in_table (rtx);
2541debfc3dSmrg static void dump_hash_table (FILE *);
2551debfc3dSmrg 
2561debfc3dSmrg /* Helpers for eliminate_partially_redundant_load.  */
2571debfc3dSmrg static bool reg_killed_on_edge (rtx, edge);
2581debfc3dSmrg static bool reg_used_on_edge (rtx, edge);
2591debfc3dSmrg 
2601debfc3dSmrg static rtx get_avail_load_store_reg (rtx_insn *);
2611debfc3dSmrg 
2621debfc3dSmrg static bool bb_has_well_behaved_predecessors (basic_block);
2631debfc3dSmrg static struct occr* get_bb_avail_insn (basic_block, struct occr *, int);
2641debfc3dSmrg static void hash_scan_set (rtx_insn *);
2651debfc3dSmrg static void compute_hash_table (void);
2661debfc3dSmrg 
2671debfc3dSmrg /* The work horses of this pass.  */
2681debfc3dSmrg static void eliminate_partially_redundant_load (basic_block,
2691debfc3dSmrg 						rtx_insn *,
2701debfc3dSmrg 						struct expr *);
2711debfc3dSmrg static void eliminate_partially_redundant_loads (void);
2721debfc3dSmrg 
2731debfc3dSmrg 
2741debfc3dSmrg /* Allocate memory for the CUID mapping array and register/memory
2751debfc3dSmrg    tracking tables.  */
2761debfc3dSmrg 
2771debfc3dSmrg static void
alloc_mem(void)2781debfc3dSmrg alloc_mem (void)
2791debfc3dSmrg {
2801debfc3dSmrg   int i;
2811debfc3dSmrg   basic_block bb;
2821debfc3dSmrg   rtx_insn *insn;
2831debfc3dSmrg 
2841debfc3dSmrg   /* Find the largest UID and create a mapping from UIDs to CUIDs.  */
2851debfc3dSmrg   uid_cuid = XCNEWVEC (int, get_max_uid () + 1);
2861debfc3dSmrg   i = 1;
2871debfc3dSmrg   FOR_EACH_BB_FN (bb, cfun)
2881debfc3dSmrg     FOR_BB_INSNS (bb, insn)
2891debfc3dSmrg       {
2901debfc3dSmrg         if (INSN_P (insn))
2911debfc3dSmrg 	  uid_cuid[INSN_UID (insn)] = i++;
2921debfc3dSmrg 	else
2931debfc3dSmrg 	  uid_cuid[INSN_UID (insn)] = i;
2941debfc3dSmrg       }
2951debfc3dSmrg 
2961debfc3dSmrg   /* Allocate the available expressions hash table.  We don't want to
2971debfc3dSmrg      make the hash table too small, but unnecessarily making it too large
2981debfc3dSmrg      also doesn't help.  The i/4 is a gcse.c relic, and seems like a
2991debfc3dSmrg      reasonable choice.  */
3001debfc3dSmrg   expr_table = new hash_table<expr_hasher> (MAX (i / 4, 13));
3011debfc3dSmrg 
3021debfc3dSmrg   /* We allocate everything on obstacks because we often can roll back
3031debfc3dSmrg      the whole obstack to some point.  Freeing obstacks is very fast.  */
3041debfc3dSmrg   gcc_obstack_init (&expr_obstack);
3051debfc3dSmrg   gcc_obstack_init (&occr_obstack);
3061debfc3dSmrg   gcc_obstack_init (&unoccr_obstack);
3071debfc3dSmrg   gcc_obstack_init (&modifies_mem_obstack);
3081debfc3dSmrg 
3091debfc3dSmrg   /* Working array used to track the last set for each register
3101debfc3dSmrg      in the current block.  */
3111debfc3dSmrg   reg_avail_info = (int *) xmalloc (FIRST_PSEUDO_REGISTER * sizeof (int));
3121debfc3dSmrg 
3131debfc3dSmrg   /* Put a dummy modifies_mem object on the modifies_mem_obstack, so we
3141debfc3dSmrg      can roll it back in reset_opr_set_tables.  */
3151debfc3dSmrg   modifies_mem_obstack_bottom =
3161debfc3dSmrg     (struct modifies_mem *) obstack_alloc (&modifies_mem_obstack,
3171debfc3dSmrg 					   sizeof (struct modifies_mem));
3181debfc3dSmrg 
3191debfc3dSmrg   blocks_with_calls = BITMAP_ALLOC (NULL);
3201debfc3dSmrg   modify_mem_list_set = BITMAP_ALLOC (NULL);
3211debfc3dSmrg 
3221debfc3dSmrg   modify_mem_list = (vec_rtx_heap *) xcalloc (last_basic_block_for_fn (cfun),
3231debfc3dSmrg 					      sizeof (vec_rtx_heap));
3241debfc3dSmrg   canon_modify_mem_list
3251debfc3dSmrg     = (vec_modify_pair_heap *) xcalloc (last_basic_block_for_fn (cfun),
3261debfc3dSmrg 					sizeof (vec_modify_pair_heap));
3271debfc3dSmrg }
3281debfc3dSmrg 
3291debfc3dSmrg /* Free memory allocated by alloc_mem.  */
3301debfc3dSmrg 
3311debfc3dSmrg static void
free_mem(void)3321debfc3dSmrg free_mem (void)
3331debfc3dSmrg {
3341debfc3dSmrg   free (uid_cuid);
3351debfc3dSmrg 
3361debfc3dSmrg   delete expr_table;
3371debfc3dSmrg   expr_table = NULL;
3381debfc3dSmrg 
3391debfc3dSmrg   obstack_free (&expr_obstack, NULL);
3401debfc3dSmrg   obstack_free (&occr_obstack, NULL);
3411debfc3dSmrg   obstack_free (&unoccr_obstack, NULL);
3421debfc3dSmrg   obstack_free (&modifies_mem_obstack, NULL);
3431debfc3dSmrg 
3441debfc3dSmrg   unsigned i;
3451debfc3dSmrg   bitmap_iterator bi;
3461debfc3dSmrg   EXECUTE_IF_SET_IN_BITMAP (modify_mem_list_set, 0, i, bi)
3471debfc3dSmrg     {
3481debfc3dSmrg       modify_mem_list[i].release ();
3491debfc3dSmrg       canon_modify_mem_list[i].release ();
3501debfc3dSmrg     }
3511debfc3dSmrg 
3521debfc3dSmrg   BITMAP_FREE (blocks_with_calls);
3531debfc3dSmrg   BITMAP_FREE (modify_mem_list_set);
3541debfc3dSmrg   free (reg_avail_info);
3551debfc3dSmrg   free (modify_mem_list);
3561debfc3dSmrg   free (canon_modify_mem_list);
3571debfc3dSmrg }
3581debfc3dSmrg 
3591debfc3dSmrg 
3601debfc3dSmrg /* Insert expression X in INSN in the hash TABLE.
3611debfc3dSmrg    If it is already present, record it as the last occurrence in INSN's
3621debfc3dSmrg    basic block.  */
3631debfc3dSmrg 
3641debfc3dSmrg static void
insert_expr_in_table(rtx x,rtx_insn * insn)3651debfc3dSmrg insert_expr_in_table (rtx x, rtx_insn *insn)
3661debfc3dSmrg {
3671debfc3dSmrg   int do_not_record_p;
3681debfc3dSmrg   hashval_t hash;
3691debfc3dSmrg   struct expr *cur_expr, **slot;
370*8feb0f0bSmrg   struct occr *avail_occr;
3711debfc3dSmrg 
3721debfc3dSmrg   hash = hash_expr (x, &do_not_record_p);
3731debfc3dSmrg 
3741debfc3dSmrg   /* Do not insert expression in the table if it contains volatile operands,
3751debfc3dSmrg      or if hash_expr determines the expression is something we don't want
3761debfc3dSmrg      to or can't handle.  */
3771debfc3dSmrg   if (do_not_record_p)
3781debfc3dSmrg     return;
3791debfc3dSmrg 
3801debfc3dSmrg   /* We anticipate that redundant expressions are rare, so for convenience
3811debfc3dSmrg      allocate a new hash table element here already and set its fields.
3821debfc3dSmrg      If we don't do this, we need a hack with a static struct expr.  Anyway,
3831debfc3dSmrg      obstack_free is really fast and one more obstack_alloc doesn't hurt if
3841debfc3dSmrg      we're going to see more expressions later on.  */
3851debfc3dSmrg   cur_expr = (struct expr *) obstack_alloc (&expr_obstack,
3861debfc3dSmrg 					    sizeof (struct expr));
3871debfc3dSmrg   cur_expr->expr = x;
3881debfc3dSmrg   cur_expr->hash = hash;
3891debfc3dSmrg   cur_expr->avail_occr = NULL;
3901debfc3dSmrg 
3911debfc3dSmrg   slot = expr_table->find_slot_with_hash (cur_expr, hash, INSERT);
3921debfc3dSmrg 
3931debfc3dSmrg   if (! (*slot))
3941debfc3dSmrg     {
3951debfc3dSmrg       /* The expression isn't found, so insert it.  */
3961debfc3dSmrg       *slot = cur_expr;
3971debfc3dSmrg 
3981debfc3dSmrg       /* Anytime we add an entry to the table, record the index
3991debfc3dSmrg 	 of the new entry.  The bitmap index starts counting
4001debfc3dSmrg 	 at zero.  */
4011debfc3dSmrg       cur_expr->bitmap_index = expr_table->elements () - 1;
4021debfc3dSmrg     }
4031debfc3dSmrg   else
4041debfc3dSmrg     {
4051debfc3dSmrg       /* The expression is already in the table, so roll back the
4061debfc3dSmrg 	 obstack and use the existing table entry.  */
4071debfc3dSmrg       obstack_free (&expr_obstack, cur_expr);
4081debfc3dSmrg       cur_expr = *slot;
4091debfc3dSmrg     }
4101debfc3dSmrg 
411*8feb0f0bSmrg   /* Search for another occurrence in the same basic block.  We insert
412*8feb0f0bSmrg      insns blockwise from start to end, so keep appending to the
413*8feb0f0bSmrg      start of the list so we have to check only a single element.  */
4141debfc3dSmrg   avail_occr = cur_expr->avail_occr;
415*8feb0f0bSmrg   if (avail_occr
416*8feb0f0bSmrg       && BLOCK_FOR_INSN (avail_occr->insn) == BLOCK_FOR_INSN (insn))
4171debfc3dSmrg     avail_occr->insn = insn;
4181debfc3dSmrg   else
4191debfc3dSmrg     {
4201debfc3dSmrg       /* First occurrence of this expression in this basic block.  */
4211debfc3dSmrg       avail_occr = (struct occr *) obstack_alloc (&occr_obstack,
4221debfc3dSmrg 						  sizeof (struct occr));
4231debfc3dSmrg       avail_occr->insn = insn;
424*8feb0f0bSmrg       avail_occr->next = cur_expr->avail_occr;
4251debfc3dSmrg       avail_occr->deleted_p = 0;
426*8feb0f0bSmrg       cur_expr->avail_occr = avail_occr;
4271debfc3dSmrg     }
4281debfc3dSmrg }
4291debfc3dSmrg 
4301debfc3dSmrg 
4311debfc3dSmrg /* Lookup pattern PAT in the expression hash table.
4321debfc3dSmrg    The result is a pointer to the table entry, or NULL if not found.  */
4331debfc3dSmrg 
4341debfc3dSmrg static struct expr *
lookup_expr_in_table(rtx pat)4351debfc3dSmrg lookup_expr_in_table (rtx pat)
4361debfc3dSmrg {
4371debfc3dSmrg   int do_not_record_p;
4381debfc3dSmrg   struct expr **slot, *tmp_expr;
4391debfc3dSmrg   hashval_t hash = hash_expr (pat, &do_not_record_p);
4401debfc3dSmrg 
4411debfc3dSmrg   if (do_not_record_p)
4421debfc3dSmrg     return NULL;
4431debfc3dSmrg 
4441debfc3dSmrg   tmp_expr = (struct expr *) obstack_alloc (&expr_obstack,
4451debfc3dSmrg 					    sizeof (struct expr));
4461debfc3dSmrg   tmp_expr->expr = pat;
4471debfc3dSmrg   tmp_expr->hash = hash;
4481debfc3dSmrg   tmp_expr->avail_occr = NULL;
4491debfc3dSmrg 
4501debfc3dSmrg   slot = expr_table->find_slot_with_hash (tmp_expr, hash, INSERT);
4511debfc3dSmrg   obstack_free (&expr_obstack, tmp_expr);
4521debfc3dSmrg 
4531debfc3dSmrg   if (!slot)
4541debfc3dSmrg     return NULL;
4551debfc3dSmrg   else
4561debfc3dSmrg     return (*slot);
4571debfc3dSmrg }
4581debfc3dSmrg 
4591debfc3dSmrg 
4601debfc3dSmrg /* Dump all expressions and occurrences that are currently in the
4611debfc3dSmrg    expression hash table to FILE.  */
4621debfc3dSmrg 
4631debfc3dSmrg /* This helper is called via htab_traverse.  */
4641debfc3dSmrg int
dump_expr_hash_table_entry(expr ** slot,FILE * file)4651debfc3dSmrg dump_expr_hash_table_entry (expr **slot, FILE *file)
4661debfc3dSmrg {
4671debfc3dSmrg   struct expr *exprs = *slot;
4681debfc3dSmrg   struct occr *occr;
4691debfc3dSmrg 
4701debfc3dSmrg   fprintf (file, "expr: ");
4711debfc3dSmrg   print_rtl (file, exprs->expr);
4721debfc3dSmrg   fprintf (file,"\nhashcode: %u\n", exprs->hash);
4731debfc3dSmrg   fprintf (file,"list of occurrences:\n");
4741debfc3dSmrg   occr = exprs->avail_occr;
4751debfc3dSmrg   while (occr)
4761debfc3dSmrg     {
4771debfc3dSmrg       rtx_insn *insn = occr->insn;
4781debfc3dSmrg       print_rtl_single (file, insn);
4791debfc3dSmrg       fprintf (file, "\n");
4801debfc3dSmrg       occr = occr->next;
4811debfc3dSmrg     }
4821debfc3dSmrg   fprintf (file, "\n");
4831debfc3dSmrg   return 1;
4841debfc3dSmrg }
4851debfc3dSmrg 
4861debfc3dSmrg static void
dump_hash_table(FILE * file)4871debfc3dSmrg dump_hash_table (FILE *file)
4881debfc3dSmrg {
4891debfc3dSmrg   fprintf (file, "\n\nexpression hash table\n");
4901debfc3dSmrg   fprintf (file, "size %ld, %ld elements, %f collision/search ratio\n",
4911debfc3dSmrg            (long) expr_table->size (),
4921debfc3dSmrg            (long) expr_table->elements (),
4931debfc3dSmrg            expr_table->collisions ());
494*8feb0f0bSmrg   if (!expr_table->is_empty ())
4951debfc3dSmrg     {
4961debfc3dSmrg       fprintf (file, "\n\ntable entries:\n");
4971debfc3dSmrg       expr_table->traverse <FILE *, dump_expr_hash_table_entry> (file);
4981debfc3dSmrg     }
4991debfc3dSmrg   fprintf (file, "\n");
5001debfc3dSmrg }
5011debfc3dSmrg 
5021debfc3dSmrg /* Return true if register X is recorded as being set by an instruction
5031debfc3dSmrg    whose CUID is greater than the one given.  */
5041debfc3dSmrg 
5051debfc3dSmrg static bool
reg_changed_after_insn_p(rtx x,int cuid)5061debfc3dSmrg reg_changed_after_insn_p (rtx x, int cuid)
5071debfc3dSmrg {
5081debfc3dSmrg   unsigned int regno, end_regno;
5091debfc3dSmrg 
5101debfc3dSmrg   regno = REGNO (x);
5111debfc3dSmrg   end_regno = END_REGNO (x);
5121debfc3dSmrg   do
5131debfc3dSmrg     if (reg_avail_info[regno] > cuid)
5141debfc3dSmrg       return true;
5151debfc3dSmrg   while (++regno < end_regno);
5161debfc3dSmrg   return false;
5171debfc3dSmrg }
5181debfc3dSmrg 
5191debfc3dSmrg /* Return nonzero if the operands of expression X are unchanged
5201debfc3dSmrg    1) from the start of INSN's basic block up to but not including INSN
5211debfc3dSmrg       if AFTER_INSN is false, or
5221debfc3dSmrg    2) from INSN to the end of INSN's basic block if AFTER_INSN is true.  */
5231debfc3dSmrg 
5241debfc3dSmrg static bool
oprs_unchanged_p(rtx x,rtx_insn * insn,bool after_insn)5251debfc3dSmrg oprs_unchanged_p (rtx x, rtx_insn *insn, bool after_insn)
5261debfc3dSmrg {
5271debfc3dSmrg   int i, j;
5281debfc3dSmrg   enum rtx_code code;
5291debfc3dSmrg   const char *fmt;
5301debfc3dSmrg 
5311debfc3dSmrg   if (x == 0)
5321debfc3dSmrg     return 1;
5331debfc3dSmrg 
5341debfc3dSmrg   code = GET_CODE (x);
5351debfc3dSmrg   switch (code)
5361debfc3dSmrg     {
5371debfc3dSmrg     case REG:
5381debfc3dSmrg       /* We are called after register allocation.  */
5391debfc3dSmrg       gcc_assert (REGNO (x) < FIRST_PSEUDO_REGISTER);
5401debfc3dSmrg       if (after_insn)
5411debfc3dSmrg 	return !reg_changed_after_insn_p (x, INSN_CUID (insn) - 1);
5421debfc3dSmrg       else
5431debfc3dSmrg 	return !reg_changed_after_insn_p (x, 0);
5441debfc3dSmrg 
5451debfc3dSmrg     case MEM:
5461debfc3dSmrg       if (load_killed_in_block_p (INSN_CUID (insn), x, after_insn))
5471debfc3dSmrg 	return 0;
5481debfc3dSmrg       else
5491debfc3dSmrg 	return oprs_unchanged_p (XEXP (x, 0), insn, after_insn);
5501debfc3dSmrg 
5511debfc3dSmrg     case PC:
5521debfc3dSmrg     case CC0: /*FIXME*/
5531debfc3dSmrg     case CONST:
5541debfc3dSmrg     CASE_CONST_ANY:
5551debfc3dSmrg     case SYMBOL_REF:
5561debfc3dSmrg     case LABEL_REF:
5571debfc3dSmrg     case ADDR_VEC:
5581debfc3dSmrg     case ADDR_DIFF_VEC:
5591debfc3dSmrg       return 1;
5601debfc3dSmrg 
5611debfc3dSmrg     case PRE_DEC:
5621debfc3dSmrg     case PRE_INC:
5631debfc3dSmrg     case POST_DEC:
5641debfc3dSmrg     case POST_INC:
5651debfc3dSmrg     case PRE_MODIFY:
5661debfc3dSmrg     case POST_MODIFY:
5671debfc3dSmrg       if (after_insn)
5681debfc3dSmrg 	return 0;
5691debfc3dSmrg       break;
5701debfc3dSmrg 
5711debfc3dSmrg     default:
5721debfc3dSmrg       break;
5731debfc3dSmrg     }
5741debfc3dSmrg 
5751debfc3dSmrg   for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
5761debfc3dSmrg     {
5771debfc3dSmrg       if (fmt[i] == 'e')
5781debfc3dSmrg 	{
5791debfc3dSmrg 	  if (! oprs_unchanged_p (XEXP (x, i), insn, after_insn))
5801debfc3dSmrg 	    return 0;
5811debfc3dSmrg 	}
5821debfc3dSmrg       else if (fmt[i] == 'E')
5831debfc3dSmrg 	for (j = 0; j < XVECLEN (x, i); j++)
5841debfc3dSmrg 	  if (! oprs_unchanged_p (XVECEXP (x, i, j), insn, after_insn))
5851debfc3dSmrg 	    return 0;
5861debfc3dSmrg     }
5871debfc3dSmrg 
5881debfc3dSmrg   return 1;
5891debfc3dSmrg }
5901debfc3dSmrg 
5911debfc3dSmrg 
5921debfc3dSmrg /* Used for communication between find_mem_conflicts and
5931debfc3dSmrg    load_killed_in_block_p.  Nonzero if find_mem_conflicts finds a
5941debfc3dSmrg    conflict between two memory references.
5951debfc3dSmrg    This is a bit of a hack to work around the limitations of note_stores.  */
5961debfc3dSmrg static int mems_conflict_p;
5971debfc3dSmrg 
5981debfc3dSmrg /* DEST is the output of an instruction.  If it is a memory reference, and
5991debfc3dSmrg    possibly conflicts with the load found in DATA, then set mems_conflict_p
6001debfc3dSmrg    to a nonzero value.  */
6011debfc3dSmrg 
6021debfc3dSmrg static void
find_mem_conflicts(rtx dest,const_rtx setter ATTRIBUTE_UNUSED,void * data)6031debfc3dSmrg find_mem_conflicts (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
6041debfc3dSmrg 		    void *data)
6051debfc3dSmrg {
6061debfc3dSmrg   rtx mem_op = (rtx) data;
6071debfc3dSmrg 
6081debfc3dSmrg   while (GET_CODE (dest) == SUBREG
6091debfc3dSmrg 	 || GET_CODE (dest) == ZERO_EXTRACT
6101debfc3dSmrg 	 || GET_CODE (dest) == STRICT_LOW_PART)
6111debfc3dSmrg     dest = XEXP (dest, 0);
6121debfc3dSmrg 
6131debfc3dSmrg   /* If DEST is not a MEM, then it will not conflict with the load.  Note
6141debfc3dSmrg      that function calls are assumed to clobber memory, but are handled
6151debfc3dSmrg      elsewhere.  */
6161debfc3dSmrg   if (! MEM_P (dest))
6171debfc3dSmrg     return;
6181debfc3dSmrg 
6191debfc3dSmrg   if (true_dependence (dest, GET_MODE (dest), mem_op))
6201debfc3dSmrg     mems_conflict_p = 1;
6211debfc3dSmrg }
6221debfc3dSmrg 
6231debfc3dSmrg 
6241debfc3dSmrg /* Return nonzero if the expression in X (a memory reference) is killed
6251debfc3dSmrg    in the current basic block before (if AFTER_INSN is false) or after
6261debfc3dSmrg    (if AFTER_INSN is true) the insn with the CUID in UID_LIMIT.
6271debfc3dSmrg 
6281debfc3dSmrg    This function assumes that the modifies_mem table is flushed when
6291debfc3dSmrg    the hash table construction or redundancy elimination phases start
6301debfc3dSmrg    processing a new basic block.  */
6311debfc3dSmrg 
6321debfc3dSmrg static int
load_killed_in_block_p(int uid_limit,rtx x,bool after_insn)6331debfc3dSmrg load_killed_in_block_p (int uid_limit, rtx x, bool after_insn)
6341debfc3dSmrg {
6351debfc3dSmrg   struct modifies_mem *list_entry = modifies_mem_list;
6361debfc3dSmrg 
6371debfc3dSmrg   while (list_entry)
6381debfc3dSmrg     {
6391debfc3dSmrg       rtx_insn *setter = list_entry->insn;
6401debfc3dSmrg 
6411debfc3dSmrg       /* Ignore entries in the list that do not apply.  */
6421debfc3dSmrg       if ((after_insn
6431debfc3dSmrg 	   && INSN_CUID (setter) < uid_limit)
6441debfc3dSmrg 	  || (! after_insn
6451debfc3dSmrg 	      && INSN_CUID (setter) > uid_limit))
6461debfc3dSmrg 	{
6471debfc3dSmrg 	  list_entry = list_entry->next;
6481debfc3dSmrg 	  continue;
6491debfc3dSmrg 	}
6501debfc3dSmrg 
6511debfc3dSmrg       /* If SETTER is a call everything is clobbered.  Note that calls
6521debfc3dSmrg 	 to pure functions are never put on the list, so we need not
6531debfc3dSmrg 	 worry about them.  */
6541debfc3dSmrg       if (CALL_P (setter))
6551debfc3dSmrg 	return 1;
6561debfc3dSmrg 
6571debfc3dSmrg       /* SETTER must be an insn of some kind that sets memory.  Call
6581debfc3dSmrg 	 note_stores to examine each hunk of memory that is modified.
6591debfc3dSmrg 	 It will set mems_conflict_p to nonzero if there may be a
6601debfc3dSmrg 	 conflict between X and SETTER.  */
6611debfc3dSmrg       mems_conflict_p = 0;
662*8feb0f0bSmrg       note_stores (setter, find_mem_conflicts, x);
6631debfc3dSmrg       if (mems_conflict_p)
6641debfc3dSmrg 	return 1;
6651debfc3dSmrg 
6661debfc3dSmrg       list_entry = list_entry->next;
6671debfc3dSmrg     }
6681debfc3dSmrg   return 0;
6691debfc3dSmrg }
6701debfc3dSmrg 
6711debfc3dSmrg 
6721debfc3dSmrg /* Record register first/last/block set information for REGNO in INSN.  */
6731debfc3dSmrg 
6741debfc3dSmrg static inline void
record_last_reg_set_info(rtx_insn * insn,rtx reg)6751debfc3dSmrg record_last_reg_set_info (rtx_insn *insn, rtx reg)
6761debfc3dSmrg {
6771debfc3dSmrg   unsigned int regno, end_regno;
6781debfc3dSmrg 
6791debfc3dSmrg   regno = REGNO (reg);
6801debfc3dSmrg   end_regno = END_REGNO (reg);
6811debfc3dSmrg   do
6821debfc3dSmrg     reg_avail_info[regno] = INSN_CUID (insn);
6831debfc3dSmrg   while (++regno < end_regno);
6841debfc3dSmrg }
6851debfc3dSmrg 
6861debfc3dSmrg static inline void
record_last_reg_set_info_regno(rtx_insn * insn,int regno)6871debfc3dSmrg record_last_reg_set_info_regno (rtx_insn *insn, int regno)
6881debfc3dSmrg {
6891debfc3dSmrg   reg_avail_info[regno] = INSN_CUID (insn);
6901debfc3dSmrg }
6911debfc3dSmrg 
6921debfc3dSmrg 
6931debfc3dSmrg /* Record memory modification information for INSN.  We do not actually care
6941debfc3dSmrg    about the memory location(s) that are set, or even how they are set (consider
6951debfc3dSmrg    a CALL_INSN).  We merely need to record which insns modify memory.  */
6961debfc3dSmrg 
6971debfc3dSmrg static void
record_last_mem_set_info(rtx_insn * insn)6981debfc3dSmrg record_last_mem_set_info (rtx_insn *insn)
6991debfc3dSmrg {
7001debfc3dSmrg   struct modifies_mem *list_entry;
7011debfc3dSmrg 
7021debfc3dSmrg   list_entry = (struct modifies_mem *) obstack_alloc (&modifies_mem_obstack,
7031debfc3dSmrg 						      sizeof (struct modifies_mem));
7041debfc3dSmrg   list_entry->insn = insn;
7051debfc3dSmrg   list_entry->next = modifies_mem_list;
7061debfc3dSmrg   modifies_mem_list = list_entry;
7071debfc3dSmrg 
7081debfc3dSmrg   record_last_mem_set_info_common (insn, modify_mem_list,
7091debfc3dSmrg 				   canon_modify_mem_list,
7101debfc3dSmrg 				   modify_mem_list_set,
7111debfc3dSmrg 				   blocks_with_calls);
7121debfc3dSmrg }
7131debfc3dSmrg 
7141debfc3dSmrg /* Called from compute_hash_table via note_stores to handle one
7151debfc3dSmrg    SET or CLOBBER in an insn.  DATA is really the instruction in which
7161debfc3dSmrg    the SET is taking place.  */
7171debfc3dSmrg 
7181debfc3dSmrg static void
record_last_set_info(rtx dest,const_rtx setter ATTRIBUTE_UNUSED,void * data)7191debfc3dSmrg record_last_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED, void *data)
7201debfc3dSmrg {
7211debfc3dSmrg   rtx_insn *last_set_insn = (rtx_insn *) data;
7221debfc3dSmrg 
7231debfc3dSmrg   if (GET_CODE (dest) == SUBREG)
7241debfc3dSmrg     dest = SUBREG_REG (dest);
7251debfc3dSmrg 
7261debfc3dSmrg   if (REG_P (dest))
7271debfc3dSmrg     record_last_reg_set_info (last_set_insn, dest);
7281debfc3dSmrg   else if (MEM_P (dest))
7291debfc3dSmrg     {
7301debfc3dSmrg       /* Ignore pushes, they don't clobber memory.  They may still
7311debfc3dSmrg 	 clobber the stack pointer though.  Some targets do argument
7321debfc3dSmrg 	 pushes without adding REG_INC notes.  See e.g. PR25196,
7331debfc3dSmrg 	 where a pushsi2 on i386 doesn't have REG_INC notes.  Note
7341debfc3dSmrg 	 such changes here too.  */
7351debfc3dSmrg       if (! push_operand (dest, GET_MODE (dest)))
7361debfc3dSmrg 	record_last_mem_set_info (last_set_insn);
7371debfc3dSmrg       else
7381debfc3dSmrg 	record_last_reg_set_info_regno (last_set_insn, STACK_POINTER_REGNUM);
7391debfc3dSmrg     }
7401debfc3dSmrg }
7411debfc3dSmrg 
7421debfc3dSmrg 
7431debfc3dSmrg /* Reset tables used to keep track of what's still available since the
7441debfc3dSmrg    start of the block.  */
7451debfc3dSmrg 
7461debfc3dSmrg static void
reset_opr_set_tables(void)7471debfc3dSmrg reset_opr_set_tables (void)
7481debfc3dSmrg {
7491debfc3dSmrg   memset (reg_avail_info, 0, FIRST_PSEUDO_REGISTER * sizeof (int));
7501debfc3dSmrg   obstack_free (&modifies_mem_obstack, modifies_mem_obstack_bottom);
7511debfc3dSmrg   modifies_mem_list = NULL;
7521debfc3dSmrg }
7531debfc3dSmrg 
7541debfc3dSmrg 
7551debfc3dSmrg /* Record things set by INSN.
7561debfc3dSmrg    This data is used by oprs_unchanged_p.  */
7571debfc3dSmrg 
7581debfc3dSmrg static void
record_opr_changes(rtx_insn * insn)7591debfc3dSmrg record_opr_changes (rtx_insn *insn)
7601debfc3dSmrg {
7611debfc3dSmrg   rtx note;
7621debfc3dSmrg 
7631debfc3dSmrg   /* Find all stores and record them.  */
764*8feb0f0bSmrg   note_stores (insn, record_last_set_info, insn);
7651debfc3dSmrg 
7661debfc3dSmrg   /* Also record autoincremented REGs for this insn as changed.  */
7671debfc3dSmrg   for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
7681debfc3dSmrg     if (REG_NOTE_KIND (note) == REG_INC)
7691debfc3dSmrg       record_last_reg_set_info (insn, XEXP (note, 0));
7701debfc3dSmrg 
7711debfc3dSmrg   /* Finally, if this is a call, record all call clobbers.  */
7721debfc3dSmrg   if (CALL_P (insn))
7731debfc3dSmrg     {
7741debfc3dSmrg       unsigned int regno;
7751debfc3dSmrg       hard_reg_set_iterator hrsi;
776*8feb0f0bSmrg       /* We don't track modes of hard registers, so we need to be
777*8feb0f0bSmrg 	 conservative and assume that partial kills are full kills.  */
778*8feb0f0bSmrg       HARD_REG_SET callee_clobbers
779*8feb0f0bSmrg 	= insn_callee_abi (insn).full_and_partial_reg_clobbers ();
780*8feb0f0bSmrg       EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi)
7811debfc3dSmrg 	record_last_reg_set_info_regno (insn, regno);
7821debfc3dSmrg 
7831debfc3dSmrg       if (! RTL_CONST_OR_PURE_CALL_P (insn))
7841debfc3dSmrg 	record_last_mem_set_info (insn);
7851debfc3dSmrg     }
7861debfc3dSmrg }
7871debfc3dSmrg 
7881debfc3dSmrg 
7891debfc3dSmrg /* Scan the pattern of INSN and add an entry to the hash TABLE.
7901debfc3dSmrg    After reload we are interested in loads/stores only.  */
7911debfc3dSmrg 
7921debfc3dSmrg static void
hash_scan_set(rtx_insn * insn)7931debfc3dSmrg hash_scan_set (rtx_insn *insn)
7941debfc3dSmrg {
7951debfc3dSmrg   rtx pat = PATTERN (insn);
7961debfc3dSmrg   rtx src = SET_SRC (pat);
7971debfc3dSmrg   rtx dest = SET_DEST (pat);
7981debfc3dSmrg 
7991debfc3dSmrg   /* We are only interested in loads and stores.  */
8001debfc3dSmrg   if (! MEM_P (src) && ! MEM_P (dest))
8011debfc3dSmrg     return;
8021debfc3dSmrg 
8031debfc3dSmrg   /* Don't mess with jumps and nops.  */
8041debfc3dSmrg   if (JUMP_P (insn) || set_noop_p (pat))
8051debfc3dSmrg     return;
8061debfc3dSmrg 
8071debfc3dSmrg   if (REG_P (dest))
8081debfc3dSmrg     {
8091debfc3dSmrg       if (/* Don't CSE something if we can't do a reg/reg copy.  */
8101debfc3dSmrg 	  can_copy_p (GET_MODE (dest))
8111debfc3dSmrg 	  /* Is SET_SRC something we want to gcse?  */
8121debfc3dSmrg 	  && general_operand (src, GET_MODE (src))
8131debfc3dSmrg #ifdef STACK_REGS
8141debfc3dSmrg 	  /* Never consider insns touching the register stack.  It may
8151debfc3dSmrg 	     create situations that reg-stack cannot handle (e.g. a stack
8161debfc3dSmrg 	     register live across an abnormal edge).  */
8171debfc3dSmrg 	  && (REGNO (dest) < FIRST_STACK_REG || REGNO (dest) > LAST_STACK_REG)
8181debfc3dSmrg #endif
8191debfc3dSmrg 	  /* An expression is not available if its operands are
8201debfc3dSmrg 	     subsequently modified, including this insn.  */
8211debfc3dSmrg 	  && oprs_unchanged_p (src, insn, true))
8221debfc3dSmrg 	{
8231debfc3dSmrg 	  insert_expr_in_table (src, insn);
8241debfc3dSmrg 	}
8251debfc3dSmrg     }
8261debfc3dSmrg   else if (REG_P (src))
8271debfc3dSmrg     {
8281debfc3dSmrg       /* Only record sets of pseudo-regs in the hash table.  */
8291debfc3dSmrg       if (/* Don't CSE something if we can't do a reg/reg copy.  */
8301debfc3dSmrg 	  can_copy_p (GET_MODE (src))
8311debfc3dSmrg 	  /* Is SET_DEST something we want to gcse?  */
8321debfc3dSmrg 	  && general_operand (dest, GET_MODE (dest))
8331debfc3dSmrg #ifdef STACK_REGS
8341debfc3dSmrg 	  /* As above for STACK_REGS.  */
8351debfc3dSmrg 	  && (REGNO (src) < FIRST_STACK_REG || REGNO (src) > LAST_STACK_REG)
8361debfc3dSmrg #endif
8371debfc3dSmrg 	  && ! (flag_float_store && FLOAT_MODE_P (GET_MODE (dest)))
8381debfc3dSmrg 	  /* Check if the memory expression is killed after insn.  */
8391debfc3dSmrg 	  && ! load_killed_in_block_p (INSN_CUID (insn) + 1, dest, true)
8401debfc3dSmrg 	  && oprs_unchanged_p (XEXP (dest, 0), insn, true))
8411debfc3dSmrg 	{
8421debfc3dSmrg 	  insert_expr_in_table (dest, insn);
8431debfc3dSmrg 	}
8441debfc3dSmrg     }
8451debfc3dSmrg }
8461debfc3dSmrg 
8471debfc3dSmrg 
8481debfc3dSmrg /* Create hash table of memory expressions available at end of basic
8491debfc3dSmrg    blocks.  Basically you should think of this hash table as the
8501debfc3dSmrg    representation of AVAIL_OUT.  This is the set of expressions that
8511debfc3dSmrg    is generated in a basic block and not killed before the end of the
8521debfc3dSmrg    same basic block.  Notice that this is really a local computation.  */
8531debfc3dSmrg 
8541debfc3dSmrg static void
compute_hash_table(void)8551debfc3dSmrg compute_hash_table (void)
8561debfc3dSmrg {
8571debfc3dSmrg   basic_block bb;
8581debfc3dSmrg 
8591debfc3dSmrg   FOR_EACH_BB_FN (bb, cfun)
8601debfc3dSmrg     {
8611debfc3dSmrg       rtx_insn *insn;
8621debfc3dSmrg 
8631debfc3dSmrg       /* First pass over the instructions records information used to
8641debfc3dSmrg 	 determine when registers and memory are last set.
8651debfc3dSmrg 	 Since we compute a "local" AVAIL_OUT, reset the tables that
8661debfc3dSmrg 	 help us keep track of what has been modified since the start
8671debfc3dSmrg 	 of the block.  */
8681debfc3dSmrg       reset_opr_set_tables ();
8691debfc3dSmrg       FOR_BB_INSNS (bb, insn)
8701debfc3dSmrg 	{
8711debfc3dSmrg 	  if (INSN_P (insn))
8721debfc3dSmrg             record_opr_changes (insn);
8731debfc3dSmrg 	}
8741debfc3dSmrg 
8751debfc3dSmrg       /* The next pass actually builds the hash table.  */
8761debfc3dSmrg       FOR_BB_INSNS (bb, insn)
8771debfc3dSmrg 	if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == SET)
8781debfc3dSmrg 	  hash_scan_set (insn);
8791debfc3dSmrg     }
8801debfc3dSmrg }
8811debfc3dSmrg 
8821debfc3dSmrg 
8831debfc3dSmrg /* Check if register REG is killed in any insn waiting to be inserted on
8841debfc3dSmrg    edge E.  This function is required to check that our data flow analysis
8851debfc3dSmrg    is still valid prior to commit_edge_insertions.  */
8861debfc3dSmrg 
8871debfc3dSmrg static bool
reg_killed_on_edge(rtx reg,edge e)8881debfc3dSmrg reg_killed_on_edge (rtx reg, edge e)
8891debfc3dSmrg {
8901debfc3dSmrg   rtx_insn *insn;
8911debfc3dSmrg 
8921debfc3dSmrg   for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
8931debfc3dSmrg     if (INSN_P (insn) && reg_set_p (reg, insn))
8941debfc3dSmrg       return true;
8951debfc3dSmrg 
8961debfc3dSmrg   return false;
8971debfc3dSmrg }
8981debfc3dSmrg 
8991debfc3dSmrg /* Similar to above - check if register REG is used in any insn waiting
9001debfc3dSmrg    to be inserted on edge E.
9011debfc3dSmrg    Assumes no such insn can be a CALL_INSN; if so call reg_used_between_p
9021debfc3dSmrg    with PREV(insn),NEXT(insn) instead of calling reg_overlap_mentioned_p.  */
9031debfc3dSmrg 
9041debfc3dSmrg static bool
reg_used_on_edge(rtx reg,edge e)9051debfc3dSmrg reg_used_on_edge (rtx reg, edge e)
9061debfc3dSmrg {
9071debfc3dSmrg   rtx_insn *insn;
9081debfc3dSmrg 
9091debfc3dSmrg   for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
9101debfc3dSmrg     if (INSN_P (insn) && reg_overlap_mentioned_p (reg, PATTERN (insn)))
9111debfc3dSmrg       return true;
9121debfc3dSmrg 
9131debfc3dSmrg   return false;
9141debfc3dSmrg }
9151debfc3dSmrg 
9161debfc3dSmrg /* Return the loaded/stored register of a load/store instruction.  */
9171debfc3dSmrg 
9181debfc3dSmrg static rtx
get_avail_load_store_reg(rtx_insn * insn)9191debfc3dSmrg get_avail_load_store_reg (rtx_insn *insn)
9201debfc3dSmrg {
9211debfc3dSmrg   if (REG_P (SET_DEST (PATTERN (insn))))
9221debfc3dSmrg     /* A load.  */
9231debfc3dSmrg     return SET_DEST (PATTERN (insn));
9241debfc3dSmrg   else
9251debfc3dSmrg     {
9261debfc3dSmrg       /* A store.  */
9271debfc3dSmrg       gcc_assert (REG_P (SET_SRC (PATTERN (insn))));
9281debfc3dSmrg       return SET_SRC (PATTERN (insn));
9291debfc3dSmrg     }
9301debfc3dSmrg }
9311debfc3dSmrg 
9321debfc3dSmrg /* Return nonzero if the predecessors of BB are "well behaved".  */
9331debfc3dSmrg 
9341debfc3dSmrg static bool
bb_has_well_behaved_predecessors(basic_block bb)9351debfc3dSmrg bb_has_well_behaved_predecessors (basic_block bb)
9361debfc3dSmrg {
9371debfc3dSmrg   edge pred;
9381debfc3dSmrg   edge_iterator ei;
9391debfc3dSmrg 
9401debfc3dSmrg   if (EDGE_COUNT (bb->preds) == 0)
9411debfc3dSmrg     return false;
9421debfc3dSmrg 
9431debfc3dSmrg   FOR_EACH_EDGE (pred, ei, bb->preds)
9441debfc3dSmrg     {
9451debfc3dSmrg       /* commit_one_edge_insertion refuses to insert on abnormal edges even if
9461debfc3dSmrg 	 the source has only one successor so EDGE_CRITICAL_P is too weak.  */
9471debfc3dSmrg       if ((pred->flags & EDGE_ABNORMAL) && !single_pred_p (pred->dest))
9481debfc3dSmrg 	return false;
9491debfc3dSmrg 
9501debfc3dSmrg       if ((pred->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label)
9511debfc3dSmrg 	return false;
9521debfc3dSmrg 
9531debfc3dSmrg       if (tablejump_p (BB_END (pred->src), NULL, NULL))
9541debfc3dSmrg 	return false;
9551debfc3dSmrg     }
9561debfc3dSmrg   return true;
9571debfc3dSmrg }
9581debfc3dSmrg 
9591debfc3dSmrg 
9601debfc3dSmrg /* Search for the occurrences of expression in BB.  */
9611debfc3dSmrg 
9621debfc3dSmrg static struct occr*
get_bb_avail_insn(basic_block bb,struct occr * orig_occr,int bitmap_index)9631debfc3dSmrg get_bb_avail_insn (basic_block bb, struct occr *orig_occr, int bitmap_index)
9641debfc3dSmrg {
9651debfc3dSmrg   struct occr *occr = orig_occr;
9661debfc3dSmrg 
9671debfc3dSmrg   for (; occr != NULL; occr = occr->next)
9681debfc3dSmrg     if (BLOCK_FOR_INSN (occr->insn) == bb)
9691debfc3dSmrg       return occr;
9701debfc3dSmrg 
9711debfc3dSmrg   /* If we could not find an occurrence in BB, see if BB
9721debfc3dSmrg      has a single predecessor with an occurrence that is
9731debfc3dSmrg      transparent through BB.  */
974*8feb0f0bSmrg   if (transp
975*8feb0f0bSmrg       && single_pred_p (bb)
9761debfc3dSmrg       && bitmap_bit_p (transp[bb->index], bitmap_index)
9771debfc3dSmrg       && (occr = get_bb_avail_insn (single_pred (bb), orig_occr, bitmap_index)))
9781debfc3dSmrg     {
9791debfc3dSmrg       rtx avail_reg = get_avail_load_store_reg (occr->insn);
9801debfc3dSmrg       if (!reg_set_between_p (avail_reg,
9811debfc3dSmrg 			      PREV_INSN (BB_HEAD (bb)),
9821debfc3dSmrg 			      NEXT_INSN (BB_END (bb)))
9831debfc3dSmrg 	  && !reg_killed_on_edge (avail_reg, single_pred_edge (bb)))
9841debfc3dSmrg 	return occr;
9851debfc3dSmrg     }
9861debfc3dSmrg 
9871debfc3dSmrg   return NULL;
9881debfc3dSmrg }
9891debfc3dSmrg 
9901debfc3dSmrg 
9911debfc3dSmrg /* This helper is called via htab_traverse.  */
9921debfc3dSmrg int
compute_expr_transp(expr ** slot,FILE * dump_file ATTRIBUTE_UNUSED)9931debfc3dSmrg compute_expr_transp (expr **slot, FILE *dump_file ATTRIBUTE_UNUSED)
9941debfc3dSmrg {
9951debfc3dSmrg   struct expr *expr = *slot;
9961debfc3dSmrg 
9971debfc3dSmrg   compute_transp (expr->expr, expr->bitmap_index, transp,
9981debfc3dSmrg 		  blocks_with_calls, modify_mem_list_set,
9991debfc3dSmrg 		  canon_modify_mem_list);
10001debfc3dSmrg   return 1;
10011debfc3dSmrg }
10021debfc3dSmrg 
10031debfc3dSmrg /* This handles the case where several stores feed a partially redundant
10041debfc3dSmrg    load. It checks if the redundancy elimination is possible and if it's
10051debfc3dSmrg    worth it.
10061debfc3dSmrg 
10071debfc3dSmrg    Redundancy elimination is possible if,
10081debfc3dSmrg    1) None of the operands of an insn have been modified since the start
10091debfc3dSmrg       of the current basic block.
10101debfc3dSmrg    2) In any predecessor of the current basic block, the same expression
10111debfc3dSmrg       is generated.
10121debfc3dSmrg 
10131debfc3dSmrg    See the function body for the heuristics that determine if eliminating
10141debfc3dSmrg    a redundancy is also worth doing, assuming it is possible.  */
10151debfc3dSmrg 
10161debfc3dSmrg static void
eliminate_partially_redundant_load(basic_block bb,rtx_insn * insn,struct expr * expr)10171debfc3dSmrg eliminate_partially_redundant_load (basic_block bb, rtx_insn *insn,
10181debfc3dSmrg 				    struct expr *expr)
10191debfc3dSmrg {
10201debfc3dSmrg   edge pred;
10211debfc3dSmrg   rtx_insn *avail_insn = NULL;
10221debfc3dSmrg   rtx avail_reg;
10231debfc3dSmrg   rtx dest, pat;
10241debfc3dSmrg   struct occr *a_occr;
10251debfc3dSmrg   struct unoccr *occr, *avail_occrs = NULL;
10261debfc3dSmrg   struct unoccr *unoccr, *unavail_occrs = NULL, *rollback_unoccr = NULL;
10271debfc3dSmrg   int npred_ok = 0;
1028a2dc1f3fSmrg   profile_count ok_count = profile_count::zero ();
1029a2dc1f3fSmrg 		 /* Redundant load execution count.  */
1030a2dc1f3fSmrg   profile_count critical_count = profile_count::zero ();
1031a2dc1f3fSmrg 		 /* Execution count of critical edges.  */
10321debfc3dSmrg   edge_iterator ei;
10331debfc3dSmrg   bool critical_edge_split = false;
10341debfc3dSmrg 
10351debfc3dSmrg   /* The execution count of the loads to be added to make the
10361debfc3dSmrg      load fully redundant.  */
1037a2dc1f3fSmrg   profile_count not_ok_count = profile_count::zero ();
10381debfc3dSmrg   basic_block pred_bb;
10391debfc3dSmrg 
10401debfc3dSmrg   pat = PATTERN (insn);
10411debfc3dSmrg   dest = SET_DEST (pat);
10421debfc3dSmrg 
10431debfc3dSmrg   /* Check that the loaded register is not used, set, or killed from the
10441debfc3dSmrg      beginning of the block.  */
10451debfc3dSmrg   if (reg_changed_after_insn_p (dest, 0)
10461debfc3dSmrg       || reg_used_between_p (dest, PREV_INSN (BB_HEAD (bb)), insn))
10471debfc3dSmrg     return;
10481debfc3dSmrg 
10491debfc3dSmrg   /* Check potential for replacing load with copy for predecessors.  */
10501debfc3dSmrg   FOR_EACH_EDGE (pred, ei, bb->preds)
10511debfc3dSmrg     {
10521debfc3dSmrg       rtx_insn *next_pred_bb_end;
10531debfc3dSmrg 
10541debfc3dSmrg       avail_insn = NULL;
10551debfc3dSmrg       avail_reg = NULL_RTX;
10561debfc3dSmrg       pred_bb = pred->src;
10571debfc3dSmrg       for (a_occr = get_bb_avail_insn (pred_bb,
10581debfc3dSmrg 				       expr->avail_occr,
10591debfc3dSmrg 				       expr->bitmap_index);
10601debfc3dSmrg 	   a_occr;
10611debfc3dSmrg 	   a_occr = get_bb_avail_insn (pred_bb,
10621debfc3dSmrg 				       a_occr->next,
10631debfc3dSmrg 				       expr->bitmap_index))
10641debfc3dSmrg 	{
10651debfc3dSmrg 	  /* Check if the loaded register is not used.  */
10661debfc3dSmrg 	  avail_insn = a_occr->insn;
10671debfc3dSmrg 	  avail_reg = get_avail_load_store_reg (avail_insn);
10681debfc3dSmrg 	  gcc_assert (avail_reg);
10691debfc3dSmrg 
10701debfc3dSmrg 	  /* Make sure we can generate a move from register avail_reg to
10711debfc3dSmrg 	     dest.  */
10721debfc3dSmrg 	  rtx_insn *move = gen_move_insn (copy_rtx (dest),
10731debfc3dSmrg 					  copy_rtx (avail_reg));
10741debfc3dSmrg 	  extract_insn (move);
10751debfc3dSmrg 	  if (! constrain_operands (1, get_preferred_alternatives (insn,
10761debfc3dSmrg 								   pred_bb))
10771debfc3dSmrg 	      || reg_killed_on_edge (avail_reg, pred)
10781debfc3dSmrg 	      || reg_used_on_edge (dest, pred))
10791debfc3dSmrg 	    {
10801debfc3dSmrg 	      avail_insn = NULL;
10811debfc3dSmrg 	      continue;
10821debfc3dSmrg 	    }
10831debfc3dSmrg 	  next_pred_bb_end = NEXT_INSN (BB_END (BLOCK_FOR_INSN (avail_insn)));
10841debfc3dSmrg 	  if (!reg_set_between_p (avail_reg, avail_insn, next_pred_bb_end))
10851debfc3dSmrg 	    /* AVAIL_INSN remains non-null.  */
10861debfc3dSmrg 	    break;
10871debfc3dSmrg 	  else
10881debfc3dSmrg 	    avail_insn = NULL;
10891debfc3dSmrg 	}
10901debfc3dSmrg 
1091a2dc1f3fSmrg       if (EDGE_CRITICAL_P (pred) && pred->count ().initialized_p ())
1092a2dc1f3fSmrg 	critical_count += pred->count ();
10931debfc3dSmrg 
10941debfc3dSmrg       if (avail_insn != NULL_RTX)
10951debfc3dSmrg 	{
10961debfc3dSmrg 	  npred_ok++;
1097a2dc1f3fSmrg 	  if (pred->count ().initialized_p ())
1098a2dc1f3fSmrg 	    ok_count = ok_count + pred->count ();
10991debfc3dSmrg 	  if (! set_noop_p (PATTERN (gen_move_insn (copy_rtx (dest),
11001debfc3dSmrg 						    copy_rtx (avail_reg)))))
11011debfc3dSmrg 	    {
11021debfc3dSmrg 	      /* Check if there is going to be a split.  */
11031debfc3dSmrg 	      if (EDGE_CRITICAL_P (pred))
11041debfc3dSmrg 		critical_edge_split = true;
11051debfc3dSmrg 	    }
11061debfc3dSmrg 	  else /* Its a dead move no need to generate.  */
11071debfc3dSmrg 	    continue;
11081debfc3dSmrg 	  occr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
11091debfc3dSmrg 						  sizeof (struct unoccr));
11101debfc3dSmrg 	  occr->insn = avail_insn;
11111debfc3dSmrg 	  occr->pred = pred;
11121debfc3dSmrg 	  occr->next = avail_occrs;
11131debfc3dSmrg 	  avail_occrs = occr;
11141debfc3dSmrg 	  if (! rollback_unoccr)
11151debfc3dSmrg 	    rollback_unoccr = occr;
11161debfc3dSmrg 	}
11171debfc3dSmrg       else
11181debfc3dSmrg 	{
11191debfc3dSmrg 	  /* Adding a load on a critical edge will cause a split.  */
11201debfc3dSmrg 	  if (EDGE_CRITICAL_P (pred))
11211debfc3dSmrg 	    critical_edge_split = true;
1122a2dc1f3fSmrg 	  if (pred->count ().initialized_p ())
1123a2dc1f3fSmrg 	    not_ok_count = not_ok_count + pred->count ();
11241debfc3dSmrg 	  unoccr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
11251debfc3dSmrg 						    sizeof (struct unoccr));
11261debfc3dSmrg 	  unoccr->insn = NULL;
11271debfc3dSmrg 	  unoccr->pred = pred;
11281debfc3dSmrg 	  unoccr->next = unavail_occrs;
11291debfc3dSmrg 	  unavail_occrs = unoccr;
11301debfc3dSmrg 	  if (! rollback_unoccr)
11311debfc3dSmrg 	    rollback_unoccr = unoccr;
11321debfc3dSmrg 	}
11331debfc3dSmrg     }
11341debfc3dSmrg 
11351debfc3dSmrg   if (/* No load can be replaced by copy.  */
11361debfc3dSmrg       npred_ok == 0
11371debfc3dSmrg       /* Prevent exploding the code.  */
11381debfc3dSmrg       || (optimize_bb_for_size_p (bb) && npred_ok > 1)
11391debfc3dSmrg       /* If we don't have profile information we cannot tell if splitting
11401debfc3dSmrg          a critical edge is profitable or not so don't do it.  */
1141a2dc1f3fSmrg       || ((!profile_info || profile_status_for_fn (cfun) != PROFILE_READ
11421debfc3dSmrg 	   || targetm.cannot_modify_jumps_p ())
11431debfc3dSmrg 	  && critical_edge_split))
11441debfc3dSmrg     goto cleanup;
11451debfc3dSmrg 
11461debfc3dSmrg   /* Check if it's worth applying the partial redundancy elimination.  */
1147a2dc1f3fSmrg   if (ok_count.to_gcov_type ()
1148*8feb0f0bSmrg       < param_gcse_after_reload_partial_fraction * not_ok_count.to_gcov_type ())
11491debfc3dSmrg     goto cleanup;
1150c0a68be4Smrg 
1151c0a68be4Smrg   gcov_type threshold;
1152c0a68be4Smrg #if (GCC_VERSION >= 5000)
1153*8feb0f0bSmrg   if (__builtin_mul_overflow (param_gcse_after_reload_critical_fraction,
1154c0a68be4Smrg 			      critical_count.to_gcov_type (), &threshold))
1155c0a68be4Smrg     threshold = profile_count::max_count;
1156c0a68be4Smrg #else
1157c0a68be4Smrg   threshold
1158*8feb0f0bSmrg     = (param_gcse_after_reload_critical_fraction
1159*8feb0f0bSmrg        * critical_count.to_gcov_type ());
1160c0a68be4Smrg #endif
1161c0a68be4Smrg 
1162c0a68be4Smrg   if (ok_count.to_gcov_type () < threshold)
11631debfc3dSmrg     goto cleanup;
11641debfc3dSmrg 
11651debfc3dSmrg   /* Generate moves to the loaded register from where
11661debfc3dSmrg      the memory is available.  */
11671debfc3dSmrg   for (occr = avail_occrs; occr; occr = occr->next)
11681debfc3dSmrg     {
11691debfc3dSmrg       avail_insn = occr->insn;
11701debfc3dSmrg       pred = occr->pred;
11711debfc3dSmrg       /* Set avail_reg to be the register having the value of the
11721debfc3dSmrg 	 memory.  */
11731debfc3dSmrg       avail_reg = get_avail_load_store_reg (avail_insn);
11741debfc3dSmrg       gcc_assert (avail_reg);
11751debfc3dSmrg 
11761debfc3dSmrg       insert_insn_on_edge (gen_move_insn (copy_rtx (dest),
11771debfc3dSmrg 					  copy_rtx (avail_reg)),
11781debfc3dSmrg 			   pred);
11791debfc3dSmrg       stats.moves_inserted++;
11801debfc3dSmrg 
11811debfc3dSmrg       if (dump_file)
11821debfc3dSmrg 	fprintf (dump_file,
11831debfc3dSmrg 		 "generating move from %d to %d on edge from %d to %d\n",
11841debfc3dSmrg 		 REGNO (avail_reg),
11851debfc3dSmrg 		 REGNO (dest),
11861debfc3dSmrg 		 pred->src->index,
11871debfc3dSmrg 		 pred->dest->index);
11881debfc3dSmrg     }
11891debfc3dSmrg 
11901debfc3dSmrg   /* Regenerate loads where the memory is unavailable.  */
11911debfc3dSmrg   for (unoccr = unavail_occrs; unoccr; unoccr = unoccr->next)
11921debfc3dSmrg     {
11931debfc3dSmrg       pred = unoccr->pred;
11941debfc3dSmrg       insert_insn_on_edge (copy_insn (PATTERN (insn)), pred);
11951debfc3dSmrg       stats.copies_inserted++;
11961debfc3dSmrg 
11971debfc3dSmrg       if (dump_file)
11981debfc3dSmrg 	{
11991debfc3dSmrg 	  fprintf (dump_file,
12001debfc3dSmrg 		   "generating on edge from %d to %d a copy of load: ",
12011debfc3dSmrg 		   pred->src->index,
12021debfc3dSmrg 		   pred->dest->index);
12031debfc3dSmrg 	  print_rtl (dump_file, PATTERN (insn));
12041debfc3dSmrg 	  fprintf (dump_file, "\n");
12051debfc3dSmrg 	}
12061debfc3dSmrg     }
12071debfc3dSmrg 
12081debfc3dSmrg   /* Delete the insn if it is not available in this block and mark it
12091debfc3dSmrg      for deletion if it is available. If insn is available it may help
12101debfc3dSmrg      discover additional redundancies, so mark it for later deletion.  */
12111debfc3dSmrg   for (a_occr = get_bb_avail_insn (bb, expr->avail_occr, expr->bitmap_index);
12121debfc3dSmrg        a_occr && (a_occr->insn != insn);
12131debfc3dSmrg        a_occr = get_bb_avail_insn (bb, a_occr->next, expr->bitmap_index))
12141debfc3dSmrg     ;
12151debfc3dSmrg 
12161debfc3dSmrg   if (!a_occr)
12171debfc3dSmrg     {
12181debfc3dSmrg       stats.insns_deleted++;
12191debfc3dSmrg 
12201debfc3dSmrg       if (dump_file)
12211debfc3dSmrg 	{
12221debfc3dSmrg 	  fprintf (dump_file, "deleting insn:\n");
12231debfc3dSmrg           print_rtl_single (dump_file, insn);
12241debfc3dSmrg           fprintf (dump_file, "\n");
12251debfc3dSmrg 	}
12261debfc3dSmrg       delete_insn (insn);
12271debfc3dSmrg     }
12281debfc3dSmrg   else
12291debfc3dSmrg     a_occr->deleted_p = 1;
12301debfc3dSmrg 
12311debfc3dSmrg cleanup:
12321debfc3dSmrg   if (rollback_unoccr)
12331debfc3dSmrg     obstack_free (&unoccr_obstack, rollback_unoccr);
12341debfc3dSmrg }
12351debfc3dSmrg 
12361debfc3dSmrg /* Performing the redundancy elimination as described before.  */
12371debfc3dSmrg 
12381debfc3dSmrg static void
eliminate_partially_redundant_loads(void)12391debfc3dSmrg eliminate_partially_redundant_loads (void)
12401debfc3dSmrg {
12411debfc3dSmrg   rtx_insn *insn;
12421debfc3dSmrg   basic_block bb;
12431debfc3dSmrg 
12441debfc3dSmrg   /* Note we start at block 1.  */
12451debfc3dSmrg 
12461debfc3dSmrg   if (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
12471debfc3dSmrg     return;
12481debfc3dSmrg 
12491debfc3dSmrg   FOR_BB_BETWEEN (bb,
12501debfc3dSmrg 		  ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb->next_bb,
12511debfc3dSmrg 		  EXIT_BLOCK_PTR_FOR_FN (cfun),
12521debfc3dSmrg 		  next_bb)
12531debfc3dSmrg     {
12541debfc3dSmrg       /* Don't try anything on basic blocks with strange predecessors.  */
12551debfc3dSmrg       if (! bb_has_well_behaved_predecessors (bb))
12561debfc3dSmrg 	continue;
12571debfc3dSmrg 
12581debfc3dSmrg       /* Do not try anything on cold basic blocks.  */
12591debfc3dSmrg       if (optimize_bb_for_size_p (bb))
12601debfc3dSmrg 	continue;
12611debfc3dSmrg 
12621debfc3dSmrg       /* Reset the table of things changed since the start of the current
12631debfc3dSmrg 	 basic block.  */
12641debfc3dSmrg       reset_opr_set_tables ();
12651debfc3dSmrg 
12661debfc3dSmrg       /* Look at all insns in the current basic block and see if there are
12671debfc3dSmrg 	 any loads in it that we can record.  */
12681debfc3dSmrg       FOR_BB_INSNS (bb, insn)
12691debfc3dSmrg 	{
12701debfc3dSmrg 	  /* Is it a load - of the form (set (reg) (mem))?  */
12711debfc3dSmrg 	  if (NONJUMP_INSN_P (insn)
12721debfc3dSmrg               && GET_CODE (PATTERN (insn)) == SET
12731debfc3dSmrg 	      && REG_P (SET_DEST (PATTERN (insn)))
12741debfc3dSmrg 	      && MEM_P (SET_SRC (PATTERN (insn))))
12751debfc3dSmrg 	    {
12761debfc3dSmrg 	      rtx pat = PATTERN (insn);
12771debfc3dSmrg 	      rtx src = SET_SRC (pat);
12781debfc3dSmrg 	      struct expr *expr;
12791debfc3dSmrg 
12801debfc3dSmrg 	      if (!MEM_VOLATILE_P (src)
12811debfc3dSmrg 		  && GET_MODE (src) != BLKmode
12821debfc3dSmrg 		  && general_operand (src, GET_MODE (src))
12831debfc3dSmrg 		  /* Are the operands unchanged since the start of the
12841debfc3dSmrg 		     block?  */
12851debfc3dSmrg 		  && oprs_unchanged_p (src, insn, false)
12861debfc3dSmrg 		  && !(cfun->can_throw_non_call_exceptions && may_trap_p (src))
12871debfc3dSmrg 		  && !side_effects_p (src)
12881debfc3dSmrg 		  /* Is the expression recorded?  */
12891debfc3dSmrg 		  && (expr = lookup_expr_in_table (src)) != NULL)
12901debfc3dSmrg 		{
12911debfc3dSmrg 		  /* We now have a load (insn) and an available memory at
12921debfc3dSmrg 		     its BB start (expr). Try to remove the loads if it is
12931debfc3dSmrg 		     redundant.  */
12941debfc3dSmrg 		  eliminate_partially_redundant_load (bb, insn, expr);
12951debfc3dSmrg 		}
12961debfc3dSmrg 	    }
12971debfc3dSmrg 
12981debfc3dSmrg 	  /* Keep track of everything modified by this insn, so that we
12991debfc3dSmrg 	     know what has been modified since the start of the current
13001debfc3dSmrg 	     basic block.  */
13011debfc3dSmrg 	  if (INSN_P (insn))
13021debfc3dSmrg 	    record_opr_changes (insn);
13031debfc3dSmrg 	}
13041debfc3dSmrg     }
13051debfc3dSmrg 
13061debfc3dSmrg   commit_edge_insertions ();
13071debfc3dSmrg }
13081debfc3dSmrg 
13091debfc3dSmrg /* Go over the expression hash table and delete insns that were
13101debfc3dSmrg    marked for later deletion.  */
13111debfc3dSmrg 
13121debfc3dSmrg /* This helper is called via htab_traverse.  */
13131debfc3dSmrg int
delete_redundant_insns_1(expr ** slot,void * data ATTRIBUTE_UNUSED)13141debfc3dSmrg delete_redundant_insns_1 (expr **slot, void *data ATTRIBUTE_UNUSED)
13151debfc3dSmrg {
13161debfc3dSmrg   struct expr *exprs = *slot;
13171debfc3dSmrg   struct occr *occr;
13181debfc3dSmrg 
13191debfc3dSmrg   for (occr = exprs->avail_occr; occr != NULL; occr = occr->next)
13201debfc3dSmrg     {
13211debfc3dSmrg       if (occr->deleted_p && dbg_cnt (gcse2_delete))
13221debfc3dSmrg 	{
13231debfc3dSmrg 	  delete_insn (occr->insn);
13241debfc3dSmrg 	  stats.insns_deleted++;
13251debfc3dSmrg 
13261debfc3dSmrg 	  if (dump_file)
13271debfc3dSmrg 	    {
13281debfc3dSmrg 	      fprintf (dump_file, "deleting insn:\n");
13291debfc3dSmrg 	      print_rtl_single (dump_file, occr->insn);
13301debfc3dSmrg 	      fprintf (dump_file, "\n");
13311debfc3dSmrg 	    }
13321debfc3dSmrg 	}
13331debfc3dSmrg     }
13341debfc3dSmrg 
13351debfc3dSmrg   return 1;
13361debfc3dSmrg }
13371debfc3dSmrg 
13381debfc3dSmrg static void
delete_redundant_insns(void)13391debfc3dSmrg delete_redundant_insns (void)
13401debfc3dSmrg {
13411debfc3dSmrg   expr_table->traverse <void *, delete_redundant_insns_1> (NULL);
13421debfc3dSmrg   if (dump_file)
13431debfc3dSmrg     fprintf (dump_file, "\n");
13441debfc3dSmrg }
13451debfc3dSmrg 
13461debfc3dSmrg /* Main entry point of the GCSE after reload - clean some redundant loads
13471debfc3dSmrg    due to spilling.  */
13481debfc3dSmrg 
13491debfc3dSmrg static void
gcse_after_reload_main(rtx f ATTRIBUTE_UNUSED)13501debfc3dSmrg gcse_after_reload_main (rtx f ATTRIBUTE_UNUSED)
13511debfc3dSmrg {
1352*8feb0f0bSmrg   /* Disable computing transparentness if it is too expensive.  */
1353*8feb0f0bSmrg   bool do_transp
1354*8feb0f0bSmrg     = !gcse_or_cprop_is_too_expensive (_("using simple load CSE after register "
1355*8feb0f0bSmrg 					 "allocation"));
13561debfc3dSmrg 
13571debfc3dSmrg   memset (&stats, 0, sizeof (stats));
13581debfc3dSmrg 
13591debfc3dSmrg   /* Allocate memory for this pass.
13601debfc3dSmrg      Also computes and initializes the insns' CUIDs.  */
13611debfc3dSmrg   alloc_mem ();
13621debfc3dSmrg 
13631debfc3dSmrg   /* We need alias analysis.  */
13641debfc3dSmrg   init_alias_analysis ();
13651debfc3dSmrg 
13661debfc3dSmrg   compute_hash_table ();
13671debfc3dSmrg 
13681debfc3dSmrg   if (dump_file)
13691debfc3dSmrg     dump_hash_table (dump_file);
13701debfc3dSmrg 
1371*8feb0f0bSmrg   if (!expr_table->is_empty ())
13721debfc3dSmrg     {
13731debfc3dSmrg       /* Knowing which MEMs are transparent through a block can signifiantly
13741debfc3dSmrg 	 increase the number of redundant loads found.  So compute transparency
13751debfc3dSmrg 	 information for each memory expression in the hash table.  */
13761debfc3dSmrg       df_analyze ();
1377*8feb0f0bSmrg       if (do_transp)
1378*8feb0f0bSmrg 	{
13791debfc3dSmrg 	  /* This cannot be part of the normal allocation routine because
13801debfc3dSmrg 	     we have to know the number of elements in the hash table.  */
13811debfc3dSmrg 	  transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
13821debfc3dSmrg 					 expr_table->elements ());
13831debfc3dSmrg 	  bitmap_vector_ones (transp, last_basic_block_for_fn (cfun));
13841debfc3dSmrg 	  expr_table->traverse <FILE *, compute_expr_transp> (dump_file);
1385*8feb0f0bSmrg 	}
1386*8feb0f0bSmrg       else
1387*8feb0f0bSmrg 	transp = NULL;
13881debfc3dSmrg       eliminate_partially_redundant_loads ();
13891debfc3dSmrg       delete_redundant_insns ();
1390*8feb0f0bSmrg       if (do_transp)
13911debfc3dSmrg 	sbitmap_vector_free (transp);
13921debfc3dSmrg 
13931debfc3dSmrg       if (dump_file)
13941debfc3dSmrg 	{
13951debfc3dSmrg 	  fprintf (dump_file, "GCSE AFTER RELOAD stats:\n");
13961debfc3dSmrg 	  fprintf (dump_file, "copies inserted: %d\n", stats.copies_inserted);
13971debfc3dSmrg 	  fprintf (dump_file, "moves inserted:  %d\n", stats.moves_inserted);
13981debfc3dSmrg 	  fprintf (dump_file, "insns deleted:   %d\n", stats.insns_deleted);
13991debfc3dSmrg 	  fprintf (dump_file, "\n\n");
14001debfc3dSmrg 	}
14011debfc3dSmrg 
14021debfc3dSmrg       statistics_counter_event (cfun, "copies inserted",
14031debfc3dSmrg 				stats.copies_inserted);
14041debfc3dSmrg       statistics_counter_event (cfun, "moves inserted",
14051debfc3dSmrg 				stats.moves_inserted);
14061debfc3dSmrg       statistics_counter_event (cfun, "insns deleted",
14071debfc3dSmrg 				stats.insns_deleted);
14081debfc3dSmrg     }
14091debfc3dSmrg 
14101debfc3dSmrg   /* We are finished with alias.  */
14111debfc3dSmrg   end_alias_analysis ();
14121debfc3dSmrg 
14131debfc3dSmrg   free_mem ();
14141debfc3dSmrg }
14151debfc3dSmrg 
14161debfc3dSmrg 
14171debfc3dSmrg 
14181debfc3dSmrg static unsigned int
rest_of_handle_gcse2(void)14191debfc3dSmrg rest_of_handle_gcse2 (void)
14201debfc3dSmrg {
14211debfc3dSmrg   gcse_after_reload_main (get_insns ());
14221debfc3dSmrg   rebuild_jump_labels (get_insns ());
14231debfc3dSmrg   return 0;
14241debfc3dSmrg }
14251debfc3dSmrg 
14261debfc3dSmrg namespace {
14271debfc3dSmrg 
14281debfc3dSmrg const pass_data pass_data_gcse2 =
14291debfc3dSmrg {
14301debfc3dSmrg   RTL_PASS, /* type */
14311debfc3dSmrg   "gcse2", /* name */
14321debfc3dSmrg   OPTGROUP_NONE, /* optinfo_flags */
14331debfc3dSmrg   TV_GCSE_AFTER_RELOAD, /* tv_id */
14341debfc3dSmrg   0, /* properties_required */
14351debfc3dSmrg   0, /* properties_provided */
14361debfc3dSmrg   0, /* properties_destroyed */
14371debfc3dSmrg   0, /* todo_flags_start */
14381debfc3dSmrg   0, /* todo_flags_finish */
14391debfc3dSmrg };
14401debfc3dSmrg 
14411debfc3dSmrg class pass_gcse2 : public rtl_opt_pass
14421debfc3dSmrg {
14431debfc3dSmrg public:
pass_gcse2(gcc::context * ctxt)14441debfc3dSmrg   pass_gcse2 (gcc::context *ctxt)
14451debfc3dSmrg     : rtl_opt_pass (pass_data_gcse2, ctxt)
14461debfc3dSmrg   {}
14471debfc3dSmrg 
14481debfc3dSmrg   /* opt_pass methods: */
gate(function * fun)14491debfc3dSmrg   virtual bool gate (function *fun)
14501debfc3dSmrg     {
14511debfc3dSmrg       return (optimize > 0 && flag_gcse_after_reload
14521debfc3dSmrg 	      && optimize_function_for_speed_p (fun));
14531debfc3dSmrg     }
14541debfc3dSmrg 
execute(function *)14551debfc3dSmrg   virtual unsigned int execute (function *) { return rest_of_handle_gcse2 (); }
14561debfc3dSmrg 
14571debfc3dSmrg }; // class pass_gcse2
14581debfc3dSmrg 
14591debfc3dSmrg } // anon namespace
14601debfc3dSmrg 
14611debfc3dSmrg rtl_opt_pass *
make_pass_gcse2(gcc::context * ctxt)14621debfc3dSmrg make_pass_gcse2 (gcc::context *ctxt)
14631debfc3dSmrg {
14641debfc3dSmrg   return new pass_gcse2 (ctxt);
14651debfc3dSmrg }
1466