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