| /* Common subexpression elimination for GNU compiler. |
| Copyright (C) 1987-2024 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "cfghooks.h" |
| #include "df.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "insn-config.h" |
| #include "regs.h" |
| #include "emit-rtl.h" |
| #include "recog.h" |
| #include "cfgrtl.h" |
| #include "cfganal.h" |
| #include "cfgcleanup.h" |
| #include "alias.h" |
| #include "toplev.h" |
| #include "rtlhooks-def.h" |
| #include "tree-pass.h" |
| #include "dbgcnt.h" |
| #include "rtl-iter.h" |
| #include "regs.h" |
| #include "function-abi.h" |
| #include "rtlanal.h" |
| #include "expr.h" |
| |
| /* The basic idea of common subexpression elimination is to go |
| through the code, keeping a record of expressions that would |
| have the same value at the current scan point, and replacing |
| expressions encountered with the cheapest equivalent expression. |
| |
| It is too complicated to keep track of the different possibilities |
| when control paths merge in this code; so, at each label, we forget all |
| that is known and start fresh. This can be described as processing each |
| extended basic block separately. We have a separate pass to perform |
| global CSE. |
| |
| Note CSE can turn a conditional or computed jump into a nop or |
| an unconditional jump. When this occurs we arrange to run the jump |
| optimizer after CSE to delete the unreachable code. |
| |
| We use two data structures to record the equivalent expressions: |
| a hash table for most expressions, and a vector of "quantity |
| numbers" to record equivalent (pseudo) registers. |
| |
| The use of the special data structure for registers is desirable |
| because it is faster. It is possible because registers references |
| contain a fairly small number, the register number, taken from |
| a contiguously allocated series, and two register references are |
| identical if they have the same number. General expressions |
| do not have any such thing, so the only way to retrieve the |
| information recorded on an expression other than a register |
| is to keep it in a hash table. |
| |
| Registers and "quantity numbers": |
| |
| At the start of each basic block, all of the (hardware and pseudo) |
| registers used in the function are given distinct quantity |
| numbers to indicate their contents. During scan, when the code |
| copies one register into another, we copy the quantity number. |
| When a register is loaded in any other way, we allocate a new |
| quantity number to describe the value generated by this operation. |
| `REG_QTY (N)' records what quantity register N is currently thought |
| of as containing. |
| |
| All real quantity numbers are greater than or equal to zero. |
| If register N has not been assigned a quantity, `REG_QTY (N)' will |
| equal -N - 1, which is always negative. |
| |
| Quantity numbers below zero do not exist and none of the `qty_table' |
| entries should be referenced with a negative index. |
| |
| We also maintain a bidirectional chain of registers for each |
| quantity number. The `qty_table` members `first_reg' and `last_reg', |
| and `reg_eqv_table' members `next' and `prev' hold these chains. |
| |
| The first register in a chain is the one whose lifespan is least local. |
| Among equals, it is the one that was seen first. |
| We replace any equivalent register with that one. |
| |
| If two registers have the same quantity number, it must be true that |
| REG expressions with qty_table `mode' must be in the hash table for both |
| registers and must be in the same class. |
| |
| The converse is not true. Since hard registers may be referenced in |
| any mode, two REG expressions might be equivalent in the hash table |
| but not have the same quantity number if the quantity number of one |
| of the registers is not the same mode as those expressions. |
| |
| Constants and quantity numbers |
| |
| When a quantity has a known constant value, that value is stored |
| in the appropriate qty_table `const_rtx'. This is in addition to |
| putting the constant in the hash table as is usual for non-regs. |
| |
| Whether a reg or a constant is preferred is determined by the configuration |
| macro CONST_COSTS and will often depend on the constant value. In any |
| event, expressions containing constants can be simplified, by fold_rtx. |
| |
| When a quantity has a known nearly constant value (such as an address |
| of a stack slot), that value is stored in the appropriate qty_table |
| `const_rtx'. |
| |
| Integer constants don't have a machine mode. However, cse |
| determines the intended machine mode from the destination |
| of the instruction that moves the constant. The machine mode |
| is recorded in the hash table along with the actual RTL |
| constant expression so that different modes are kept separate. |
| |
| Other expressions: |
| |
| To record known equivalences among expressions in general |
| we use a hash table called `table'. It has a fixed number of buckets |
| that contain chains of `struct table_elt' elements for expressions. |
| These chains connect the elements whose expressions have the same |
| hash codes. |
| |
| Other chains through the same elements connect the elements which |
| currently have equivalent values. |
| |
| Register references in an expression are canonicalized before hashing |
| the expression. This is done using `reg_qty' and qty_table `first_reg'. |
| The hash code of a register reference is computed using the quantity |
| number, not the register number. |
| |
| When the value of an expression changes, it is necessary to remove from the |
| hash table not just that expression but all expressions whose values |
| could be different as a result. |
| |
| 1. If the value changing is in memory, except in special cases |
| ANYTHING referring to memory could be changed. That is because |
| nobody knows where a pointer does not point. |
| The function `invalidate_memory' removes what is necessary. |
| |
| The special cases are when the address is constant or is |
| a constant plus a fixed register such as the frame pointer |
| or a static chain pointer. When such addresses are stored in, |
| we can tell exactly which other such addresses must be invalidated |
| due to overlap. `invalidate' does this. |
| All expressions that refer to non-constant |
| memory addresses are also invalidated. `invalidate_memory' does this. |
| |
| 2. If the value changing is a register, all expressions |
| containing references to that register, and only those, |
| must be removed. |
| |
| Because searching the entire hash table for expressions that contain |
| a register is very slow, we try to figure out when it isn't necessary. |
| Precisely, this is necessary only when expressions have been |
| entered in the hash table using this register, and then the value has |
| changed, and then another expression wants to be added to refer to |
| the register's new value. This sequence of circumstances is rare |
| within any one basic block. |
| |
| `REG_TICK' and `REG_IN_TABLE', accessors for members of |
| cse_reg_info, are used to detect this case. REG_TICK (i) is |
| incremented whenever a value is stored in register i. |
| REG_IN_TABLE (i) holds -1 if no references to register i have been |
| entered in the table; otherwise, it contains the value REG_TICK (i) |
| had when the references were entered. If we want to enter a |
| reference and REG_IN_TABLE (i) != REG_TICK (i), we must scan and |
| remove old references. Until we want to enter a new entry, the |
| mere fact that the two vectors don't match makes the entries be |
| ignored if anyone tries to match them. |
| |
| Registers themselves are entered in the hash table as well as in |
| the equivalent-register chains. However, `REG_TICK' and |
| `REG_IN_TABLE' do not apply to expressions which are simple |
| register references. These expressions are removed from the table |
| immediately when they become invalid, and this can be done even if |
| we do not immediately search for all the expressions that refer to |
| the register. |
| |
| A CLOBBER rtx in an instruction invalidates its operand for further |
| reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK |
| invalidates everything that resides in memory. |
| |
| Related expressions: |
| |
| Constant expressions that differ only by an additive integer |
| are called related. When a constant expression is put in |
| the table, the related expression with no constant term |
| is also entered. These are made to point at each other |
| so that it is possible to find out if there exists any |
| register equivalent to an expression related to a given expression. */ |
| |
| /* Length of qty_table vector. We know in advance we will not need |
| a quantity number this big. */ |
| |
| static int max_qty; |
| |
| /* Next quantity number to be allocated. |
| This is 1 + the largest number needed so far. */ |
| |
| static int next_qty; |
| |
| /* Per-qty information tracking. |
| |
| `first_reg' and `last_reg' track the head and tail of the |
| chain of registers which currently contain this quantity. |
| |
| `mode' contains the machine mode of this quantity. |
| |
| `const_rtx' holds the rtx of the constant value of this |
| quantity, if known. A summations of the frame/arg pointer |
| and a constant can also be entered here. When this holds |
| a known value, `const_insn' is the insn which stored the |
| constant value. |
| |
| `comparison_{code,const,qty}' are used to track when a |
| comparison between a quantity and some constant or register has |
| been passed. In such a case, we know the results of the comparison |
| in case we see it again. These members record a comparison that |
| is known to be true. `comparison_code' holds the rtx code of such |
| a comparison, else it is set to UNKNOWN and the other two |
| comparison members are undefined. `comparison_const' holds |
| the constant being compared against, or zero if the comparison |
| is not against a constant. `comparison_qty' holds the quantity |
| being compared against when the result is known. If the comparison |
| is not with a register, `comparison_qty' is INT_MIN. */ |
| |
| struct qty_table_elem |
| { |
| rtx const_rtx; |
| rtx_insn *const_insn; |
| rtx comparison_const; |
| int comparison_qty; |
| unsigned int first_reg, last_reg; |
| ENUM_BITFIELD(machine_mode) mode : MACHINE_MODE_BITSIZE; |
| ENUM_BITFIELD(rtx_code) comparison_code : RTX_CODE_BITSIZE; |
| }; |
| |
| /* The table of all qtys, indexed by qty number. */ |
| static struct qty_table_elem *qty_table; |
| |
| /* Insn being scanned. */ |
| |
| static rtx_insn *this_insn; |
| static bool optimize_this_for_speed_p; |
| |
| /* Index by register number, gives the number of the next (or |
| previous) register in the chain of registers sharing the same |
| value. |
| |
| Or -1 if this register is at the end of the chain. |
| |
| If REG_QTY (N) == -N - 1, reg_eqv_table[N].next is undefined. */ |
| |
| /* Per-register equivalence chain. */ |
| struct reg_eqv_elem |
| { |
| int next, prev; |
| }; |
| |
| /* The table of all register equivalence chains. */ |
| static struct reg_eqv_elem *reg_eqv_table; |
| |
| struct cse_reg_info |
| { |
| /* The timestamp at which this register is initialized. */ |
| unsigned int timestamp; |
| |
| /* The quantity number of the register's current contents. */ |
| int reg_qty; |
| |
| /* The number of times the register has been altered in the current |
| basic block. */ |
| int reg_tick; |
| |
| /* The REG_TICK value at which rtx's containing this register are |
| valid in the hash table. If this does not equal the current |
| reg_tick value, such expressions existing in the hash table are |
| invalid. */ |
| int reg_in_table; |
| |
| /* The SUBREG that was set when REG_TICK was last incremented. Set |
| to -1 if the last store was to the whole register, not a subreg. */ |
| unsigned int subreg_ticked; |
| }; |
| |
| /* A table of cse_reg_info indexed by register numbers. */ |
| static struct cse_reg_info *cse_reg_info_table; |
| |
| /* The size of the above table. */ |
| static unsigned int cse_reg_info_table_size; |
| |
| /* The index of the first entry that has not been initialized. */ |
| static unsigned int cse_reg_info_table_first_uninitialized; |
| |
| /* The timestamp at the beginning of the current run of |
| cse_extended_basic_block. We increment this variable at the beginning of |
| the current run of cse_extended_basic_block. The timestamp field of a |
| cse_reg_info entry matches the value of this variable if and only |
| if the entry has been initialized during the current run of |
| cse_extended_basic_block. */ |
| static unsigned int cse_reg_info_timestamp; |
| |
| /* A HARD_REG_SET containing all the hard registers for which there is |
| currently a REG expression in the hash table. Note the difference |
| from the above variables, which indicate if the REG is mentioned in some |
| expression in the table. */ |
| |
| static HARD_REG_SET hard_regs_in_table; |
| |
| /* True if CSE has altered the CFG. */ |
| static bool cse_cfg_altered; |
| |
| /* True if CSE has altered conditional jump insns in such a way |
| that jump optimization should be redone. */ |
| static bool cse_jumps_altered; |
| |
| /* True if we put a LABEL_REF into the hash table for an INSN |
| without a REG_LABEL_OPERAND, we have to rerun jump after CSE |
| to put in the note. */ |
| static bool recorded_label_ref; |
| |
| /* canon_hash stores 1 in do_not_record if it notices a reference to PC or |
| some other volatile subexpression. */ |
| |
| static int do_not_record; |
| |
| /* canon_hash stores 1 in hash_arg_in_memory |
| if it notices a reference to memory within the expression being hashed. */ |
| |
| static int hash_arg_in_memory; |
| |
| /* The hash table contains buckets which are chains of `struct table_elt's, |
| each recording one expression's information. |
| That expression is in the `exp' field. |
| |
| The canon_exp field contains a canonical (from the point of view of |
| alias analysis) version of the `exp' field. |
| |
| Those elements with the same hash code are chained in both directions |
| through the `next_same_hash' and `prev_same_hash' fields. |
| |
| Each set of expressions with equivalent values |
| are on a two-way chain through the `next_same_value' |
| and `prev_same_value' fields, and all point with |
| the `first_same_value' field at the first element in |
| that chain. The chain is in order of increasing cost. |
| Each element's cost value is in its `cost' field. |
| |
| The `in_memory' field is nonzero for elements that |
| involve any reference to memory. These elements are removed |
| whenever a write is done to an unidentified location in memory. |
| To be safe, we assume that a memory address is unidentified unless |
| the address is either a symbol constant or a constant plus |
| the frame pointer or argument pointer. |
| |
| The `related_value' field is used to connect related expressions |
| (that differ by adding an integer). |
| The related expressions are chained in a circular fashion. |
| `related_value' is zero for expressions for which this |
| chain is not useful. |
| |
| The `cost' field stores the cost of this element's expression. |
| The `regcost' field stores the value returned by approx_reg_cost for |
| this element's expression. |
| |
| The `is_const' flag is set if the element is a constant (including |
| a fixed address). |
| |
| The `flag' field is used as a temporary during some search routines. |
| |
| The `mode' field is usually the same as GET_MODE (`exp'), but |
| if `exp' is a CONST_INT and has no machine mode then the `mode' |
| field is the mode it was being used as. Each constant is |
| recorded separately for each mode it is used with. */ |
| |
| struct table_elt |
| { |
| rtx exp; |
| rtx canon_exp; |
| struct table_elt *next_same_hash; |
| struct table_elt *prev_same_hash; |
| struct table_elt *next_same_value; |
| struct table_elt *prev_same_value; |
| struct table_elt *first_same_value; |
| struct table_elt *related_value; |
| int cost; |
| int regcost; |
| ENUM_BITFIELD(machine_mode) mode : MACHINE_MODE_BITSIZE; |
| char in_memory; |
| char is_const; |
| char flag; |
| }; |
| |
| /* We don't want a lot of buckets, because we rarely have very many |
| things stored in the hash table, and a lot of buckets slows |
| down a lot of loops that happen frequently. */ |
| #define HASH_SHIFT 5 |
| #define HASH_SIZE (1 << HASH_SHIFT) |
| #define HASH_MASK (HASH_SIZE - 1) |
| |
| /* Determine whether register number N is considered a fixed register for the |
| purpose of approximating register costs. |
| It is desirable to replace other regs with fixed regs, to reduce need for |
| non-fixed hard regs. |
| A reg wins if it is either the frame pointer or designated as fixed. */ |
| #define FIXED_REGNO_P(N) \ |
| ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \ |
| || fixed_regs[N] || global_regs[N]) |
| |
| /* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed |
| hard registers and pointers into the frame are the cheapest with a cost |
| of 0. Next come pseudos with a cost of one and other hard registers with |
| a cost of 2. Aside from these special cases, call `rtx_cost'. */ |
| |
| #define CHEAP_REGNO(N) \ |
| (REGNO_PTR_FRAME_P (N) \ |
| || (HARD_REGISTER_NUM_P (N) \ |
| && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS)) |
| |
| #define COST(X, MODE) \ |
| (REG_P (X) ? 0 : notreg_cost (X, MODE, SET, 1)) |
| #define COST_IN(X, MODE, OUTER, OPNO) \ |
| (REG_P (X) ? 0 : notreg_cost (X, MODE, OUTER, OPNO)) |
| |
| /* Get the number of times this register has been updated in this |
| basic block. */ |
| |
| #define REG_TICK(N) (get_cse_reg_info (N)->reg_tick) |
| |
| /* Get the point at which REG was recorded in the table. */ |
| |
| #define REG_IN_TABLE(N) (get_cse_reg_info (N)->reg_in_table) |
| |
| /* Get the SUBREG set at the last increment to REG_TICK (-1 if not a |
| SUBREG). */ |
| |
| #define SUBREG_TICKED(N) (get_cse_reg_info (N)->subreg_ticked) |
| |
| /* Get the quantity number for REG. */ |
| |
| #define REG_QTY(N) (get_cse_reg_info (N)->reg_qty) |
| |
| /* Determine if the quantity number for register X represents a valid index |
| into the qty_table. */ |
| |
| #define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0) |
| |
| /* Compare table_elt X and Y and return true iff X is cheaper than Y. */ |
| |
| #define CHEAPER(X, Y) \ |
| (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) |
| |
| static struct table_elt *table[HASH_SIZE]; |
| |
| /* Chain of `struct table_elt's made so far for this function |
| but currently removed from the table. */ |
| |
| static struct table_elt *free_element_chain; |
| |
| /* Trace a patch through the CFG. */ |
| |
| struct branch_path |
| { |
| /* The basic block for this path entry. */ |
| basic_block bb; |
| }; |
| |
| /* This data describes a block that will be processed by |
| cse_extended_basic_block. */ |
| |
| struct cse_basic_block_data |
| { |
| /* Total number of SETs in block. */ |
| int nsets; |
| /* Size of current branch path, if any. */ |
| int path_size; |
| /* Current path, indicating which basic_blocks will be processed. */ |
| struct branch_path *path; |
| }; |
| |
| |
| /* Pointers to the live in/live out bitmaps for the boundaries of the |
| current EBB. */ |
| static bitmap cse_ebb_live_in, cse_ebb_live_out; |
| |
| /* A simple bitmap to track which basic blocks have been visited |
| already as part of an already processed extended basic block. */ |
| static sbitmap cse_visited_basic_blocks; |
| |
| static bool fixed_base_plus_p (rtx x); |
| static int notreg_cost (rtx, machine_mode, enum rtx_code, int); |
| static int preferable (int, int, int, int); |
| static void new_basic_block (void); |
| static void make_new_qty (unsigned int, machine_mode); |
| static void make_regs_eqv (unsigned int, unsigned int); |
| static void delete_reg_equiv (unsigned int); |
| static bool mention_regs (rtx); |
| static bool insert_regs (rtx, struct table_elt *, bool); |
| static void remove_from_table (struct table_elt *, unsigned); |
| static void remove_pseudo_from_table (rtx, unsigned); |
| static struct table_elt *lookup (rtx, unsigned, machine_mode); |
| static struct table_elt *lookup_for_remove (rtx, unsigned, machine_mode); |
| static rtx lookup_as_function (rtx, enum rtx_code); |
| static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned, |
| machine_mode, int, int); |
| static struct table_elt *insert (rtx, struct table_elt *, unsigned, |
| machine_mode); |
| static void merge_equiv_classes (struct table_elt *, struct table_elt *); |
| static void invalidate (rtx, machine_mode); |
| static void remove_invalid_refs (unsigned int); |
| static void remove_invalid_subreg_refs (unsigned int, poly_uint64, |
| machine_mode); |
| static void rehash_using_reg (rtx); |
| static void invalidate_memory (void); |
| static rtx use_related_value (rtx, struct table_elt *); |
| |
| static inline unsigned canon_hash (rtx, machine_mode); |
| static inline unsigned safe_hash (rtx, machine_mode); |
| static inline unsigned hash_rtx_string (const char *); |
| |
| static rtx canon_reg (rtx, rtx_insn *); |
| static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *, |
| machine_mode *, |
| machine_mode *); |
| static rtx fold_rtx (rtx, rtx_insn *); |
| static rtx equiv_constant (rtx); |
| static void record_jump_equiv (rtx_insn *, bool); |
| static void record_jump_cond (enum rtx_code, machine_mode, rtx, rtx); |
| static void cse_insn (rtx_insn *); |
| static void cse_prescan_path (struct cse_basic_block_data *); |
| static void invalidate_from_clobbers (rtx_insn *); |
| static void invalidate_from_sets_and_clobbers (rtx_insn *); |
| static void cse_extended_basic_block (struct cse_basic_block_data *); |
| extern void dump_class (struct table_elt*); |
| static void get_cse_reg_info_1 (unsigned int regno); |
| static struct cse_reg_info * get_cse_reg_info (unsigned int regno); |
| |
| static void flush_hash_table (void); |
| static bool insn_live_p (rtx_insn *, int *); |
| static bool set_live_p (rtx, int *); |
| static void cse_change_cc_mode_insn (rtx_insn *, rtx); |
| static void cse_change_cc_mode_insns (rtx_insn *, rtx_insn *, rtx); |
| static machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx, |
| bool); |
| |
| |
| #undef RTL_HOOKS_GEN_LOWPART |
| #define RTL_HOOKS_GEN_LOWPART gen_lowpart_if_possible |
| |
| static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER; |
| |
| /* Compute hash code of X in mode M. Special-case case where X is a pseudo |
| register (hard registers may require `do_not_record' to be set). */ |
| |
| static inline unsigned |
| HASH (rtx x, machine_mode mode) |
| { |
| unsigned h = (REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER |
| ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (x))) |
| : canon_hash (x, mode)); |
| return (h ^ (h >> HASH_SHIFT)) & HASH_MASK; |
| } |
| |
| /* Like HASH, but without side-effects. */ |
| |
| static inline unsigned |
| SAFE_HASH (rtx x, machine_mode mode) |
| { |
| unsigned h = (REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER |
| ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (x))) |
| : safe_hash (x, mode)); |
| return (h ^ (h >> HASH_SHIFT)) & HASH_MASK; |
| } |
| |
| /* Nonzero if X has the form (PLUS frame-pointer integer). */ |
| |
| static bool |
| fixed_base_plus_p (rtx x) |
| { |
| switch (GET_CODE (x)) |
| { |
| case REG: |
| if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx) |
| return true; |
| if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM]) |
| return true; |
| return false; |
| |
| case PLUS: |
| if (!CONST_INT_P (XEXP (x, 1))) |
| return false; |
| return fixed_base_plus_p (XEXP (x, 0)); |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* Dump the expressions in the equivalence class indicated by CLASSP. |
| This function is used only for debugging. */ |
| DEBUG_FUNCTION void |
| dump_class (struct table_elt *classp) |
| { |
| struct table_elt *elt; |
| |
| fprintf (stderr, "Equivalence chain for "); |
| print_rtl (stderr, classp->exp); |
| fprintf (stderr, ": \n"); |
| |
| for (elt = classp->first_same_value; elt; elt = elt->next_same_value) |
| { |
| print_rtl (stderr, elt->exp); |
| fprintf (stderr, "\n"); |
| } |
| } |
| |
| /* Return an estimate of the cost of the registers used in an rtx. |
| This is mostly the number of different REG expressions in the rtx; |
| however for some exceptions like fixed registers we use a cost of |
| 0. If any other hard register reference occurs, return MAX_COST. */ |
| |
| static int |
| approx_reg_cost (const_rtx x) |
| { |
| int cost = 0; |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, x, NONCONST) |
| { |
| const_rtx x = *iter; |
| if (REG_P (x)) |
| { |
| unsigned int regno = REGNO (x); |
| if (!CHEAP_REGNO (regno)) |
| { |
| if (regno < FIRST_PSEUDO_REGISTER) |
| { |
| if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) |
| return MAX_COST; |
| cost += 2; |
| } |
| else |
| cost += 1; |
| } |
| } |
| } |
| return cost; |
| } |
| |
| /* Return a negative value if an rtx A, whose costs are given by COST_A |
| and REGCOST_A, is more desirable than an rtx B. |
| Return a positive value if A is less desirable, or 0 if the two are |
| equally good. */ |
| static int |
| preferable (int cost_a, int regcost_a, int cost_b, int regcost_b) |
| { |
| /* First, get rid of cases involving expressions that are entirely |
| unwanted. */ |
| if (cost_a != cost_b) |
| { |
| if (cost_a == MAX_COST) |
| return 1; |
| if (cost_b == MAX_COST) |
| return -1; |
| } |
| |
| /* Avoid extending lifetimes of hardregs. */ |
| if (regcost_a != regcost_b) |
| { |
| if (regcost_a == MAX_COST) |
| return 1; |
| if (regcost_b == MAX_COST) |
| return -1; |
| } |
| |
| /* Normal operation costs take precedence. */ |
| if (cost_a != cost_b) |
| return cost_a - cost_b; |
| /* Only if these are identical consider effects on register pressure. */ |
| if (regcost_a != regcost_b) |
| return regcost_a - regcost_b; |
| return 0; |
| } |
| |
| /* Internal function, to compute cost when X is not a register; called |
| from COST macro to keep it simple. */ |
| |
| static int |
| notreg_cost (rtx x, machine_mode mode, enum rtx_code outer, int opno) |
| { |
| scalar_int_mode int_mode, inner_mode; |
| return ((GET_CODE (x) == SUBREG |
| && REG_P (SUBREG_REG (x)) |
| && is_int_mode (mode, &int_mode) |
| && is_int_mode (GET_MODE (SUBREG_REG (x)), &inner_mode) |
| && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode) |
| && subreg_lowpart_p (x) |
| && TRULY_NOOP_TRUNCATION_MODES_P (int_mode, inner_mode)) |
| ? 0 |
| : rtx_cost (x, mode, outer, opno, optimize_this_for_speed_p) * 2); |
| } |
| |
| |
| /* Initialize CSE_REG_INFO_TABLE. */ |
| |
| static void |
| init_cse_reg_info (unsigned int nregs) |
| { |
| /* Do we need to grow the table? */ |
| if (nregs > cse_reg_info_table_size) |
| { |
| unsigned int new_size; |
| |
| if (cse_reg_info_table_size < 2048) |
| { |
| /* Compute a new size that is a power of 2 and no smaller |
| than the large of NREGS and 64. */ |
| new_size = (cse_reg_info_table_size |
| ? cse_reg_info_table_size : 64); |
| |
| while (new_size < nregs) |
| new_size *= 2; |
| } |
| else |
| { |
| /* If we need a big table, allocate just enough to hold |
| NREGS registers. */ |
| new_size = nregs; |
| } |
| |
| /* Reallocate the table with NEW_SIZE entries. */ |
| free (cse_reg_info_table); |
| cse_reg_info_table = XNEWVEC (struct cse_reg_info, new_size); |
| cse_reg_info_table_size = new_size; |
| cse_reg_info_table_first_uninitialized = 0; |
| } |
| |
| /* Do we have all of the first NREGS entries initialized? */ |
| if (cse_reg_info_table_first_uninitialized < nregs) |
| { |
| unsigned int old_timestamp = cse_reg_info_timestamp - 1; |
| unsigned int i; |
| |
| /* Put the old timestamp on newly allocated entries so that they |
| will all be considered out of date. We do not touch those |
| entries beyond the first NREGS entries to be nice to the |
| virtual memory. */ |
| for (i = cse_reg_info_table_first_uninitialized; i < nregs; i++) |
| cse_reg_info_table[i].timestamp = old_timestamp; |
| |
| cse_reg_info_table_first_uninitialized = nregs; |
| } |
| } |
| |
| /* Given REGNO, initialize the cse_reg_info entry for REGNO. */ |
| |
| static void |
| get_cse_reg_info_1 (unsigned int regno) |
| { |
| /* Set TIMESTAMP field to CSE_REG_INFO_TIMESTAMP so that this |
| entry will be considered to have been initialized. */ |
| cse_reg_info_table[regno].timestamp = cse_reg_info_timestamp; |
| |
| /* Initialize the rest of the entry. */ |
| cse_reg_info_table[regno].reg_tick = 1; |
| cse_reg_info_table[regno].reg_in_table = -1; |
| cse_reg_info_table[regno].subreg_ticked = -1; |
| cse_reg_info_table[regno].reg_qty = -regno - 1; |
| } |
| |
| /* Find a cse_reg_info entry for REGNO. */ |
| |
| static inline struct cse_reg_info * |
| get_cse_reg_info (unsigned int regno) |
| { |
| struct cse_reg_info *p = &cse_reg_info_table[regno]; |
| |
| /* If this entry has not been initialized, go ahead and initialize |
| it. */ |
| if (p->timestamp != cse_reg_info_timestamp) |
| get_cse_reg_info_1 (regno); |
| |
| return p; |
| } |
| |
| /* Clear the hash table and initialize each register with its own quantity, |
| for a new basic block. */ |
| |
| static void |
| new_basic_block (void) |
| { |
| int i; |
| |
| next_qty = 0; |
| |
| /* Invalidate cse_reg_info_table. */ |
| cse_reg_info_timestamp++; |
| |
| /* Clear out hash table state for this pass. */ |
| CLEAR_HARD_REG_SET (hard_regs_in_table); |
| |
| /* The per-quantity values used to be initialized here, but it is |
| much faster to initialize each as it is made in `make_new_qty'. */ |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| { |
| struct table_elt *first; |
| |
| first = table[i]; |
| if (first != NULL) |
| { |
| struct table_elt *last = first; |
| |
| table[i] = NULL; |
| |
| while (last->next_same_hash != NULL) |
| last = last->next_same_hash; |
| |
| /* Now relink this hash entire chain into |
| the free element list. */ |
| |
| last->next_same_hash = free_element_chain; |
| free_element_chain = first; |
| } |
| } |
| } |
| |
| /* Say that register REG contains a quantity in mode MODE not in any |
| register before and initialize that quantity. */ |
| |
| static void |
| make_new_qty (unsigned int reg, machine_mode mode) |
| { |
| int q; |
| struct qty_table_elem *ent; |
| struct reg_eqv_elem *eqv; |
| |
| gcc_assert (next_qty < max_qty); |
| |
| q = REG_QTY (reg) = next_qty++; |
| ent = &qty_table[q]; |
| ent->first_reg = reg; |
| ent->last_reg = reg; |
| ent->mode = mode; |
| ent->const_rtx = ent->const_insn = NULL; |
| ent->comparison_code = UNKNOWN; |
| |
| eqv = ®_eqv_table[reg]; |
| eqv->next = eqv->prev = -1; |
| } |
| |
| /* Make reg NEW equivalent to reg OLD. |
| OLD is not changing; NEW is. */ |
| |
| static void |
| make_regs_eqv (unsigned int new_reg, unsigned int old_reg) |
| { |
| unsigned int lastr, firstr; |
| int q = REG_QTY (old_reg); |
| struct qty_table_elem *ent; |
| |
| ent = &qty_table[q]; |
| |
| /* Nothing should become eqv until it has a "non-invalid" qty number. */ |
| gcc_assert (REGNO_QTY_VALID_P (old_reg)); |
| |
| REG_QTY (new_reg) = q; |
| firstr = ent->first_reg; |
| lastr = ent->last_reg; |
| |
| /* Prefer fixed hard registers to anything. Prefer pseudo regs to other |
| hard regs. Among pseudos, if NEW will live longer than any other reg |
| of the same qty, and that is beyond the current basic block, |
| make it the new canonical replacement for this qty. */ |
| if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr)) |
| /* Certain fixed registers might be of the class NO_REGS. This means |
| that not only can they not be allocated by the compiler, but |
| they cannot be used in substitutions or canonicalizations |
| either. */ |
| && (new_reg >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new_reg) != NO_REGS) |
| && ((new_reg < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new_reg)) |
| || (new_reg >= FIRST_PSEUDO_REGISTER |
| && (firstr < FIRST_PSEUDO_REGISTER |
| || (bitmap_bit_p (cse_ebb_live_out, new_reg) |
| && !bitmap_bit_p (cse_ebb_live_out, firstr)) |
| || (bitmap_bit_p (cse_ebb_live_in, new_reg) |
| && !bitmap_bit_p (cse_ebb_live_in, firstr)))))) |
| { |
| reg_eqv_table[firstr].prev = new_reg; |
| reg_eqv_table[new_reg].next = firstr; |
| reg_eqv_table[new_reg].prev = -1; |
| ent->first_reg = new_reg; |
| } |
| else |
| { |
| /* If NEW is a hard reg (known to be non-fixed), insert at end. |
| Otherwise, insert before any non-fixed hard regs that are at the |
| end. Registers of class NO_REGS cannot be used as an |
| equivalent for anything. */ |
| while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0 |
| && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr)) |
| && new_reg >= FIRST_PSEUDO_REGISTER) |
| lastr = reg_eqv_table[lastr].prev; |
| reg_eqv_table[new_reg].next = reg_eqv_table[lastr].next; |
| if (reg_eqv_table[lastr].next >= 0) |
| reg_eqv_table[reg_eqv_table[lastr].next].prev = new_reg; |
| else |
| qty_table[q].last_reg = new_reg; |
| reg_eqv_table[lastr].next = new_reg; |
| reg_eqv_table[new_reg].prev = lastr; |
| } |
| } |
| |
| /* Remove REG from its equivalence class. */ |
| |
| static void |
| delete_reg_equiv (unsigned int reg) |
| { |
| struct qty_table_elem *ent; |
| int q = REG_QTY (reg); |
| int p, n; |
| |
| /* If invalid, do nothing. */ |
| if (! REGNO_QTY_VALID_P (reg)) |
| return; |
| |
| ent = &qty_table[q]; |
| |
| p = reg_eqv_table[reg].prev; |
| n = reg_eqv_table[reg].next; |
| |
| if (n != -1) |
| reg_eqv_table[n].prev = p; |
| else |
| ent->last_reg = p; |
| if (p != -1) |
| reg_eqv_table[p].next = n; |
| else |
| ent->first_reg = n; |
| |
| REG_QTY (reg) = -reg - 1; |
| } |
| |
| /* Remove any invalid expressions from the hash table |
| that refer to any of the registers contained in expression X. |
| |
| Make sure that newly inserted references to those registers |
| as subexpressions will be considered valid. |
| |
| mention_regs is not called when a register itself |
| is being stored in the table. |
| |
| Return true if we have done something that may have changed |
| the hash code of X. */ |
| |
| static bool |
| mention_regs (rtx x) |
| { |
| enum rtx_code code; |
| int i, j; |
| const char *fmt; |
| bool changed = false; |
| |
| if (x == 0) |
| return false; |
| |
| code = GET_CODE (x); |
| if (code == REG) |
| { |
| unsigned int regno = REGNO (x); |
| unsigned int endregno = END_REGNO (x); |
| unsigned int i; |
| |
| for (i = regno; i < endregno; i++) |
| { |
| if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i)) |
| remove_invalid_refs (i); |
| |
| REG_IN_TABLE (i) = REG_TICK (i); |
| SUBREG_TICKED (i) = -1; |
| } |
| |
| return false; |
| } |
| |
| /* If this is a SUBREG, we don't want to discard other SUBREGs of the same |
| pseudo if they don't use overlapping words. We handle only pseudos |
| here for simplicity. */ |
| if (code == SUBREG && REG_P (SUBREG_REG (x)) |
| && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER) |
| { |
| unsigned int i = REGNO (SUBREG_REG (x)); |
| |
| if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i)) |
| { |
| /* If REG_IN_TABLE (i) differs from REG_TICK (i) by one, and |
| the last store to this register really stored into this |
| subreg, then remove the memory of this subreg. |
| Otherwise, remove any memory of the entire register and |
| all its subregs from the table. */ |
| if (REG_TICK (i) - REG_IN_TABLE (i) > 1 |
| || SUBREG_TICKED (i) != REGNO (SUBREG_REG (x))) |
| remove_invalid_refs (i); |
| else |
| remove_invalid_subreg_refs (i, SUBREG_BYTE (x), GET_MODE (x)); |
| } |
| |
| REG_IN_TABLE (i) = REG_TICK (i); |
| SUBREG_TICKED (i) = REGNO (SUBREG_REG (x)); |
| return false; |
| } |
| |
| /* If X is a comparison or a COMPARE and either operand is a register |
| that does not have a quantity, give it one. This is so that a later |
| call to record_jump_equiv won't cause X to be assigned a different |
| hash code and not found in the table after that call. |
| |
| It is not necessary to do this here, since rehash_using_reg can |
| fix up the table later, but doing this here eliminates the need to |
| call that expensive function in the most common case where the only |
| use of the register is in the comparison. */ |
| |
| if (code == COMPARE || COMPARISON_P (x)) |
| { |
| if (REG_P (XEXP (x, 0)) |
| && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))) |
| if (insert_regs (XEXP (x, 0), NULL, false)) |
| { |
| rehash_using_reg (XEXP (x, 0)); |
| changed = true; |
| } |
| |
| if (REG_P (XEXP (x, 1)) |
| && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))) |
| if (insert_regs (XEXP (x, 1), NULL, false)) |
| { |
| rehash_using_reg (XEXP (x, 1)); |
| changed = true; |
| } |
| } |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| if (fmt[i] == 'e') |
| { |
| if (mention_regs (XEXP (x, i))) |
| changed = true; |
| } |
| else if (fmt[i] == 'E') |
| for (j = 0; j < XVECLEN (x, i); j++) |
| if (mention_regs (XVECEXP (x, i, j))) |
| changed = true; |
| |
| return changed; |
| } |
| |
| /* Update the register quantities for inserting X into the hash table |
| with a value equivalent to CLASSP. |
| (If the class does not contain a REG, it is irrelevant.) |
| If MODIFIED is true, X is a destination; it is being modified. |
| Note that delete_reg_equiv should be called on a register |
| before insert_regs is done on that register with MODIFIED != 0. |
| |
| True value means that elements of reg_qty have changed |
| so X's hash code may be different. */ |
| |
| static bool |
| insert_regs (rtx x, struct table_elt *classp, bool modified) |
| { |
| if (REG_P (x)) |
| { |
| unsigned int regno = REGNO (x); |
| int qty_valid; |
| |
| /* If REGNO is in the equivalence table already but is of the |
| wrong mode for that equivalence, don't do anything here. */ |
| |
| qty_valid = REGNO_QTY_VALID_P (regno); |
| if (qty_valid) |
| { |
| struct qty_table_elem *ent = &qty_table[REG_QTY (regno)]; |
| |
| if (ent->mode != GET_MODE (x)) |
| return false; |
| } |
| |
| if (modified || ! qty_valid) |
| { |
| if (classp) |
| for (classp = classp->first_same_value; |
| classp != 0; |
| classp = classp->next_same_value) |
| if (REG_P (classp->exp) |
| && GET_MODE (classp->exp) == GET_MODE (x)) |
| { |
| unsigned c_regno = REGNO (classp->exp); |
| |
| gcc_assert (REGNO_QTY_VALID_P (c_regno)); |
| |
| /* Suppose that 5 is hard reg and 100 and 101 are |
| pseudos. Consider |
| |
| (set (reg:si 100) (reg:si 5)) |
| (set (reg:si 5) (reg:si 100)) |
| (set (reg:di 101) (reg:di 5)) |
| |
| We would now set REG_QTY (101) = REG_QTY (5), but the |
| entry for 5 is in SImode. When we use this later in |
| copy propagation, we get the register in wrong mode. */ |
| if (qty_table[REG_QTY (c_regno)].mode != GET_MODE (x)) |
| continue; |
| |
| make_regs_eqv (regno, c_regno); |
| return true; |
| } |
| |
| /* Mention_regs for a SUBREG checks if REG_TICK is exactly one larger |
| than REG_IN_TABLE to find out if there was only a single preceding |
| invalidation - for the SUBREG - or another one, which would be |
| for the full register. However, if we find here that REG_TICK |
| indicates that the register is invalid, it means that it has |
| been invalidated in a separate operation. The SUBREG might be used |
| now (then this is a recursive call), or we might use the full REG |
| now and a SUBREG of it later. So bump up REG_TICK so that |
| mention_regs will do the right thing. */ |
| if (! modified |
| && REG_IN_TABLE (regno) >= 0 |
| && REG_TICK (regno) == REG_IN_TABLE (regno) + 1) |
| REG_TICK (regno)++; |
| make_new_qty (regno, GET_MODE (x)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* If X is a SUBREG, we will likely be inserting the inner register in the |
| table. If that register doesn't have an assigned quantity number at |
| this point but does later, the insertion that we will be doing now will |
| not be accessible because its hash code will have changed. So assign |
| a quantity number now. */ |
| |
| else if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x)) |
| && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x)))) |
| { |
| insert_regs (SUBREG_REG (x), NULL, false); |
| mention_regs (x); |
| return true; |
| } |
| else |
| return mention_regs (x); |
| } |
| |
| |
| /* Compute upper and lower anchors for CST. Also compute the offset of CST |
| from these anchors/bases such that *_BASE + *_OFFS = CST. Return false iff |
| CST is equal to an anchor. */ |
| |
| static bool |
| compute_const_anchors (rtx cst, |
| HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs, |
| HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs) |
| { |
| unsigned HOST_WIDE_INT n = UINTVAL (cst); |
| |
| *lower_base = n & ~(targetm.const_anchor - 1); |
| if ((unsigned HOST_WIDE_INT) *lower_base == n) |
| return false; |
| |
| *upper_base = ((n + (targetm.const_anchor - 1)) |
| & ~(targetm.const_anchor - 1)); |
| *upper_offs = n - *upper_base; |
| *lower_offs = n - *lower_base; |
| return true; |
| } |
| |
| /* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE. */ |
| |
| static void |
| insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs, |
| machine_mode mode) |
| { |
| struct table_elt *elt; |
| unsigned hash; |
| rtx anchor_exp; |
| rtx exp; |
| |
| anchor_exp = gen_int_mode (anchor, mode); |
| hash = HASH (anchor_exp, mode); |
| elt = lookup (anchor_exp, hash, mode); |
| if (!elt) |
| elt = insert (anchor_exp, NULL, hash, mode); |
| |
| exp = plus_constant (mode, reg, offs); |
| /* REG has just been inserted and the hash codes recomputed. */ |
| mention_regs (exp); |
| hash = HASH (exp, mode); |
| |
| /* Use the cost of the register rather than the whole expression. When |
| looking up constant anchors we will further offset the corresponding |
| expression therefore it does not make sense to prefer REGs over |
| reg-immediate additions. Prefer instead the oldest expression. Also |
| don't prefer pseudos over hard regs so that we derive constants in |
| argument registers from other argument registers rather than from the |
| original pseudo that was used to synthesize the constant. */ |
| insert_with_costs (exp, elt, hash, mode, COST (reg, mode), 1); |
| } |
| |
| /* The constant CST is equivalent to the register REG. Create |
| equivalences between the two anchors of CST and the corresponding |
| register-offset expressions using REG. */ |
| |
| static void |
| insert_const_anchors (rtx reg, rtx cst, machine_mode mode) |
| { |
| HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; |
| |
| if (!compute_const_anchors (cst, &lower_base, &lower_offs, |
| &upper_base, &upper_offs)) |
| return; |
| |
| /* Ignore anchors of value 0. Constants accessible from zero are |
| simple. */ |
| if (lower_base != 0) |
| insert_const_anchor (lower_base, reg, -lower_offs, mode); |
| |
| if (upper_base != 0) |
| insert_const_anchor (upper_base, reg, -upper_offs, mode); |
| } |
| |
| /* We need to express ANCHOR_ELT->exp + OFFS. Walk the equivalence list of |
| ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a |
| valid expression. Return the cheapest and oldest of such expressions. In |
| *OLD, return how old the resulting expression is compared to the other |
| equivalent expressions. */ |
| |
| static rtx |
| find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs, |
| unsigned *old) |
| { |
| struct table_elt *elt; |
| unsigned idx; |
| struct table_elt *match_elt; |
| rtx match; |
| |
| /* Find the cheapest and *oldest* expression to maximize the chance of |
| reusing the same pseudo. */ |
| |
| match_elt = NULL; |
| match = NULL_RTX; |
| for (elt = anchor_elt->first_same_value, idx = 0; |
| elt; |
| elt = elt->next_same_value, idx++) |
| { |
| if (match_elt && CHEAPER (match_elt, elt)) |
| return match; |
| |
| if (REG_P (elt->exp) |
| || (GET_CODE (elt->exp) == PLUS |
| && REG_P (XEXP (elt->exp, 0)) |
| && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT)) |
| { |
| rtx x; |
| |
| /* Ignore expressions that are no longer valid. */ |
| if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false)) |
| continue; |
| |
| x = plus_constant (GET_MODE (elt->exp), elt->exp, offs); |
| if (REG_P (x) |
| || (GET_CODE (x) == PLUS |
| && IN_RANGE (INTVAL (XEXP (x, 1)), |
| -targetm.const_anchor, |
| targetm.const_anchor - 1))) |
| { |
| match = x; |
| match_elt = elt; |
| *old = idx; |
| } |
| } |
| } |
| |
| return match; |
| } |
| |
| /* Try to express the constant SRC_CONST using a register+offset expression |
| derived from a constant anchor. Return it if successful or NULL_RTX, |
| otherwise. */ |
| |
| static rtx |
| try_const_anchors (rtx src_const, machine_mode mode) |
| { |
| struct table_elt *lower_elt, *upper_elt; |
| HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; |
| rtx lower_anchor_rtx, upper_anchor_rtx; |
| rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX; |
| unsigned lower_old, upper_old; |
| |
| /* CONST_INT may be in various modes, avoid non-scalar-int mode. */ |
| if (!SCALAR_INT_MODE_P (mode)) |
| return NULL_RTX; |
| |
| if (!compute_const_anchors (src_const, &lower_base, &lower_offs, |
| &upper_base, &upper_offs)) |
| return NULL_RTX; |
| |
| lower_anchor_rtx = GEN_INT (lower_base); |
| upper_anchor_rtx = GEN_INT (upper_base); |
| lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode); |
| upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode); |
| |
| if (lower_elt) |
| lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old); |
| if (upper_elt) |
| upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old); |
| |
| if (!lower_exp) |
| return upper_exp; |
| if (!upper_exp) |
| return lower_exp; |
| |
| /* Return the older expression. */ |
| return (upper_old > lower_old ? upper_exp : lower_exp); |
| } |
| |
| /* Look in or update the hash table. */ |
| |
| /* Remove table element ELT from use in the table. |
| HASH is its hash code, made using the HASH macro. |
| It's an argument because often that is known in advance |
| and we save much time not recomputing it. */ |
| |
| static void |
| remove_from_table (struct table_elt *elt, unsigned int hash) |
| { |
| if (elt == 0) |
| return; |
| |
| /* Mark this element as removed. See cse_insn. */ |
| elt->first_same_value = 0; |
| |
| /* Remove the table element from its equivalence class. */ |
| |
| { |
| struct table_elt *prev = elt->prev_same_value; |
| struct table_elt *next = elt->next_same_value; |
| |
| if (next) |
| next->prev_same_value = prev; |
| |
| if (prev) |
| prev->next_same_value = next; |
| else |
| { |
| struct table_elt *newfirst = next; |
| while (next) |
| { |
| next->first_same_value = newfirst; |
| next = next->next_same_value; |
| } |
| } |
| } |
| |
| /* Remove the table element from its hash bucket. */ |
| |
| { |
| struct table_elt *prev = elt->prev_same_hash; |
| struct table_elt *next = elt->next_same_hash; |
| |
| if (next) |
| next->prev_same_hash = prev; |
| |
| if (prev) |
| prev->next_same_hash = next; |
| else if (table[hash] == elt) |
| table[hash] = next; |
| else |
| { |
| /* This entry is not in the proper hash bucket. This can happen |
| when two classes were merged by `merge_equiv_classes'. Search |
| for the hash bucket that it heads. This happens only very |
| rarely, so the cost is acceptable. */ |
| for (hash = 0; hash < HASH_SIZE; hash++) |
| if (table[hash] == elt) |
| table[hash] = next; |
| } |
| } |
| |
| /* Remove the table element from its related-value circular chain. */ |
| |
| if (elt->related_value != 0 && elt->related_value != elt) |
| { |
| struct table_elt *p = elt->related_value; |
| |
| while (p->related_value != elt) |
| p = p->related_value; |
| p->related_value = elt->related_value; |
| if (p->related_value == p) |
| p->related_value = 0; |
| } |
| |
| /* Now add it to the free element chain. */ |
| elt->next_same_hash = free_element_chain; |
| free_element_chain = elt; |
| } |
| |
| /* Same as above, but X is a pseudo-register. */ |
| |
| static void |
| remove_pseudo_from_table (rtx x, unsigned int hash) |
| { |
| struct table_elt *elt; |
| |
| /* Because a pseudo-register can be referenced in more than one |
| mode, we might have to remove more than one table entry. */ |
| while ((elt = lookup_for_remove (x, hash, VOIDmode))) |
| remove_from_table (elt, hash); |
| } |
| |
| /* Look up X in the hash table and return its table element, |
| or 0 if X is not in the table. |
| |
| MODE is the machine-mode of X, or if X is an integer constant |
| with VOIDmode then MODE is the mode with which X will be used. |
| |
| Here we are satisfied to find an expression whose tree structure |
| looks like X. */ |
| |
| static struct table_elt * |
| lookup (rtx x, unsigned int hash, machine_mode mode) |
| { |
| struct table_elt *p; |
| |
| for (p = table[hash]; p; p = p->next_same_hash) |
| if (mode == p->mode && ((x == p->exp && REG_P (x)) |
| || exp_equiv_p (x, p->exp, !REG_P (x), false))) |
| return p; |
| |
| return 0; |
| } |
| |
| /* Like `lookup' but don't care whether the table element uses invalid regs. |
| Also ignore discrepancies in the machine mode of a register. */ |
| |
| static struct table_elt * |
| lookup_for_remove (rtx x, unsigned int hash, machine_mode mode) |
| { |
| struct table_elt *p; |
| |
| if (REG_P (x)) |
| { |
| unsigned int regno = REGNO (x); |
| |
| /* Don't check the machine mode when comparing registers; |
| invalidating (REG:SI 0) also invalidates (REG:DF 0). */ |
| for (p = table[hash]; p; p = p->next_same_hash) |
| if (REG_P (p->exp) |
| && REGNO (p->exp) == regno) |
| return p; |
| } |
| else |
| { |
| for (p = table[hash]; p; p = p->next_same_hash) |
| if (mode == p->mode |
| && (x == p->exp || exp_equiv_p (x, p->exp, 0, false))) |
| return p; |
| } |
| |
| return 0; |
| } |
| |
| /* Look for an expression equivalent to X and with code CODE. |
| If one is found, return that expression. */ |
| |
| static rtx |
| lookup_as_function (rtx x, enum rtx_code code) |
| { |
| struct table_elt *p |
| = lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x)); |
| |
| if (p == 0) |
| return 0; |
| |
| for (p = p->first_same_value; p; p = p->next_same_value) |
| if (GET_CODE (p->exp) == code |
| /* Make sure this is a valid entry in the table. */ |
| && exp_equiv_p (p->exp, p->exp, 1, false)) |
| return p->exp; |
| |
| return 0; |
| } |
| |
| /* Insert X in the hash table, assuming HASH is its hash code and |
| CLASSP is an element of the class it should go in (or 0 if a new |
| class should be made). COST is the code of X and reg_cost is the |
| cost of registers in X. It is inserted at the proper position to |
| keep the class in the order cheapest first. |
| |
| MODE is the machine-mode of X, or if X is an integer constant |
| with VOIDmode then MODE is the mode with which X will be used. |
| |
| For elements of equal cheapness, the most recent one |
| goes in front, except that the first element in the list |
| remains first unless a cheaper element is added. The order of |
| pseudo-registers does not matter, as canon_reg will be called to |
| find the cheapest when a register is retrieved from the table. |
| |
| The in_memory field in the hash table element is set to 0. |
| The caller must set it nonzero if appropriate. |
| |
| You should call insert_regs (X, CLASSP, MODIFY) before calling here, |
| and if insert_regs returns a nonzero value |
| you must then recompute its hash code before calling here. |
| |
| If necessary, update table showing constant values of quantities. */ |
| |
| static struct table_elt * |
| insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash, |
| machine_mode mode, int cost, int reg_cost) |
| { |
| struct table_elt *elt; |
| |
| /* If X is a register and we haven't made a quantity for it, |
| something is wrong. */ |
| gcc_assert (!REG_P (x) || REGNO_QTY_VALID_P (REGNO (x))); |
| |
| /* If X is a hard register, show it is being put in the table. */ |
| if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) |
| add_to_hard_reg_set (&hard_regs_in_table, GET_MODE (x), REGNO (x)); |
| |
| /* Put an element for X into the right hash bucket. */ |
| |
| elt = free_element_chain; |
| if (elt) |
| free_element_chain = elt->next_same_hash; |
| else |
| elt = XNEW (struct table_elt); |
| |
| elt->exp = x; |
| elt->canon_exp = NULL_RTX; |
| elt->cost = cost; |
| elt->regcost = reg_cost; |
| elt->next_same_value = 0; |
| elt->prev_same_value = 0; |
| elt->next_same_hash = table[hash]; |
| elt->prev_same_hash = 0; |
| elt->related_value = 0; |
| elt->in_memory = 0; |
| elt->mode = mode; |
| elt->is_const = (CONSTANT_P (x) || fixed_base_plus_p (x)); |
| |
| if (table[hash]) |
| table[hash]->prev_same_hash = elt; |
| table[hash] = elt; |
| |
| /* Put it into the proper value-class. */ |
| if (classp) |
| { |
| classp = classp->first_same_value; |
| if (CHEAPER (elt, classp)) |
| /* Insert at the head of the class. */ |
| { |
| struct table_elt *p; |
| elt->next_same_value = classp; |
| classp->prev_same_value = elt; |
| elt->first_same_value = elt; |
| |
| for (p = classp; p; p = p->next_same_value) |
| p->first_same_value = elt; |
| } |
| else |
| { |
| /* Insert not at head of the class. */ |
| /* Put it after the last element cheaper than X. */ |
| struct table_elt *p, *next; |
| |
| for (p = classp; |
| (next = p->next_same_value) && CHEAPER (next, elt); |
| p = next) |
| ; |
| |
| /* Put it after P and before NEXT. */ |
| elt->next_same_value = next; |
| if (next) |
| next->prev_same_value = elt; |
| |
| elt->prev_same_value = p; |
| p->next_same_value = elt; |
| elt->first_same_value = classp; |
| } |
| } |
| else |
| elt->first_same_value = elt; |
| |
| /* If this is a constant being set equivalent to a register or a register |
| being set equivalent to a constant, note the constant equivalence. |
| |
| If this is a constant, it cannot be equivalent to a different constant, |
| and a constant is the only thing that can be cheaper than a register. So |
| we know the register is the head of the class (before the constant was |
| inserted). |
| |
| If this is a register that is not already known equivalent to a |
| constant, we must check the entire class. |
| |
| If this is a register that is already known equivalent to an insn, |
| update the qtys `const_insn' to show that `this_insn' is the latest |
| insn making that quantity equivalent to the constant. */ |
| |
| if (elt->is_const && classp && REG_P (classp->exp) |
| && !REG_P (x)) |
| { |
| int exp_q = REG_QTY (REGNO (classp->exp)); |
| struct qty_table_elem *exp_ent = &qty_table[exp_q]; |
| |
| exp_ent->const_rtx = gen_lowpart (exp_ent->mode, x); |
| exp_ent->const_insn = this_insn; |
| } |
| |
| else if (REG_P (x) |
| && classp |
| && ! qty_table[REG_QTY (REGNO (x))].const_rtx |
| && ! elt->is_const) |
| { |
| struct table_elt *p; |
| |
| for (p = classp; p != 0; p = p->next_same_value) |
| { |
| if (p->is_const && !REG_P (p->exp)) |
| { |
| int x_q = REG_QTY (REGNO (x)); |
| struct qty_table_elem *x_ent = &qty_table[x_q]; |
| |
| x_ent->const_rtx |
| = gen_lowpart (GET_MODE (x), p->exp); |
| x_ent->const_insn = this_insn; |
| break; |
| } |
| } |
| } |
| |
| else if (REG_P (x) |
| && qty_table[REG_QTY (REGNO (x))].const_rtx |
| && GET_MODE (x) == qty_table[REG_QTY (REGNO (x))].mode) |
| qty_table[REG_QTY (REGNO (x))].const_insn = this_insn; |
| |
| /* If this is a constant with symbolic value, |
| and it has a term with an explicit integer value, |
| link it up with related expressions. */ |
| if (GET_CODE (x) == CONST) |
| { |
| rtx subexp = get_related_value (x); |
| unsigned subhash; |
| struct table_elt *subelt, *subelt_prev; |
| |
| if (subexp != 0) |
| { |
| /* Get the integer-free subexpression in the hash table. */ |
| subhash = SAFE_HASH (subexp, mode); |
| subelt = lookup (subexp, subhash, mode); |
| if (subelt == 0) |
| subelt = insert (subexp, NULL, subhash, mode); |
| /* Initialize SUBELT's circular chain if it has none. */ |
| if (subelt->related_value == 0) |
| subelt->related_value = subelt; |
| /* Find the element in the circular chain that precedes SUBELT. */ |
| subelt_prev = subelt; |
| while (subelt_prev->related_value != subelt) |
| subelt_prev = subelt_prev->related_value; |
| /* Put new ELT into SUBELT's circular chain just before SUBELT. |
| This way the element that follows SUBELT is the oldest one. */ |
| elt->related_value = subelt_prev->related_value; |
| subelt_prev->related_value = elt; |
| } |
| } |
| |
| return elt; |
| } |
| |
| /* Wrap insert_with_costs by passing the default costs. */ |
| |
| static struct table_elt * |
| insert (rtx x, struct table_elt *classp, unsigned int hash, |
| machine_mode mode) |
| { |
| return insert_with_costs (x, classp, hash, mode, |
| COST (x, mode), approx_reg_cost (x)); |
| } |
| |
| |
| /* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from |
| CLASS2 into CLASS1. This is done when we have reached an insn which makes |
| the two classes equivalent. |
| |
| CLASS1 will be the surviving class; CLASS2 should not be used after this |
| call. |
| |
| Any invalid entries in CLASS2 will not be copied. */ |
| |
| static void |
| merge_equiv_classes (struct table_elt *class1, struct table_elt *class2) |
| { |
| struct table_elt *elt, *next, *new_elt; |
| |
| /* Ensure we start with the head of the classes. */ |
| class1 = class1->first_same_value; |
| class2 = class2->first_same_value; |
| |
| /* If they were already equal, forget it. */ |
| if (class1 == class2) |
| return; |
| |
| for (elt = class2; elt; elt = next) |
| { |
| unsigned int hash; |
| rtx exp = elt->exp; |
| machine_mode mode = elt->mode; |
| |
| next = elt->next_same_value; |
| |
| /* Remove old entry, make a new one in CLASS1's class. |
| Don't do this for invalid entries as we cannot find their |
| hash code (it also isn't necessary). */ |
| if (REG_P (exp) || exp_equiv_p (exp, exp, 1, false)) |
| { |
| bool need_rehash = false; |
| |
| hash_arg_in_memory = 0; |
| hash = HASH (exp, mode); |
| |
| if (REG_P (exp)) |
| { |
| need_rehash = REGNO_QTY_VALID_P (REGNO (exp)); |
| delete_reg_equiv (REGNO (exp)); |
| } |
| |
| if (REG_P (exp) && REGNO (exp) >= FIRST_PSEUDO_REGISTER) |
| remove_pseudo_from_table (exp, hash); |
| else |
| remove_from_table (elt, hash); |
| |
| if (insert_regs (exp, class1, false) || need_rehash) |
| { |
| rehash_using_reg (exp); |
| hash = HASH (exp, mode); |
| } |
| new_elt = insert (exp, class1, hash, mode); |
| new_elt->in_memory = hash_arg_in_memory; |
| if (GET_CODE (exp) == ASM_OPERANDS && elt->cost == MAX_COST) |
| new_elt->cost = MAX_COST; |
| } |
| } |
| } |
| |
| /* Flush the entire hash table. */ |
| |
| static void |
| flush_hash_table (void) |
| { |
| int i; |
| struct table_elt *p; |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| for (p = table[i]; p; p = table[i]) |
| { |
| /* Note that invalidate can remove elements |
| after P in the current hash chain. */ |
| if (REG_P (p->exp)) |
| invalidate (p->exp, VOIDmode); |
| else |
| remove_from_table (p, i); |
| } |
| } |
| |
| /* Check whether an anti dependence exists between X and EXP. MODE and |
| ADDR are as for canon_anti_dependence. */ |
| |
| static bool |
| check_dependence (const_rtx x, rtx exp, machine_mode mode, rtx addr) |
| { |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, x, NONCONST) |
| { |
| const_rtx x = *iter; |
| if (MEM_P (x) && canon_anti_dependence (x, true, exp, mode, addr)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Remove from the hash table, or mark as invalid, all expressions whose |
| values could be altered by storing in register X. */ |
| |
| static void |
| invalidate_reg (rtx x) |
| { |
| gcc_assert (GET_CODE (x) == REG); |
| |
| /* If X is a register, dependencies on its contents are recorded |
| through the qty number mechanism. Just change the qty number of |
| the register, mark it as invalid for expressions that refer to it, |
| and remove it itself. */ |
| unsigned int regno = REGNO (x); |
| unsigned int hash = HASH (x, GET_MODE (x)); |
| |
| /* Remove REGNO from any quantity list it might be on and indicate |
| that its value might have changed. If it is a pseudo, remove its |
| entry from the hash table. |
| |
| For a hard register, we do the first two actions above for any |
| additional hard registers corresponding to X. Then, if any of these |
| registers are in the table, we must remove any REG entries that |
| overlap these registers. */ |
| |
| delete_reg_equiv (regno); |
| REG_TICK (regno)++; |
| SUBREG_TICKED (regno) = -1; |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| remove_pseudo_from_table (x, hash); |
| else |
| { |
| HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno); |
| unsigned int endregno = END_REGNO (x); |
| unsigned int rn; |
| struct table_elt *p, *next; |
| |
| CLEAR_HARD_REG_BIT (hard_regs_in_table, regno); |
| |
| for (rn = regno + 1; rn < endregno; rn++) |
| { |
| in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn); |
| CLEAR_HARD_REG_BIT (hard_regs_in_table, rn); |
| delete_reg_equiv (rn); |
| REG_TICK (rn)++; |
| SUBREG_TICKED (rn) = -1; |
| } |
| |
| if (in_table) |
| for (hash = 0; hash < HASH_SIZE; hash++) |
| for (p = table[hash]; p; p = next) |
| { |
| next = p->next_same_hash; |
| |
| if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) |
| continue; |
| |
| unsigned int tregno = REGNO (p->exp); |
| unsigned int tendregno = END_REGNO (p->exp); |
| if (tendregno > regno && tregno < endregno) |
| remove_from_table (p, hash); |
| } |
| } |
| } |
| |
| /* Remove from the hash table, or mark as invalid, all expressions whose |
| values could be altered by storing in X. X is a register, a subreg, or |
| a memory reference with nonvarying address (because, when a memory |
| reference with a varying address is stored in, all memory references are |
| removed by invalidate_memory so specific invalidation is superfluous). |
| FULL_MODE, if not VOIDmode, indicates that this much should be |
| invalidated instead of just the amount indicated by the mode of X. This |
| is only used for bitfield stores into memory. |
| |
| A nonvarying address may be just a register or just a symbol reference, |
| or it may be either of those plus a numeric offset. */ |
| |
| static void |
| invalidate (rtx x, machine_mode full_mode) |
| { |
| int i; |
| struct table_elt *p; |
| rtx addr; |
| |
| switch (GET_CODE (x)) |
| { |
| case REG: |
| invalidate_reg (x); |
| return; |
| |
| case SUBREG: |
| invalidate (SUBREG_REG (x), VOIDmode); |
| return; |
| |
| case PARALLEL: |
| for (i = XVECLEN (x, 0) - 1; i >= 0; --i) |
| invalidate (XVECEXP (x, 0, i), VOIDmode); |
| return; |
| |
| case EXPR_LIST: |
| /* This is part of a disjoint return value; extract the location in |
| question ignoring the offset. */ |
| invalidate (XEXP (x, 0), VOIDmode); |
| return; |
| |
| case MEM: |
| addr = canon_rtx (get_addr (XEXP (x, 0))); |
| /* Calculate the canonical version of X here so that |
| true_dependence doesn't generate new RTL for X on each call. */ |
| x = canon_rtx (x); |
| |
| /* Remove all hash table elements that refer to overlapping pieces of |
| memory. */ |
| if (full_mode == VOIDmode) |
| full_mode = GET_MODE (x); |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| { |
| struct table_elt *next; |
| |
| for (p = table[i]; p; p = next) |
| { |
| next = p->next_same_hash; |
| if (p->in_memory) |
| { |
| /* Just canonicalize the expression once; |
| otherwise each time we call invalidate |
| true_dependence will canonicalize the |
| expression again. */ |
| if (!p->canon_exp) |
| p->canon_exp = canon_rtx (p->exp); |
| if (check_dependence (p->canon_exp, x, full_mode, addr)) |
| remove_from_table (p, i); |
| } |
| } |
| } |
| return; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Invalidate DEST. Used when DEST is not going to be added |
| into the hash table for some reason, e.g. do_not_record |
| flagged on it. */ |
| |
| static void |
| invalidate_dest (rtx dest) |
| { |
| if (REG_P (dest) |
| || GET_CODE (dest) == SUBREG |
| || MEM_P (dest)) |
| invalidate (dest, VOIDmode); |
| else if (GET_CODE (dest) == STRICT_LOW_PART |
| || GET_CODE (dest) == ZERO_EXTRACT) |
| invalidate (XEXP (dest, 0), GET_MODE (dest)); |
| } |
| |
| /* Remove all expressions that refer to register REGNO, |
| since they are already invalid, and we are about to |
| mark that register valid again and don't want the old |
| expressions to reappear as valid. */ |
| |
| static void |
| remove_invalid_refs (unsigned int regno) |
| { |
| unsigned int i; |
| struct table_elt *p, *next; |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| for (p = table[i]; p; p = next) |
| { |
| next = p->next_same_hash; |
| if (!REG_P (p->exp) && refers_to_regno_p (regno, p->exp)) |
| remove_from_table (p, i); |
| } |
| } |
| |
| /* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET, |
| and mode MODE. */ |
| static void |
| remove_invalid_subreg_refs (unsigned int regno, poly_uint64 offset, |
| machine_mode mode) |
| { |
| unsigned int i; |
| struct table_elt *p, *next; |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| for (p = table[i]; p; p = next) |
| { |
| rtx exp = p->exp; |
| next = p->next_same_hash; |
| |
| if (!REG_P (exp) |
| && (GET_CODE (exp) != SUBREG |
| || !REG_P (SUBREG_REG (exp)) |
| || REGNO (SUBREG_REG (exp)) != regno |
| || ranges_maybe_overlap_p (SUBREG_BYTE (exp), |
| GET_MODE_SIZE (GET_MODE (exp)), |
| offset, GET_MODE_SIZE (mode))) |
| && refers_to_regno_p (regno, p->exp)) |
| remove_from_table (p, i); |
| } |
| } |
| |
| /* Recompute the hash codes of any valid entries in the hash table that |
| reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG. |
| |
| This is called when we make a jump equivalence. */ |
| |
| static void |
| rehash_using_reg (rtx x) |
| { |
| unsigned int i; |
| struct table_elt *p, *next; |
| unsigned hash; |
| |
| if (GET_CODE (x) == SUBREG) |
| x = SUBREG_REG (x); |
| |
| /* If X is not a register or if the register is known not to be in any |
| valid entries in the table, we have no work to do. */ |
| |
| if (!REG_P (x) |
| || REG_IN_TABLE (REGNO (x)) < 0 |
| || REG_IN_TABLE (REGNO (x)) != REG_TICK (REGNO (x))) |
| return; |
| |
| /* Scan all hash chains looking for valid entries that mention X. |
| If we find one and it is in the wrong hash chain, move it. */ |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| for (p = table[i]; p; p = next) |
| { |
| next = p->next_same_hash; |
| if (reg_mentioned_p (x, p->exp) |
| && exp_equiv_p (p->exp, p->exp, 1, false) |
| && i != (hash = SAFE_HASH (p->exp, p->mode))) |
| { |
| if (p->next_same_hash) |
| p->next_same_hash->prev_same_hash = p->prev_same_hash; |
| |
| if (p->prev_same_hash) |
| p->prev_same_hash->next_same_hash = p->next_same_hash; |
| else |
| table[i] = p->next_same_hash; |
| |
| p->next_same_hash = table[hash]; |
| p->prev_same_hash = 0; |
| if (table[hash]) |
| table[hash]->prev_same_hash = p; |
| table[hash] = p; |
| } |
| } |
| } |
| |
| /* Remove from the hash table any expression that is a call-clobbered |
| register in INSN. Also update their TICK values. */ |
| |
| static void |
| invalidate_for_call (rtx_insn *insn) |
| { |
| unsigned int regno; |
| unsigned hash; |
| struct table_elt *p, *next; |
| int in_table = 0; |
| hard_reg_set_iterator hrsi; |
| |
| /* Go through all the hard registers. For each that might be clobbered |
| in call insn INSN, remove the register from quantity chains and update |
| reg_tick if defined. Also see if any of these registers is currently |
| in the table. |
| |
| ??? We could be more precise for partially-clobbered registers, |
| and only invalidate values that actually occupy the clobbered part |
| of the registers. It doesn't seem worth the effort though, since |
| we shouldn't see this situation much before RA. Whatever choice |
| we make here has to be consistent with the table walk below, |
| so any change to this test will require a change there too. */ |
| HARD_REG_SET callee_clobbers |
| = insn_callee_abi (insn).full_and_partial_reg_clobbers (); |
| EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi) |
| { |
| delete_reg_equiv (regno); |
| if (REG_TICK (regno) >= 0) |
| { |
| REG_TICK (regno)++; |
| SUBREG_TICKED (regno) = -1; |
| } |
| in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0); |
| } |
| |
| /* In the case where we have no call-clobbered hard registers in the |
| table, we are done. Otherwise, scan the table and remove any |
| entry that overlaps a call-clobbered register. */ |
| |
| if (in_table) |
| for (hash = 0; hash < HASH_SIZE; hash++) |
| for (p = table[hash]; p; p = next) |
| { |
| next = p->next_same_hash; |
| |
| if (!REG_P (p->exp) |
| || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER) |
| continue; |
| |
| /* This must use the same test as above rather than the |
| more accurate clobbers_reg_p. */ |
| if (overlaps_hard_reg_set_p (callee_clobbers, GET_MODE (p->exp), |
| REGNO (p->exp))) |
| remove_from_table (p, hash); |
| } |
| } |
| |
| /* Given an expression X of type CONST, |
| and ELT which is its table entry (or 0 if it |
| is not in the hash table), |
| return an alternate expression for X as a register plus integer. |
| If none can be found, return 0. */ |
| |
| static rtx |
| use_related_value (rtx x, struct table_elt *elt) |
| { |
| struct table_elt *relt = 0; |
| struct table_elt *p, *q; |
| HOST_WIDE_INT offset; |
| |
| /* First, is there anything related known? |
| If we have a table element, we can tell from that. |
| Otherwise, must look it up. */ |
| |
| if (elt != 0 && elt->related_value != 0) |
| relt = elt; |
| else if (elt == 0 && GET_CODE (x) == CONST) |
| { |
| rtx subexp = get_related_value (x); |
| if (subexp != 0) |
| relt = lookup (subexp, |
| SAFE_HASH (subexp, GET_MODE (subexp)), |
| GET_MODE (subexp)); |
| } |
| |
| if (relt == 0) |
| return 0; |
| |
| /* Search all related table entries for one that has an |
| equivalent register. */ |
| |
| p = relt; |
| while (1) |
| { |
| /* This loop is strange in that it is executed in two different cases. |
| The first is when X is already in the table. Then it is searching |
| the RELATED_VALUE list of X's class (RELT). The second case is when |
| X is not in the table. Then RELT points to a class for the related |
| value. |
| |
| Ensure that, whatever case we are in, that we ignore classes that have |
| the same value as X. */ |
| |
| if (rtx_equal_p (x, p->exp)) |
| q = 0; |
| else |
| for (q = p->first_same_value; q; q = q->next_same_value) |
| if (REG_P (q->exp)) |
| break; |
| |
| if (q) |
| break; |
| |
| p = p->related_value; |
| |
| /* We went all the way around, so there is nothing to be found. |
| Alternatively, perhaps RELT was in the table for some other reason |
| and it has no related values recorded. */ |
| if (p == relt || p == 0) |
| break; |
| } |
| |
| if (q == 0) |
| return 0; |
| |
| offset = (get_integer_term (x) - get_integer_term (p->exp)); |
| /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ |
| return plus_constant (q->mode, q->exp, offset); |
| } |
| |
| |
| /* Hash a string. Just add its bytes up. */ |
| static inline unsigned |
| hash_rtx_string (const char *ps) |
| { |
| unsigned hash = 0; |
| const unsigned char *p = (const unsigned char *) ps; |
| |
| if (p) |
| while (*p) |
| hash += *p++; |
| |
| return hash; |
| } |
| |
| /* Hash an rtx. We are careful to make sure the value is never negative. |
| Equivalent registers hash identically. |
| MODE is used in hashing for CONST_INTs only; |
| otherwise the mode of X is used. |
| |
| Store 1 in DO_NOT_RECORD_P if any subexpression is volatile. |
| |
| If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains |
| a MEM rtx which does not have the MEM_READONLY_P flag set. |
| |
| Note that cse_insn knows that the hash code of a MEM expression |
| is just (int) MEM plus the hash code of the address. |
| |
| Call CB on each rtx if CB is not NULL. |
| When the callback returns true, we continue with the new rtx. */ |
| |
| unsigned |
| hash_rtx (const_rtx x, machine_mode mode, |
| int *do_not_record_p, int *hash_arg_in_memory_p, |
| bool have_reg_qty, hash_rtx_callback_function cb) |
| { |
| int i, j; |
| unsigned hash = 0; |
| enum rtx_code code; |
| const char *fmt; |
| machine_mode newmode; |
| rtx newx; |
| |
| /* Used to turn recursion into iteration. We can't rely on GCC's |
| tail-recursion elimination since we need to keep accumulating values |
| in HASH. */ |
| repeat: |
| if (x == 0) |
| return hash; |
| |
| /* Invoke the callback first. */ |
| if (cb != NULL |
| && ((*cb) (x, mode, &newx, &newmode))) |
| { |
| hash += hash_rtx (newx, newmode, do_not_record_p, |
| hash_arg_in_memory_p, have_reg_qty, cb); |
| return hash; |
| } |
| |
| code = GET_CODE (x); |
| switch (code) |
| { |
| case REG: |
| { |
| unsigned int regno = REGNO (x); |
| |
| if (do_not_record_p && !reload_completed) |
| { |
| /* On some machines, we can't record any non-fixed hard register, |
| because extending its life will cause reload problems. We |
| consider ap, fp, sp, gp to be fixed for this purpose. |
| |
| We also consider CCmode registers to be fixed for this purpose; |
| failure to do so leads to failure to simplify 0<100 type of |
| conditionals. |
| |
| On all machines, we can't record any global registers. |
| Nor should we record any register that is in a small |
| class, as defined by TARGET_CLASS_LIKELY_SPILLED_P. */ |
| bool record; |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| record = true; |
| else if (x == frame_pointer_rtx |
| || x == hard_frame_pointer_rtx |
| || x == arg_pointer_rtx |
| || x == stack_pointer_rtx |
| || x == pic_offset_table_rtx) |
| record = true; |
| else if (global_regs[regno]) |
| record = false; |
| else if (fixed_regs[regno]) |
| record = true; |
| else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC) |
| record = true; |
| else if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) |
| record = false; |
| else if (targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno))) |
| record = false; |
| else |
| record = true; |
| |
| if (!record) |
| { |
| *do_not_record_p = 1; |
| return 0; |
| } |
| } |
| |
| hash += ((unsigned int) REG << 7); |
| hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno); |
| return hash; |
| } |
| |
| /* We handle SUBREG of a REG specially because the underlying |
| reg changes its hash value with every value change; we don't |
| want to have to forget unrelated subregs when one subreg changes. */ |
| case SUBREG: |
| { |
| if (REG_P (SUBREG_REG (x))) |
| { |
| hash += (((unsigned int) SUBREG << 7) |
| + REGNO (SUBREG_REG (x)) |
| + (constant_lower_bound (SUBREG_BYTE (x)) |
| / UNITS_PER_WORD)); |
| return hash; |
| } |
| break; |
| } |
| |
| case CONST_INT: |
| hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode |
| + (unsigned int) INTVAL (x)); |
| return hash; |
| |
| case CONST_WIDE_INT: |
| for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++) |
| hash += CONST_WIDE_INT_ELT (x, i); |
| return hash; |
| |
| case CONST_POLY_INT: |
| { |
| inchash::hash h; |
| h.add_int (hash); |
| for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i) |
| h.add_wide_int (CONST_POLY_INT_COEFFS (x)[i]); |
| return h.end (); |
| } |
| |
| case CONST_DOUBLE: |
| /* This is like the general case, except that it only counts |
| the integers representing the constant. */ |
| hash += (unsigned int) code + (unsigned int) GET_MODE (x); |
| if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode) |
| hash += ((unsigned int) CONST_DOUBLE_LOW (x) |
| + (unsigned int) CONST_DOUBLE_HIGH (x)); |
| else |
| hash += real_hash (CONST_DOUBLE_REAL_VALUE (x)); |
| return hash; |
| |
| case CONST_FIXED: |
| hash += (unsigned int) code + (unsigned int) GET_MODE (x); |
| hash += fixed_hash (CONST_FIXED_VALUE (x)); |
| return hash; |
| |
| case CONST_VECTOR: |
| { |
| int units; |
| rtx elt; |
| |
| units = const_vector_encoded_nelts (x); |
| |
| for (i = 0; i < units; ++i) |
| { |
| elt = CONST_VECTOR_ENCODED_ELT (x, i); |
| hash += hash_rtx (elt, GET_MODE (elt), |
| do_not_record_p, hash_arg_in_memory_p, |
| have_reg_qty, cb); |
| } |
| |
| return hash; |
| } |
| |
| /* Assume there is only one rtx object for any given label. */ |
| case LABEL_REF: |
| /* We don't hash on the address of the CODE_LABEL to avoid bootstrap |
| differences and differences between each stage's debugging dumps. */ |
| hash += (((unsigned int) LABEL_REF << 7) |
| + CODE_LABEL_NUMBER (label_ref_label (x))); |
| return hash; |
| |
| case SYMBOL_REF: |
| { |
| /* Don't hash on the symbol's address to avoid bootstrap differences. |
| Different hash values may cause expressions to be recorded in |
| different orders and thus different registers to be used in the |
| final assembler. This also avoids differences in the dump files |
| between various stages. */ |
| unsigned int h = 0; |
| const unsigned char *p = (const unsigned char *) XSTR (x, 0); |
| |
| while (*p) |
| h += (h << 7) + *p++; /* ??? revisit */ |
| |
| hash += ((unsigned int) SYMBOL_REF << 7) + h; |
| return hash; |
| } |
| |
| case MEM: |
| /* We don't record if marked volatile or if BLKmode since we don't |
| know the size of the move. */ |
| if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)) |
| { |
| *do_not_record_p = 1; |
| return 0; |
| } |
| if (hash_arg_in_memory_p && !MEM_READONLY_P (x)) |
| *hash_arg_in_memory_p = 1; |
| |
| /* Now that we have already found this special case, |
| might as well speed it up as much as possible. */ |
| hash += (unsigned) MEM; |
| x = XEXP (x, 0); |
| goto repeat; |
| |
| case USE: |
| /* A USE that mentions non-volatile memory needs special |
| handling since the MEM may be BLKmode which normally |
| prevents an entry from being made. Pure calls are |
| marked by a USE which mentions BLKmode memory. |
| See calls.cc:emit_call_1. */ |
| if (MEM_P (XEXP (x, 0)) |
| && ! MEM_VOLATILE_P (XEXP (x, 0))) |
| { |
| hash += (unsigned) USE; |
| x = XEXP (x, 0); |
| |
| if (hash_arg_in_memory_p && !MEM_READONLY_P (x)) |
| *hash_arg_in_memory_p = 1; |
| |
| /* Now that we have already found this special case, |
| might as well speed it up as much as possible. */ |
| hash += (unsigned) MEM; |
| x = XEXP (x, 0); |
| goto repeat; |
| } |
| break; |
| |
| case PRE_DEC: |
| case PRE_INC: |
| case POST_DEC: |
| case POST_INC: |
| case PRE_MODIFY: |
| case POST_MODIFY: |
| case PC: |
| case CALL: |
| case UNSPEC_VOLATILE: |
| if (do_not_record_p) { |
| *do_not_record_p = 1; |
| return 0; |
| } |
| else |
| return hash; |
| break; |
| |
| case ASM_OPERANDS: |
| if (do_not_record_p && MEM_VOLATILE_P (x)) |
| { |
| *do_not_record_p = 1; |
| return 0; |
| } |
| else |
| { |
| /* We don't want to take the filename and line into account. */ |
| hash += (unsigned) code + (unsigned) GET_MODE (x) |
| + hash_rtx_string (ASM_OPERANDS_TEMPLATE (x)) |
| + hash_rtx_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x)) |
| + (unsigned) ASM_OPERANDS_OUTPUT_IDX (x); |
| |
| if (ASM_OPERANDS_INPUT_LENGTH (x)) |
| { |
| for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) |
| { |
| hash += (hash_rtx (ASM_OPERANDS_INPUT (x, i), |
| GET_MODE (ASM_OPERANDS_INPUT (x, i)), |
| do_not_record_p, hash_arg_in_memory_p, |
| have_reg_qty, cb) |
| + hash_rtx_string |
| (ASM_OPERANDS_INPUT_CONSTRAINT (x, i))); |
| } |
| |
| hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0)); |
| x = ASM_OPERANDS_INPUT (x, 0); |
| mode = GET_MODE (x); |
| goto repeat; |
| } |
| |
| return hash; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| i = GET_RTX_LENGTH (code) - 1; |
| hash += (unsigned) code + (unsigned) GET_MODE (x); |
| fmt = GET_RTX_FORMAT (code); |
| for (; i >= 0; i--) |
| { |
| switch (fmt[i]) |
| { |
| case 'e': |
| /* If we are about to do the last recursive call |
| needed at this level, change it into iteration. |
| This function is called enough to be worth it. */ |
| if (i == 0) |
| { |
| x = XEXP (x, i); |
| goto repeat; |
| } |
| |
| hash += hash_rtx (XEXP (x, i), VOIDmode, do_not_record_p, |
| hash_arg_in_memory_p, |
| have_reg_qty, cb); |
| break; |
| |
| case 'E': |
| for (j = 0; j < XVECLEN (x, i); j++) |
| hash += hash_rtx (XVECEXP (x, i, j), VOIDmode, do_not_record_p, |
| hash_arg_in_memory_p, |
| have_reg_qty, cb); |
| break; |
| |
| case 's': |
| hash += hash_rtx_string (XSTR (x, i)); |
| break; |
| |
| case 'i': |
| hash += (unsigned int) XINT (x, i); |
| break; |
| |
| case 'p': |
| hash += constant_lower_bound (SUBREG_BYTE (x)); |
| break; |
| |
| case '0': case 't': |
| /* Unused. */ |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| return hash; |
| } |
| |
| /* Hash an rtx X for cse via hash_rtx. |
| Stores 1 in do_not_record if any subexpression is volatile. |
| Stores 1 in hash_arg_in_memory if X contains a mem rtx which |
| does not have the MEM_READONLY_P flag set. */ |
| |
| static inline unsigned |
| canon_hash (rtx x, machine_mode mode) |
| { |
| return hash_rtx (x, mode, &do_not_record, &hash_arg_in_memory, true); |
| } |
| |
| /* Like canon_hash but with no side effects, i.e. do_not_record |
| and hash_arg_in_memory are not changed. */ |
| |
| static inline unsigned |
| safe_hash (rtx x, machine_mode mode) |
| { |
| int dummy_do_not_record; |
| return hash_rtx (x, mode, &dummy_do_not_record, NULL, true); |
| } |
| |
| /* Return true iff X and Y would canonicalize into the same thing, |
| without actually constructing the canonicalization of either one. |
| If VALIDATE is nonzero, |
| we assume X is an expression being processed from the rtl |
| and Y was found in the hash table. We check register refs |
| in Y for being marked as valid. |
| |
| If FOR_GCSE is true, we compare X and Y for equivalence for GCSE. */ |
| |
| bool |
| exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse) |
| { |
| int i, j; |
| enum rtx_code code; |
| const char *fmt; |
| |
| /* Note: it is incorrect to assume an expression is equivalent to itself |
| if VALIDATE is nonzero. */ |
| if (x == y && !validate) |
| return true; |
| |
| if (x == 0 || y == 0) |
| return x == y; |
| |
| code = GET_CODE (x); |
| if (code != GET_CODE (y)) |
| return false; |
| |
| /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ |
| if (GET_MODE (x) != GET_MODE (y)) |
| return false; |
| |
| /* MEMs referring to different address space are not equivalent. */ |
| if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y)) |
| return false; |
| |
| switch (code) |
| { |
| case PC: |
| CASE_CONST_UNIQUE: |
| return x == y; |
| |
| case CONST_VECTOR: |
| if (!same_vector_encodings_p (x, y)) |
| return false; |
| break; |
| |
| case LABEL_REF: |
| return label_ref_label (x) == label_ref_label (y); |
| |
| case SYMBOL_REF: |
| return XSTR (x, 0) == XSTR (y, 0); |
| |
| case REG: |
| if (for_gcse) |
| return REGNO (x) == REGNO (y); |
| else |
| { |
| unsigned int regno = REGNO (y); |
| unsigned int i; |
| unsigned int endregno = END_REGNO (y); |
| |
| /* If the quantities are not the same, the expressions are not |
| equivalent. If there are and we are not to validate, they |
| are equivalent. Otherwise, ensure all regs are up-to-date. */ |
| |
| if (REG_QTY (REGNO (x)) != REG_QTY (regno)) |
| return false; |
| |
| if (! validate) |
| return true; |
| |
| for (i = regno; i < endregno; i++) |
| if (REG_IN_TABLE (i) != REG_TICK (i)) |
| return false; |
| |
| return true; |
| } |
| |
| case MEM: |
| if (for_gcse) |
| { |
| /* A volatile mem should not be considered equivalent to any |
| other. */ |
| if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y)) |
| return false; |
| |
| /* Can't merge two expressions in different alias sets, since we |
| can decide that the expression is transparent in a block when |
| it isn't, due to it being set with the different alias set. |
| |
| Also, can't merge two expressions with different MEM_ATTRS. |
| They could e.g. be two different entities allocated into the |
| same space on the stack (see e.g. PR25130). In that case, the |
| MEM addresses can be the same, even though the two MEMs are |
| absolutely not equivalent. |
| |
| But because really all MEM attributes should be the same for |
| equivalent MEMs, we just use the invariant that MEMs that have |
| the same attributes share the same mem_attrs data structure. */ |
| if (!mem_attrs_eq_p (MEM_ATTRS (x), MEM_ATTRS (y))) |
| return false; |
| |
| /* If we are handling exceptions, we cannot consider two expressions |
| with different trapping status as equivalent, because simple_mem |
| might accept one and reject the other. */ |
| if (cfun->can_throw_non_call_exceptions |
| && (MEM_NOTRAP_P (x) != MEM_NOTRAP_P (y))) |
| return false; |
| } |
| break; |
| |
| /* For commutative operations, check both orders. */ |
| case PLUS: |
| case MULT: |
| case AND: |
| case IOR: |
| case XOR: |
| case NE: |
| case EQ: |
| return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), |
| validate, for_gcse) |
| && exp_equiv_p (XEXP (x, 1), XEXP (y, 1), |
| validate, for_gcse)) |
| || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1), |
| validate, for_gcse) |
| && exp_equiv_p (XEXP (x, 1), XEXP (y, 0), |
| validate, for_gcse))); |
| |
| case ASM_OPERANDS: |
| /* We don't use the generic code below because we want to |
| disregard filename and line numbers. */ |
| |
| /* A volatile asm isn't equivalent to any other. */ |
| if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y)) |
| return false; |
| |
| if (GET_MODE (x) != GET_MODE (y) |
| || strcmp (ASM_OPERANDS_TEMPLATE (x), ASM_OPERANDS_TEMPLATE (y)) |
| || strcmp (ASM_OPERANDS_OUTPUT_CONSTRAINT (x), |
| ASM_OPERANDS_OUTPUT_CONSTRAINT (y)) |
| || ASM_OPERANDS_OUTPUT_IDX (x) != ASM_OPERANDS_OUTPUT_IDX (y) |
| || ASM_OPERANDS_INPUT_LENGTH (x) != ASM_OPERANDS_INPUT_LENGTH (y)) |
| return false; |
| |
| if (ASM_OPERANDS_INPUT_LENGTH (x)) |
| { |
| for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) |
| if (! exp_equiv_p (ASM_OPERANDS_INPUT (x, i), |
| ASM_OPERANDS_INPUT (y, i), |
| validate, for_gcse) |
| || strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i), |
| ASM_OPERANDS_INPUT_CONSTRAINT (y, i))) |
| return false; |
| } |
| |
| return true; |
| |
| default: |
| break; |
| } |
| |
| /* Compare the elements. If any pair of corresponding elements |
| fail to match, return 0 for the whole thing. */ |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| switch (fmt[i]) |
| { |
| case 'e': |
| if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), |
| validate, for_gcse)) |
| return false; |
| break; |
| |
| case 'E': |
| if (XVECLEN (x, i) != XVECLEN (y, i)) |
| return 0; |
| for (j = 0; j < XVECLEN (x, i); j++) |
| if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), |
| validate, for_gcse)) |
| return false; |
| break; |
| |
| case 's': |
| if (strcmp (XSTR (x, i), XSTR (y, i))) |
| return false; |
| break; |
| |
| case 'i': |
| if (XINT (x, i) != XINT (y, i)) |
| return false; |
| break; |
| |
| case 'w': |
| if (XWINT (x, i) != XWINT (y, i)) |
| return false; |
| break; |
| |
| case 'p': |
| if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y))) |
| return false; |
| break; |
| |
| case '0': |
| case 't': |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Subroutine of canon_reg. Pass *XLOC through canon_reg, and validate |
| the result if necessary. INSN is as for canon_reg. */ |
| |
| static void |
| validate_canon_reg (rtx *xloc, rtx_insn *insn) |
| { |
| if (*xloc) |
| { |
| rtx new_rtx = canon_reg (*xloc, insn); |
| |
| /* If replacing pseudo with hard reg or vice versa, ensure the |
| insn remains valid. Likewise if the insn has MATCH_DUPs. */ |
| gcc_assert (insn && new_rtx); |
| validate_change (insn, xloc, new_rtx, 1); |
| } |
| } |
| |
| /* Canonicalize an expression: |
| replace each register reference inside it |
| with the "oldest" equivalent register. |
| |
| If INSN is nonzero validate_change is used to ensure that INSN remains valid |
| after we make our substitution. The calls are made with IN_GROUP nonzero |
| so apply_change_group must be called upon the outermost return from this |
| function (unless INSN is zero). The result of apply_change_group can |
| generally be discarded since the changes we are making are optional. */ |
| |
| static rtx |
| canon_reg (rtx x, rtx_insn *insn) |
| { |
| int i; |
| enum rtx_code code; |
| const char *fmt; |
| |
| if (x == 0) |
| return x; |
| |
| code = GET_CODE (x); |
| switch (code) |
| { |
| case PC: |
| case CONST: |
| CASE_CONST_ANY: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case ADDR_VEC: |
| case ADDR_DIFF_VEC: |
| return x; |
| |
| case REG: |
| { |
| int first; |
| int q; |
| struct qty_table_elem *ent; |
| |
| /* Never replace a hard reg, because hard regs can appear |
| in more than one machine mode, and we must preserve the mode |
| of each occurrence. Also, some hard regs appear in |
| MEMs that are shared and mustn't be altered. Don't try to |
| replace any reg that maps to a reg of class NO_REGS. */ |
| if (REGNO (x) < FIRST_PSEUDO_REGISTER |
| || ! REGNO_QTY_VALID_P (REGNO (x))) |
| return x; |
| |
| q = REG_QTY (REGNO (x)); |
| ent = &qty_table[q]; |
| first = ent->first_reg; |
| return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first] |
| : REGNO_REG_CLASS (first) == NO_REGS ? x |
| : gen_rtx_REG (ent->mode, first)); |
| } |
| |
| default: |
| break; |
| } |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| int j; |
| |
| if (fmt[i] == 'e') |
| validate_canon_reg (&XEXP (x, i), insn); |
| else if (fmt[i] == 'E') |
| for (j = 0; j < XVECLEN (x, i); j++) |
| validate_canon_reg (&XVECEXP (x, i, j), insn); |
| } |
| |
| return x; |
| } |
| |
| /* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison |
| operation (EQ, NE, GT, etc.), follow it back through the hash table and |
| what values are being compared. |
| |
| *PARG1 and *PARG2 are updated to contain the rtx representing the values |
| actually being compared. For example, if *PARG1 was (reg:CC CC_REG) and |
| *PARG2 was (const_int 0), *PARG1 and *PARG2 will be set to the objects that |
| were compared to produce (reg:CC CC_REG). |
| |
| The return value is the comparison operator and is either the code of |
| A or the code corresponding to the inverse of the comparison. */ |
| |
| static enum rtx_code |
| find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2, |
| machine_mode *pmode1, machine_mode *pmode2) |
| { |
| rtx arg1, arg2; |
| hash_set<rtx> *visited = NULL; |
| /* Set nonzero when we find something of interest. */ |
| rtx x = NULL; |
| |
| arg1 = *parg1, arg2 = *parg2; |
| |
| /* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */ |
| |
| while (arg2 == CONST0_RTX (GET_MODE (arg1))) |
| { |
| int reverse_code = 0; |
| struct table_elt *p = 0; |
| |
| /* Remember state from previous iteration. */ |
| if (x) |
| { |
| if (!visited) |
| visited = new hash_set<rtx>; |
| visited->add (x); |
| x = 0; |
| } |
| |
| /* If arg1 is a COMPARE, extract the comparison arguments from it. */ |
| |
| if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx) |
| x = arg1; |
| |
| /* If ARG1 is a comparison operator and CODE is testing for |
| STORE_FLAG_VALUE, get the inner arguments. */ |
| |
| else if (COMPARISON_P (arg1)) |
| { |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| REAL_VALUE_TYPE fsfv; |
| #endif |
| |
| if (code == NE |
| || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT |
| && code == LT && STORE_FLAG_VALUE == -1) |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1)) |
| && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), |
| REAL_VALUE_NEGATIVE (fsfv))) |
| #endif |
| ) |
| x = arg1; |
| else if (code == EQ |
| || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT |
| && code == GE && STORE_FLAG_VALUE == -1) |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1)) |
| && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), |
| REAL_VALUE_NEGATIVE (fsfv))) |
| #endif |
| ) |
| x = arg1, reverse_code = 1; |
| } |
| |
| /* ??? We could also check for |
| |
| (ne (and (eq (...) (const_int 1))) (const_int 0)) |
| |
| and related forms, but let's wait until we see them occurring. */ |
| |
| if (x == 0) |
| /* Look up ARG1 in the hash table and see if it has an equivalence |
| that lets us see what is being compared. */ |
| p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1)); |
| if (p) |
| { |
| p = p->first_same_value; |
| |
| /* If what we compare is already known to be constant, that is as |
| good as it gets. |
| We need to break the loop in this case, because otherwise we |
| can have an infinite loop when looking at a reg that is known |
| to be a constant which is the same as a comparison of a reg |
| against zero which appears later in the insn stream, which in |
| turn is constant and the same as the comparison of the first reg |
| against zero... */ |
| if (p->is_const) |
| break; |
| } |
| |
| for (; p; p = p->next_same_value) |
| { |
| machine_mode inner_mode = GET_MODE (p->exp); |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| REAL_VALUE_TYPE fsfv; |
| #endif |
| |
| /* If the entry isn't valid, skip it. */ |
| if (! exp_equiv_p (p->exp, p->exp, 1, false)) |
| continue; |
| |
| /* If it's a comparison we've used before, skip it. */ |
| if (visited && visited->contains (p->exp)) |
| continue; |
| |
| if (GET_CODE (p->exp) == COMPARE |
| /* Another possibility is that this machine has a compare insn |
| that includes the comparison code. In that case, ARG1 would |
| be equivalent to a comparison operation that would set ARG1 to |
| either STORE_FLAG_VALUE or zero. If this is an NE operation, |
| ORIG_CODE is the actual comparison being done; if it is an EQ, |
| we must reverse ORIG_CODE. On machine with a negative value |
| for STORE_FLAG_VALUE, also look at LT and GE operations. */ |
| || ((code == NE |
| || (code == LT |
| && val_signbit_known_set_p (inner_mode, |
| STORE_FLAG_VALUE)) |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| || (code == LT |
| && SCALAR_FLOAT_MODE_P (inner_mode) |
| && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), |
| REAL_VALUE_NEGATIVE (fsfv))) |
| #endif |
| ) |
| && COMPARISON_P (p->exp))) |
| { |
| x = p->exp; |
| break; |
| } |
| else if ((code == EQ |
| || (code == GE |
| && val_signbit_known_set_p (inner_mode, |
| STORE_FLAG_VALUE)) |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| || (code == GE |
| && SCALAR_FLOAT_MODE_P (inner_mode) |
| && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)), |
| REAL_VALUE_NEGATIVE (fsfv))) |
| #endif |
| ) |
| && COMPARISON_P (p->exp)) |
| { |
| reverse_code = 1; |
| x = p->exp; |
| break; |
| } |
| |
| /* If this non-trapping address, e.g. fp + constant, the |
| equivalent is a better operand since it may let us predict |
| the value of the comparison. */ |
| else if (!rtx_addr_can_trap_p (p->exp)) |
| { |
| arg1 = p->exp; |
| continue; |
| } |
| } |
| |
| /* If we didn't find a useful equivalence for ARG1, we are done. |
| Otherwise, set up for the next iteration. */ |
| if (x == 0) |
| break; |
| |
| /* If we need to reverse the comparison, make sure that is |
| possible -- we can't necessarily infer the value of GE from LT |
| with floating-point operands. */ |
| if (reverse_code) |
| { |
| enum rtx_code reversed = reversed_comparison_code (x, NULL); |
| if (reversed == UNKNOWN) |
| break; |
| else |
| code = reversed; |
| } |
| else if (COMPARISON_P (x)) |
| code = GET_CODE (x); |
| arg1 = XEXP (x, 0), arg2 = XEXP (x, 1); |
| } |
| |
| /* Return our results. Return the modes from before fold_rtx |
| because fold_rtx might produce const_int, and then it's too late. */ |
| *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2); |
| *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0); |
| |
| if (visited) |
| delete visited; |
| return code; |
| } |
| |
| /* If X is a nontrivial arithmetic operation on an argument for which |
| a constant value can be determined, return the result of operating |
| on that value, as a constant. Otherwise, return X, possibly with |
| one or more operands changed to a forward-propagated constant. |
| |
| If X is a register whose contents are known, we do NOT return |
| those contents here; equiv_constant is called to perform that task. |
| For SUBREGs and MEMs, we do that both here and in equiv_constant. |
| |
| INSN is the insn that we may be modifying. If it is 0, make a copy |
| of X before modifying it. */ |
| |
| static rtx |
| fold_rtx (rtx x, rtx_insn *insn) |
| { |
| enum rtx_code code; |
| machine_mode mode; |
| const char *fmt; |
| int i; |
| rtx new_rtx = 0; |
| bool changed = false; |
| poly_int64 xval; |
| |
| /* Operands of X. */ |
| /* Workaround -Wmaybe-uninitialized false positive during |
| profiledbootstrap by initializing them. */ |
| rtx folded_arg0 = NULL_RTX; |
| rtx folded_arg1 = NULL_RTX; |
| |
| /* Constant equivalents of first three operands of X; |
| 0 when no such equivalent is known. */ |
| rtx const_arg0; |
| rtx const_arg1; |
| rtx const_arg2; |
| |
| /* The mode of the first operand of X. We need this for sign and zero |
| extends. */ |
| machine_mode mode_arg0; |
| |
| if (x == 0) |
| return x; |
| |
| /* Try to perform some initial simplifications on X. */ |
| code = GET_CODE (x); |
| switch (code) |
| { |
| case MEM: |
| case SUBREG: |
| /* The first operand of a SIGN/ZERO_EXTRACT has a different meaning |
| than it would in other contexts. Basically its mode does not |
| signify the size of the object read. That information is carried |
| by size operand. If we happen to have a MEM of the appropriate |
| mode in our tables with a constant value we could simplify the |
| extraction incorrectly if we allowed substitution of that value |
| for the MEM. */ |
| case ZERO_EXTRACT: |
| case SIGN_EXTRACT: |
| if ((new_rtx = equiv_constant (x)) != NULL_RTX) |
| return new_rtx; |
| return x; |
| |
| case CONST: |
| CASE_CONST_ANY: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case REG: |
| case PC: |
| /* No use simplifying an EXPR_LIST |
| since they are used only for lists of args |
| in a function call's REG_EQUAL note. */ |
| case EXPR_LIST: |
| return x; |
| |
| case ASM_OPERANDS: |
| if (insn) |
| { |
| for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) |
| validate_change (insn, &ASM_OPERANDS_INPUT (x, i), |
| fold_rtx (ASM_OPERANDS_INPUT (x, i), insn), 0); |
| } |
| return x; |
| |
| case CALL: |
| if (NO_FUNCTION_CSE && CONSTANT_P (XEXP (XEXP (x, 0), 0))) |
| return x; |
| break; |
| case VEC_SELECT: |
| { |
| rtx trueop0 = XEXP (x, 0); |
| mode = GET_MODE (trueop0); |
| rtx trueop1 = XEXP (x, 1); |
| /* If we select a low-part subreg, return that. */ |
| if (vec_series_lowpart_p (GET_MODE (x), mode, trueop1)) |
| { |
| rtx new_rtx = lowpart_subreg (GET_MODE (x), trueop0, mode); |
| if (new_rtx != NULL_RTX) |
| return new_rtx; |
| } |
| } |
| |
| /* Anything else goes through the loop below. */ |
| default: |
| break; |
| } |
| |
| mode = GET_MODE (x); |
| const_arg0 = 0; |
| const_arg1 = 0; |
| const_arg2 = 0; |
| mode_arg0 = VOIDmode; |
| |
| /* Try folding our operands. |
| Then see which ones have constant values known. */ |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| if (fmt[i] == 'e') |
| { |
| rtx folded_arg = XEXP (x, i), const_arg; |
| machine_mode mode_arg = GET_MODE (folded_arg); |
| |
| switch (GET_CODE (folded_arg)) |
| { |
| case MEM: |
| case REG: |
| case SUBREG: |
| const_arg = equiv_constant (folded_arg); |
| break; |
| |
| case CONST: |
| CASE_CONST_ANY: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| const_arg = folded_arg; |
| break; |
| |
| default: |
| folded_arg = fold_rtx (folded_arg, insn); |
| const_arg = equiv_constant (folded_arg); |
| break; |
| } |
| |
| /* For the first three operands, see if the operand |
| is constant or equivalent to a constant. */ |
| switch (i) |
| { |
| case 0: |
| folded_arg0 = folded_arg; |
| const_arg0 = const_arg; |
| mode_arg0 = mode_arg; |
| break; |
| case 1: |
| folded_arg1 = folded_arg; |
| const_arg1 = const_arg; |
| break; |
| case 2: |
| const_arg2 = const_arg; |
| break; |
| } |
| |
| /* Pick the least expensive of the argument and an equivalent constant |
| argument. */ |
| if (const_arg != 0 |
| && const_arg != folded_arg |
| && (COST_IN (const_arg, mode_arg, code, i) |
| <= COST_IN (folded_arg, mode_arg, code, i)) |
| |
| /* It's not safe to substitute the operand of a conversion |
| operator with a constant, as the conversion's identity |
| depends upon the mode of its operand. This optimization |
| is handled by the call to simplify_unary_operation. */ |
| && (GET_RTX_CLASS (code) != RTX_UNARY |
| || GET_MODE (const_arg) == mode_arg0 |
| || (code != ZERO_EXTEND |
| && code != SIGN_EXTEND |
| && code != TRUNCATE |
| && code != FLOAT_TRUNCATE |
| && code != FLOAT_EXTEND |
| && code != FLOAT |
| && code != FIX |
| && code != UNSIGNED_FLOAT |
| && code != UNSIGNED_FIX))) |
| folded_arg = const_arg; |
| |
| if (folded_arg == XEXP (x, i)) |
| continue; |
| |
| if (insn == NULL_RTX && !changed) |
| x = copy_rtx (x); |
| changed = true; |
| validate_unshare_change (insn, &XEXP (x, i), folded_arg, 1); |
| } |
| |
| if (changed) |
| { |
| /* Canonicalize X if necessary, and keep const_argN and folded_argN |
| consistent with the order in X. */ |
| if (canonicalize_change_group (insn, x)) |
| { |
| std::swap (const_arg0, const_arg1); |
| std::swap (folded_arg0, folded_arg1); |
| } |
| |
| apply_change_group (); |
| } |
| |
| /* If X is an arithmetic operation, see if we can simplify it. */ |
| |
| switch (GET_RTX_CLASS (code)) |
| { |
| case RTX_UNARY: |
| { |
| /* We can't simplify extension ops unless we know the |
| original mode. */ |
| if ((code == ZERO_EXTEND || code == SIGN_EXTEND) |
| && mode_arg0 == VOIDmode) |
| break; |
| |
| new_rtx = simplify_unary_operation (code, mode, |
| const_arg0 ? const_arg0 : folded_arg0, |
| mode_arg0); |
| } |
| break; |
| |
| case RTX_COMPARE: |
| case RTX_COMM_COMPARE: |
| /* See what items are actually being compared and set FOLDED_ARG[01] |
| to those values and CODE to the actual comparison code. If any are |
| constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't |
| do anything if both operands are already known to be constant. */ |
| |
| /* ??? Vector mode comparisons are not supported yet. */ |
| if (VECTOR_MODE_P (mode)) |
| break; |
| |
| if (const_arg0 == 0 || const_arg1 == 0) |
| { |
| struct table_elt *p0, *p1; |
| rtx true_rtx, false_rtx; |
| machine_mode mode_arg1; |
| |
| if (SCALAR_FLOAT_MODE_P (mode)) |
| { |
| #ifdef FLOAT_STORE_FLAG_VALUE |
| true_rtx = (const_double_from_real_value |
| (FLOAT_STORE_FLAG_VALUE (mode), mode)); |
| #else |
| true_rtx = NULL_RTX; |
| #endif |
| false_rtx = CONST0_RTX (mode); |
| } |
| else |
| { |
| true_rtx = const_true_rtx; |
| false_rtx = const0_rtx; |
| } |
| |
| code = find_comparison_args (code, &folded_arg0, &folded_arg1, |
| &mode_arg0, &mode_arg1); |
| |
| /* If the mode is VOIDmode or a MODE_CC mode, we don't know |
| what kinds of things are being compared, so we can't do |
| anything with this comparison. */ |
| |
| if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC) |
| break; |
| |
| const_arg0 = equiv_constant (folded_arg0); |
| const_arg1 = equiv_constant (folded_arg1); |
| |
| /* If we do not now have two constants being compared, see |
| if we can nevertheless deduce some things about the |
| comparison. */ |
| if (const_arg0 == 0 || const_arg1 == 0) |
| { |
| if (const_arg1 != NULL) |
| { |
| rtx cheapest_simplification; |
| int cheapest_cost; |
| rtx simp_result; |
| struct table_elt *p; |
| |
| /* See if we can find an equivalent of folded_arg0 |
| that gets us a cheaper expression, possibly a |
| constant through simplifications. */ |
| p = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0), |
| mode_arg0); |
| |
| if (p != NULL) |
| { |
| cheapest_simplification = x; |
| cheapest_cost = COST (x, mode); |
| |
| for (p = p->first_same_value; p != NULL; p = p->next_same_value) |
| { |
| int cost; |
| |
| /* If the entry isn't valid, skip it. */ |
| if (! exp_equiv_p (p->exp, p->exp, 1, false)) |
| continue; |
| |
| /* Try to simplify using this equivalence. */ |
| simp_result |
| = simplify_relational_operation (code, mode, |
| mode_arg0, |
| p->exp, |
| const_arg1); |
| |
| if (simp_result == NULL) |
| continue; |
| |
| cost = COST (simp_result, mode); |
| if (cost < cheapest_cost) |
| { |
| cheapest_cost = cost; |
| cheapest_simplification = simp_result; |
| } |
| } |
| |
| /* If we have a cheaper expression now, use that |
| and try folding it further, from the top. */ |
| if (cheapest_simplification != x) |
| return fold_rtx (copy_rtx (cheapest_simplification), |
| insn); |
| } |
| } |
| |
| /* See if the two operands are the same. */ |
| |
| if ((REG_P (folded_arg0) |
| && REG_P (folded_arg1) |
| && (REG_QTY (REGNO (folded_arg0)) |
| == REG_QTY (REGNO (folded_arg1)))) |
| || ((p0 = lookup (folded_arg0, |
| SAFE_HASH (folded_arg0, mode_arg0), |
| mode_arg0)) |
| && (p1 = lookup (folded_arg1, |
| SAFE_HASH (folded_arg1, mode_arg0), |
| mode_arg0)) |
| && p0->first_same_value == p1->first_same_value)) |
| folded_arg1 = folded_arg0; |
| |
| /* If FOLDED_ARG0 is a register, see if the comparison we are |
| doing now is either the same as we did before or the reverse |
| (we only check the reverse if not floating-point). */ |
| else if (REG_P (folded_arg0)) |
| { |
| int qty = REG_QTY (REGNO (folded_arg0)); |
| |
| if (REGNO_QTY_VALID_P (REGNO (folded_arg0))) |
| { |
| struct qty_table_elem *ent = &qty_table[qty]; |
| |
| if ((comparison_dominates_p (ent->comparison_code, code) |
| || (! FLOAT_MODE_P (mode_arg0) |
| && comparison_dominates_p (ent->comparison_code, |
| reverse_condition (code)))) |
| && (rtx_equal_p (ent->comparison_const, folded_arg1) |
| || (const_arg1 |
| && rtx_equal_p (ent->comparison_const, |
| const_arg1)) |
| || (REG_P (folded_arg1) |
| && (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty)))) |
| { |
| if (comparison_dominates_p (ent->comparison_code, code)) |
| { |
| if (true_rtx) |
| return true_rtx; |
| else |
| break; |
| } |
| else |
| return false_rtx; |
| } |
| } |
| } |
| } |
| } |
| |
| /* If we are comparing against zero, see if the first operand is |
| equivalent to an IOR with a constant. If so, we may be able to |
| determine the result of this comparison. */ |
| if (const_arg1 == const0_rtx && !const_arg0) |
| { |
| rtx y = lookup_as_function (folded_arg0, IOR); |
| rtx inner_const; |
| |
| if (y != 0 |
| && (inner_const = equiv_constant (XEXP (y, 1))) != 0 |
| && CONST_INT_P (inner_const) |
| && INTVAL (inner_const) != 0) |
| folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const); |
| } |
| |
| { |
| rtx op0 = const_arg0 ? const_arg0 : copy_rtx (folded_arg0); |
| rtx op1 = const_arg1 ? const_arg1 : copy_rtx (folded_arg1); |
| new_rtx = simplify_relational_operation (code, mode, mode_arg0, |
| op0, op1); |
| } |
| break; |
| |
| case RTX_BIN_ARITH: |
| case RTX_COMM_ARITH: |
| switch (code) |
| { |
| case PLUS: |
| /* If the second operand is a LABEL_REF, see if the first is a MINUS |
| with that LABEL_REF as its second operand. If so, the result is |
| the first operand of that MINUS. This handles switches with an |
| ADDR_DIFF_VEC table. */ |
| if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF) |
| { |
| rtx y |
| = GET_CODE (folded_arg0) == MINUS ? folded_arg0 |
| : lookup_as_function (folded_arg0, MINUS); |
| |
| if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF |
| && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg1)) |
| return XEXP (y, 0); |
| |
| /* Now try for a CONST of a MINUS like the above. */ |
| if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0 |
| : lookup_as_function (folded_arg0, CONST))) != 0 |
| && GET_CODE (XEXP (y, 0)) == MINUS |
| && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF |
| && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg1)) |
| return XEXP (XEXP (y, 0), 0); |
| } |
| |
| /* Likewise if the operands are in the other order. */ |
| if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF) |
| { |
| rtx y |
| = GET_CODE (folded_arg1) == MINUS ? folded_arg1 |
| : lookup_as_function (folded_arg1, MINUS); |
| |
| if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF |
| && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg0)) |
| return XEXP (y, 0); |
| |
| /* Now try for a CONST of a MINUS like the above. */ |
| if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1 |
| : lookup_as_function (folded_arg1, CONST))) != 0 |
| && GET_CODE (XEXP (y, 0)) == MINUS |
| && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF |
| && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg0)) |
| return XEXP (XEXP (y, 0), 0); |
| } |
| |
| /* If second operand is a register equivalent to a negative |
| CONST_INT, see if we can find a register equivalent to the |
| positive constant. Make a MINUS if so. Don't do this for |
| a non-negative constant since we might then alternate between |
| choosing positive and negative constants. Having the positive |
| constant previously-used is the more common case. Be sure |
| the resulting constant is non-negative; if const_arg1 were |
| the smallest negative number this would overflow: depending |
| on the mode, this would either just be the same value (and |
| hence not save anything) or be incorrect. */ |
| if (const_arg1 != 0 && CONST_INT_P (const_arg1) |
| && INTVAL (const_arg1) < 0 |
| /* This used to test |
| |
| -INTVAL (const_arg1) >= 0 |
| |
| But The Sun V5.0 compilers mis-compiled that test. So |
| instead we test for the problematic value in a more direct |
| manner and hope the Sun compilers get it correct. */ |
| && INTVAL (const_arg1) != |
| (HOST_WIDE_INT_1 << (HOST_BITS_PER_WIDE_INT - 1)) |
| && REG_P (folded_arg1)) |
| { |
| rtx new_const = GEN_INT (-INTVAL (const_arg1)); |
| struct table_elt *p |
| = lookup (new_const, SAFE_HASH (new_const, mode), mode); |
| |
| if (p) |
| for (p = p->first_same_value; p; p = p->next_same_value) |
| if (REG_P (p->exp)) |
| return simplify_gen_binary (MINUS, mode, folded_arg0, |
| canon_reg (p->exp, NULL)); |
| } |
| goto from_plus; |
| |
| case MINUS: |
| /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2). |
| If so, produce (PLUS Z C2-C). */ |
| if (const_arg1 != 0 && poly_int_rtx_p (const_arg1, &xval)) |
| { |
| rtx y = lookup_as_function (XEXP (x, 0), PLUS); |
| if (y && poly_int_rtx_p (XEXP (y, 1))) |
| return fold_rtx (plus_constant (mode, copy_rtx (y), -xval), |
| NULL); |
| } |
| |
| /* Fall through. */ |
| |
| from_plus: |
| case SMIN: case SMAX: case UMIN: case UMAX: |
| case IOR: case AND: case XOR: |
| case MULT: |
| case ASHIFT: case LSHIFTRT: case ASHIFTRT: |
| /* If we have (<op> <reg> <const_int>) for an associative OP and REG |
| is known to be of similar form, we may be able to replace the |
| operation with a combined operation. This may eliminate the |
| intermediate operation if every use is simplified in this way. |
| Note that the similar optimization done by combine.cc only works |
| if the intermediate operation's result has only one reference. */ |
| |
| if (REG_P (folded_arg0) |
| && const_arg1 && CONST_INT_P (const_arg1)) |
| { |
| int is_shift |
| = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT); |
| rtx y, inner_const, new_const; |
| rtx canon_const_arg1 = const_arg1; |
| enum rtx_code associate_code; |
| |
| if (is_shift |
| && (INTVAL (const_arg1) >= GET_MODE_UNIT_PRECISION (mode) |
| || INTVAL (const_arg1) < 0)) |
| { |
| if (SHIFT_COUNT_TRUNCATED) |
| canon_const_arg1 = gen_int_shift_amount |
| (mode, (INTVAL (const_arg1) |
| & (GET_MODE_UNIT_BITSIZE (mode) - 1))); |
| else |
| break; |
| } |
| |
| y = lookup_as_function (folded_arg0, code); |
| if (y == 0) |
| break; |
| |
| /* If we have compiled a statement like |
| "if (x == (x & mask1))", and now are looking at |
| "x & mask2", we will have a case where the first operand |
| of Y is the same as our first operand. Unless we detect |
| this case, an infinite loop will result. */ |
| if (XEXP (y, 0) == folded_arg0) |
| break; |
| |
| inner_const = equiv_constant (fold_rtx (XEXP (y, 1), 0)); |
| if (!inner_const || !CONST_INT_P (inner_const)) |
| break; |
| |
| /* Don't associate these operations if they are a PLUS with the |
| same constant and it is a power of two. These might be doable |
| with a pre- or post-increment. Similarly for two subtracts of |
| identical powers of two with post decrement. */ |
| |
| if (code == PLUS && const_arg1 == inner_const |
| && ((HAVE_PRE_INCREMENT |
| && pow2p_hwi (INTVAL (const_arg1))) |
| || (HAVE_POST_INCREMENT |
| && pow2p_hwi (INTVAL (const_arg1))) |
| || (HAVE_PRE_DECREMENT |
| && pow2p_hwi (- INTVAL (const_arg1))) |
| || (HAVE_POST_DECREMENT |
| && pow2p_hwi (- INTVAL (const_arg1))))) |
| break; |
| |
| /* ??? Vector mode shifts by scalar |
| shift operand are not supported yet. */ |
| if (is_shift && VECTOR_MODE_P (mode)) |
| break; |
| |
| if (is_shift |
| && (INTVAL (inner_const) >= GET_MODE_UNIT_PRECISION (mode) |
| || INTVAL (inner_const) < 0)) |
| { |
| if (SHIFT_COUNT_TRUNCATED) |
| inner_const = gen_int_shift_amount |
| (mode, (INTVAL (inner_const) |
| & (GET_MODE_UNIT_BITSIZE (mode) - 1))); |
| else |
| break; |
| } |
| |
| /* Compute the code used to compose the constants. For example, |
| A-C1-C2 is A-(C1 + C2), so if CODE == MINUS, we want PLUS. */ |
| |
| associate_code = (is_shift || code == MINUS ? PLUS : code); |
| |
| new_const = simplify_binary_operation (associate_code, mode, |
| canon_const_arg1, |
| inner_const); |
| |
| if (new_const == 0) |
| break; |
| |
| /* If we are associating shift operations, don't let this |
| produce a shift of the size of the object or larger. |
| This could occur when we follow a sign-extend by a right |
| shift on a machine that does a sign-extend as a pair |
| of shifts. */ |
| |
| if (is_shift |
| && CONST_INT_P (new_const) |
| && INTVAL (new_const) >= GET_MODE_UNIT_PRECISION (mode)) |
| { |
| /* As an exception, we can turn an ASHIFTRT of this |
| form into a shift of the number of bits - 1. */ |
| if (code == ASHIFTRT) |
| new_const = gen_int_shift_amount |
| (mode, GET_MODE_UNIT_BITSIZE (mode) - 1); |
| else if (!side_effects_p (XEXP (y, 0))) |
| return CONST0_RTX (mode); |
| else |
| break; |
| } |
| |
| y = copy_rtx (XEXP (y, 0)); |
| |
| /* If Y contains our first operand (the most common way this |
| can happen is if Y is a MEM), we would do into an infinite |
| loop if we tried to fold it. So don't in that case. */ |
| |
| if (! reg_mentioned_p (folded_arg0, y)) |
| y = fold_rtx (y, insn); |
| |
| return simplify_gen_binary (code, mode, y, new_const); |
| } |
| break; |
| |
| case DIV: case UDIV: |
| /* ??? The associative optimization performed immediately above is |
| also possible for DIV and UDIV using associate_code of MULT. |
| However, we would need extra code to verify that the |
| multiplication does not overflow, that is, there is no overflow |
| in the calculation of new_const. */ |
| break; |
| |
| default: |
| break; |
| } |
| |
| new_rtx = simplify_binary_operation (code, mode, |
| const_arg0 ? const_arg0 : folded_arg0, |
| const_arg1 ? const_arg1 : folded_arg1); |
| break; |
| |
| case RTX_OBJ: |
| /* (lo_sum (high X) X) is simply X. */ |
| if (code == LO_SUM && const_arg0 != 0 |
| && GET_CODE (const_arg0) == HIGH |
| && rtx_equal_p (XEXP (const_arg0, 0), const_arg1)) |
| return const_arg1; |
| break; |
| |
| case RTX_TERNARY: |
| case RTX_BITFIELD_OPS: |
| new_rtx = simplify_ternary_operation (code, mode, mode_arg0, |
| const_arg0 ? const_arg0 : folded_arg0, |
| const_arg1 ? const_arg1 : folded_arg1, |
| const_arg2 ? const_arg2 : XEXP (x, 2)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return new_rtx ? new_rtx : x; |
| } |
| |
| /* Return a constant value currently equivalent to X. |
| Return 0 if we don't know one. */ |
| |
| static rtx |
| equiv_constant (rtx x) |
| { |
| if (REG_P (x) |
| && REGNO_QTY_VALID_P (REGNO (x))) |
| { |
| int x_q = REG_QTY (REGNO (x)); |
| struct qty_table_elem *x_ent = &qty_table[x_q]; |
| |
| if (x_ent->const_rtx) |
| x = gen_lowpart (GET_MODE (x), x_ent->const_rtx); |
| } |
| |
| if (x == 0 || CONSTANT_P (x)) |
| return x; |
| |
| if (GET_CODE (x) == SUBREG) |
| { |
| machine_mode mode = GET_MODE (x); |
| machine_mode imode = GET_MODE (SUBREG_REG (x)); |
| rtx new_rtx; |
| |
| /* See if we previously assigned a constant value to this SUBREG. */ |
| if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0 |
| || (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0 |
| || (NUM_POLY_INT_COEFFS > 1 |
| && (new_rtx = lookup_as_function (x, CONST_POLY_INT)) != 0) |
| || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0 |
| || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0) |
| return new_rtx; |
| |
| /* If we didn't and if doing so makes sense, see if we previously |
| assigned a constant value to the enclosing word mode SUBREG. */ |
| if (known_lt (GET_MODE_SIZE (mode), UNITS_PER_WORD) |
| && known_lt (UNITS_PER_WORD, GET_MODE_SIZE (imode))) |
| { |
| poly_int64 byte = (SUBREG_BYTE (x) |
| - subreg_lowpart_offset (mode, word_mode)); |
| if (known_ge (byte, 0) && multiple_p (byte, UNITS_PER_WORD)) |
| { |
| rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte); |
| new_rtx = lookup_as_function (y, CONST_INT); |
| if (new_rtx) |
| return gen_lowpart (mode, new_rtx); |
| } |
| } |
| |
| /* Otherwise see if we already have a constant for the inner REG, |
| and if that is enough to calculate an equivalent constant for |
| the subreg. Note that the upper bits of paradoxical subregs |
| are undefined, so they cannot be said to equal anything. */ |
| if (REG_P (SUBREG_REG (x)) |
| && !paradoxical_subreg_p (x) |
| && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0) |
| return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x)); |
| |
| return 0; |
| } |
| |
| /* If X is a MEM, see if it is a constant-pool reference, or look it up in |
| the hash table in case its value was seen before. */ |
| |
| if (MEM_P (x)) |
| { |
| struct table_elt *elt; |
| |
| x = avoid_constant_pool_reference (x); |
| if (CONSTANT_P (x)) |
| return x; |
| |
| elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x)); |
| if (elt == 0) |
| return 0; |
| |
| for (elt = elt->first_same_value; elt; elt = elt->next_same_value) |
| if (elt->is_const && CONSTANT_P (elt->exp)) |
| return elt->exp; |
| } |
| |
| return 0; |
| } |
| |
| /* Given INSN, a jump insn, TAKEN indicates if we are following the |
| "taken" branch. |
| |
| In certain cases, this can cause us to add an equivalence. For example, |
| if we are following the taken case of |
| if (i == 2) |
| we can add the fact that `i' and '2' are now equivalent. |
| |
| In any case, we can record that this comparison was passed. If the same |
| comparison is seen later, we will know its value. */ |
| |
| static void |
| record_jump_equiv (rtx_insn *insn, bool taken) |
| { |
| int cond_known_true; |
| rtx op0, op1; |
| rtx set; |
| machine_mode mode, mode0, mode1; |
| enum rtx_code code; |
| |
| /* Ensure this is the right kind of insn. */ |
| gcc_assert (any_condjump_p (insn)); |
| |
| set = pc_set (insn); |
| |
| /* See if this jump condition is known true or false. */ |
| if (taken) |
| cond_known_true = (XEXP (SET_SRC (set), 2) == pc_rtx); |
| else |
| cond_known_true = (XEXP (SET_SRC (set), 1) == pc_rtx); |
| |
| /* Get the type of comparison being done and the operands being compared. |
| If we had to reverse a non-equality condition, record that fact so we |
| know that it isn't valid for floating-point. */ |
| code = GET_CODE (XEXP (SET_SRC (set), 0)); |
| op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn); |
| op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn); |
| |
| /* If fold_rtx returns NULL_RTX, there's nothing to record. */ |
| if (op0 == NULL_RTX || op1 == NULL_RTX) |
| return; |
| |
| code = find_comparison_args (code, &op0, &op1, &mode0, &mode1); |
| if (! cond_known_true) |
| { |
| code = reversed_comparison_code_parts (code, op0, op1, insn); |
| |
| /* Don't remember if we can't find the inverse. */ |
| if (code == UNKNOWN) |
| return; |
| } |
| |
| /* The mode is the mode of the non-constant. */ |
| mode = mode0; |
| if (mode1 != VOIDmode) |
| mode = mode1; |
| |
| record_jump_cond (code, mode, op0, op1); |
| } |
| |
| /* Yet another form of subreg creation. In this case, we want something in |
| MODE, and we should assume OP has MODE iff it is naturally modeless. */ |
| |
| static rtx |
| record_jump_cond_subreg (machine_mode mode, rtx op) |
| { |
| machine_mode op_mode = GET_MODE (op); |
| if (op_mode == mode || op_mode == VOIDmode) |
| return op; |
| return lowpart_subreg (mode, op, op_mode); |
| } |
| |
| /* We know that comparison CODE applied to OP0 and OP1 in MODE is true. |
| Make any useful entries we can with that information. Called from |
| above function and called recursively. */ |
| |
| static void |
| record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0, rtx op1) |
| { |
| unsigned op0_hash, op1_hash; |
| int op0_in_memory, op1_in_memory; |
| struct table_elt *op0_elt, *op1_elt; |
| |
| /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG, |
| we know that they are also equal in the smaller mode (this is also |
| true for all smaller modes whether or not there is a SUBREG, but |
| is not worth testing for with no SUBREG). */ |
| |
| /* Note that GET_MODE (op0) may not equal MODE. */ |
| if (code == EQ && paradoxical_subreg_p (op0)) |
| { |
| machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); |
| rtx tem = record_jump_cond_subreg (inner_mode, op1); |
| if (tem) |
| record_jump_cond (code, mode, SUBREG_REG (op0), tem); |
| } |
| |
| if (code == EQ && paradoxical_subreg_p (op1)) |
| { |
| machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); |
| rtx tem = record_jump_cond_subreg (inner_mode, op0); |
| if (tem) |
| record_jump_cond (code, mode, SUBREG_REG (op1), tem); |
| } |
| |
| /* Similarly, if this is an NE comparison, and either is a SUBREG |
| making a smaller mode, we know the whole thing is also NE. */ |
| |
| /* Note that GET_MODE (op0) may not equal MODE; |
| if we test MODE instead, we can get an infinite recursion |
| alternating between two modes each wider than MODE. */ |
| |
| if (code == NE |
| && partial_subreg_p (op0) |
| && subreg_lowpart_p (op0)) |
| { |
| machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); |
| rtx tem = record_jump_cond_subreg (inner_mode, op1); |
| if (tem) |
| record_jump_cond (code, mode, SUBREG_REG (op0), tem); |
| } |
| |
| if (code == NE |
| && partial_subreg_p (op1) |
| && subreg_lowpart_p (op1)) |
| { |
| machine_mode inner_mode = GET_MODE (SUBREG_REG (op1)); |
| rtx tem = record_jump_cond_subreg (inner_mode, op0); |
| if (tem) |
| record_jump_cond (code, mode, SUBREG_REG (op1), tem); |
| } |
| |
| /* Hash both operands. */ |
| |
| do_not_record = 0; |
| hash_arg_in_memory = 0; |
| op0_hash = HASH (op0, mode); |
| op0_in_memory = hash_arg_in_memory; |
| |
| if (do_not_record) |
| return; |
| |
| do_not_record = 0; |
| hash_arg_in_memory = 0; |
| op1_hash = HASH (op1, mode); |
| op1_in_memory = hash_arg_in_memory; |
| |
| if (do_not_record) |
| return; |
| |
| /* Look up both operands. */ |
| op0_elt = lookup (op0, op0_hash, mode); |
| op1_elt = lookup (op1, op1_hash, mode); |
| |
| /* If both operands are already equivalent or if they are not in the |
| table but are identical, do nothing. */ |
| if ((op0_elt != 0 && op1_elt != 0 |
| && op0_elt->first_same_value == op1_elt->first_same_value) |
| || op0 == op1 || rtx_equal_p (op0, op1)) |
| return; |
| |
| /* If we aren't setting two things equal all we can do is save this |
| comparison. Similarly if this is floating-point. In the latter |
| case, OP1 might be zero and both -0.0 and 0.0 are equal to it. |
| If we record the equality, we might inadvertently delete code |
| whose intent was to change -0 to +0. */ |
| |
| if (code != EQ || FLOAT_MODE_P (GET_MODE (op0))) |
| { |
| struct qty_table_elem *ent; |
| int qty; |
| |
| /* If OP0 is not a register, or if OP1 is neither a register |
| or constant, we can't do anything. */ |
| |
| if (!REG_P (op1)) |
| op1 = equiv_constant (op1); |
| |
| if (!REG_P (op0) || op1 == 0) |
| return; |
| |
| /* Put OP0 in the hash table if it isn't already. This gives it a |
| new quantity number. */ |
| if (op0_elt == 0) |
| { |
| if (insert_regs (op0, NULL, false)) |
| { |
| rehash_using_reg (op0); |
| op0_hash = HASH (op0, mode); |
| |
| /* If OP0 is contained in OP1, this changes its hash code |
| as well. Faster to rehash than to check, except |
| for the simple case of a constant. */ |
| if (! CONSTANT_P (op1)) |
| op1_hash = HASH (op1,mode); |
| } |
| |
| op0_elt = insert (op0, NULL, op0_hash, mode); |
| op0_elt->in_memory = op0_in_memory; |
| } |
| |
| qty = REG_QTY (REGNO (op0)); |
| ent = &qty_table[qty]; |
| |
| ent->comparison_code = code; |
| if (REG_P (op1)) |
| { |
| /* Look it up again--in case op0 and op1 are the same. */ |
| op1_elt = lookup (op1, op1_hash, mode); |
| |
| /* Put OP1 in the hash table so it gets a new quantity number. */ |
| if (op1_elt == 0) |
| { |
| if (insert_regs (op1, NULL, false)) |
| { |
| rehash_using_reg (op1); |
| op1_hash = HASH (op1, mode); |
| } |
| |
| op1_elt = insert (op1, NULL, op1_hash, mode); |
| op1_elt->in_memory = op1_in_memory; |
| } |
| |
| ent->comparison_const = NULL_RTX; |
| ent->comparison_qty = REG_QTY (REGNO (op1)); |
| } |
| else |
| { |
| ent->comparison_const = op1; |
| ent->comparison_qty = INT_MIN; |
| } |
| |
| return; |
| } |
| |
| /* If either side is still missing an equivalence, make it now, |
| then merge the equivalences. */ |
| |
| if (op0_elt == 0) |
| { |
| if (insert_regs (op0, NULL, false)) |
| { |
| rehash_using_reg (op0); |
| op0_hash = HASH (op0, mode); |
| } |
| |
| op0_elt = insert (op0, NULL, op0_hash, mode); |
| op0_elt->in_memory = op0_in_memory; |
| } |
| |
| if (op1_elt == 0) |
| { |
| if (insert_regs (op1, NULL, false)) |
| { |
| rehash_using_reg (op1); |
| op1_hash = HASH (op1, mode); |
| } |
| |
| op1_elt = insert (op1, NULL, op1_hash, mode); |
| op1_elt->in_memory = op1_in_memory; |
| } |
| |
| merge_equiv_classes (op0_elt, op1_elt); |
| } |
| |
| /* CSE processing for one instruction. |
| |
| Most "true" common subexpressions are mostly optimized away in GIMPLE, |
| but the few that "leak through" are cleaned up by cse_insn, and complex |
| addressing modes are often formed here. |
| |
| The main function is cse_insn, and between here and that function |
| a couple of helper functions is defined to keep the size of cse_insn |
| within reasonable proportions. |
| |
| Data is shared between the main and helper functions via STRUCT SET, |
| that contains all data related for every set in the instruction that |
| is being processed. |
| |
| Note that cse_main processes all sets in the instruction. Most |
| passes in GCC only process simple SET insns or single_set insns, but |
| CSE processes insns with multiple sets as well. */ |
| |
| /* Data on one SET contained in the instruction. */ |
| |
| struct set |
| { |
| /* The SET rtx itself. */ |
| rtx rtl; |
| /* The SET_SRC of the rtx (the original value, if it is changing). */ |
| rtx src; |
| /* The hash-table element for the SET_SRC of the SET. */ |
| struct table_elt *src_elt; |
| /* Hash value for the SET_SRC. */ |
| unsigned src_hash; |
| /* Hash value for the SET_DEST. */ |
| unsigned dest_hash; |
| /* The SET_DEST, with SUBREG, etc., stripped. */ |
| rtx inner_dest; |
| /* Original machine mode, in case it becomes a CONST_INT. */ |
| ENUM_BITFIELD(machine_mode) mode : MACHINE_MODE_BITSIZE; |
| /* Nonzero if the SET_SRC is in memory. */ |
| unsigned int src_in_memory : 1; |
| /* Nonzero if the SET_SRC contains something |
| whose value cannot be predicted and understood. */ |
| unsigned int src_volatile : 1; |
| /* Nonzero if RTL is an artifical set that has been created to describe |
| part of an insn's effect. Zero means that RTL appears directly in |
| the insn pattern. */ |
| unsigned int is_fake_set : 1; |
| /* Hash value of constant equivalent for SET_SRC. */ |
| unsigned src_const_hash; |
| /* A constant equivalent for SET_SRC, if any. */ |
| rtx src_const; |
| /* Table entry for constant equivalent for SET_SRC, if any. */ |
| struct table_elt *src_const_elt; |
| /* Table entry for the destination address. */ |
| struct table_elt *dest_addr_elt; |
| }; |
| |
| /* Special handling for (set REG0 REG1) where REG0 is the |
| "cheapest", cheaper than REG1. After cse, REG1 will probably not |
| be used in the sequel, so (if easily done) change this insn to |
| (set REG1 REG0) and replace REG1 with REG0 in the previous insn |
| that computed their value. Then REG1 will become a dead store |
| and won't cloud the situation for later optimizations. |
| |
| Do not make this change if REG1 is a hard register, because it will |
| then be used in the sequel and we may be changing a two-operand insn |
| into a three-operand insn. |
| |
| This is the last transformation that cse_insn will try to do. */ |
| |
| static void |
| try_back_substitute_reg (rtx set, rtx_insn *insn) |
| { |
| rtx dest = SET_DEST (set); |
| rtx src = SET_SRC (set); |
| |
| if (REG_P (dest) |
| && REG_P (src) && ! HARD_REGISTER_P (src) |
| && REGNO_QTY_VALID_P (REGNO (src))) |
| { |
| int src_q = REG_QTY (REGNO (src)); |
| struct qty_table_elem *src_ent = &qty_table[src_q]; |
| |
| if (src_ent->first_reg == REGNO (dest)) |
| { |
| /* Scan for the previous nonnote insn, but stop at a basic |
| block boundary. */ |
| rtx_insn *prev = insn; |
| rtx_insn *bb_head = BB_HEAD (BLOCK_FOR_INSN (insn)); |
| do |
| { |
| prev = PREV_INSN (prev); |
| } |
| while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev))); |
| |
| /* Do not swap the registers around if the previous instruction |
| attaches a REG_EQUIV note to REG1. |
| |
| ??? It's not entirely clear whether we can transfer a REG_EQUIV |
| from the pseudo that originally shadowed an incoming argument |
| to another register. Some uses of REG_EQUIV might rely on it |
| being attached to REG1 rather than REG2. |
| |
| This section previously turned the REG_EQUIV into a REG_EQUAL |
| note. We cannot do that because REG_EQUIV may provide an |
| uninitialized stack slot when REG_PARM_STACK_SPACE is used. */ |
| if (NONJUMP_INSN_P (prev) |
| && GET_CODE (PATTERN (prev)) == SET |
| && SET_DEST (PATTERN (prev)) == src |
| && ! find_reg_note (prev, REG_EQUIV, NULL_RTX)) |
| { |
| rtx note; |
| |
| validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1); |
| validate_change (insn, &SET_DEST (set), src, 1); |
| validate_change (insn, &SET_SRC (set), dest, 1); |
| apply_change_group (); |
| |
| /* If INSN has a REG_EQUAL note, and this note mentions |
| REG0, then we must delete it, because the value in |
| REG0 has changed. If the note's value is REG1, we must |
| also delete it because that is now this insn's dest. */ |
| note = find_reg_note (insn, REG_EQUAL, NULL_RTX); |
| if (note != 0 |
| && (reg_mentioned_p (dest, XEXP (note, 0)) |
| || rtx_equal_p (src, XEXP (note, 0)))) |
| remove_note (insn, note); |
| |
| /* If INSN has a REG_ARGS_SIZE note, move it to PREV. */ |
| note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); |
| if (note != 0) |
| { |
| remove_note (insn, note); |
| gcc_assert (!find_reg_note (prev, REG_ARGS_SIZE, NULL_RTX)); |
| set_unique_reg_note (prev, REG_ARGS_SIZE, XEXP (note, 0)); |
| } |
| } |
| } |
| } |
| } |
| |
| /* Add an entry containing RTL X into SETS. IS_FAKE_SET is true if X is |
| an artifical set that has been created to describe part of an insn's |
| effect. */ |
| static inline void |
| add_to_set (vec<struct set> *sets, rtx x, bool is_fake_set) |
| { |
| struct set entry = {}; |
| entry.rtl = x; |
| entry.is_fake_set = is_fake_set; |
| sets->safe_push (entry); |
| } |
| |
| /* Record all the SETs in this instruction into SETS_PTR, |
| and return the number of recorded sets. */ |
| static int |
| find_sets_in_insn (rtx_insn *insn, vec<struct set> *psets) |
| { |
| rtx x = PATTERN (insn); |
| |
| if (GET_CODE (x) == SET) |
| { |
| /* Ignore SETs that are unconditional jumps. |
| They never need cse processing, so this does not hurt. |
| The reason is not efficiency but rather |
| so that we can test at the end for instructions |
| that have been simplified to unconditional jumps |
| and not be misled by unchanged instructions |
| that were unconditional jumps to begin with. */ |
| if (SET_DEST (x) == pc_rtx |
| && GET_CODE (SET_SRC (x)) == LABEL_REF) |
| ; |
| /* Don't count call-insns, (set (reg 0) (call ...)), as a set. |
| The hard function value register is used only once, to copy to |
| someplace else, so it isn't worth cse'ing. */ |
| else if (GET_CODE (SET_SRC (x)) == CALL) |
| ; |
| else if (GET_CODE (SET_SRC (x)) == CONST_VECTOR |
| && GET_MODE_CLASS (GET_MODE (SET_SRC (x))) != MODE_VECTOR_BOOL |
| /* Prevent duplicates from being generated if the type is a V1 |
| type and a subreg. Folding this will result in the same |
| element as folding x itself. */ |
| && !(SUBREG_P (SET_DEST (x)) |
| && known_eq (GET_MODE_NUNITS (GET_MODE (SET_SRC (x))), 1))) |
| { |
| /* First register the vector itself. */ |
| add_to_set (psets, x, false); |
| rtx src = SET_SRC (x); |
| /* Go over the constants of the CONST_VECTOR in forward order, to |
| put them in the same order in the SETS array. */ |
| for (unsigned i = 0; i < const_vector_encoded_nelts (src) ; i++) |
| { |
| /* These are templates and don't actually get emitted but are |
| used to tell CSE how to get to a particular constant. */ |
| rtx y = simplify_gen_vec_select (SET_DEST (x), i); |
| gcc_assert (y); |
| rtx set = gen_rtx_SET (y, CONST_VECTOR_ELT (src, i)); |
| add_to_set (psets, set, true); |
| } |
| } |
| else |
| add_to_set (psets, x, false); |
| } |
| else if (GET_CODE (x) == PARALLEL) |
| { |
| int i, lim = XVECLEN (x, 0); |
| |
| /* Go over the expressions of the PARALLEL in forward order, to |
| put them in the same order in the SETS array. */ |
| for (i = 0; i < lim; i++) |
| { |
| rtx y = XVECEXP (x, 0, i); |
| if (GET_CODE (y) == SET) |
| { |
| /* As above, we ignore unconditional jumps and call-insns and |
| ignore the result of apply_change_group. */ |
| if (SET_DEST (y) == pc_rtx |
| && GET_CODE (SET_SRC (y)) == LABEL_REF) |
| ; |
| else if (GET_CODE (SET_SRC (y)) == CALL) |
| ; |
| else |
| add_to_set (psets, y, false); |
| } |
| } |
| } |
| |
| return psets->length (); |
| } |
| |
| /* Subroutine of canonicalize_insn. X is an ASM_OPERANDS in INSN. */ |
| |
| static void |
| canon_asm_operands (rtx x, rtx_insn *insn) |
| { |
| for (int i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) |
| { |
| rtx input = ASM_OPERANDS_INPUT (x, i); |
| if (!(REG_P (input) && HARD_REGISTER_P (input))) |
| { |
| input = canon_reg (input, insn); |
| validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1); |
| } |
| } |
| } |
| |
| /* Where possible, substitute every register reference in the N_SETS |
| number of SETS in INSN with the canonical register. |
| |
| Register canonicalization propagatest the earliest register (i.e. |
| one that is set before INSN) with the same value. This is a very |
| useful, simple form of CSE, to clean up warts from expanding GIMPLE |
| to RTL. For instance, a CONST for an address is usually expanded |
| multiple times to loads into different registers, thus creating many |
| subexpressions of the form: |
| |
| (set (reg1) (some_const)) |
| (set (mem (... reg1 ...) (thing))) |
| (set (reg2) (some_const)) |
| (set (mem (... reg2 ...) (thing))) |
| |
| After canonicalizing, the code takes the following form: |
| |
| (set (reg1) (some_const)) |
| (set (mem (... reg1 ...) (thing))) |
| (set (reg2) (some_const)) |
| (set (mem (... reg1 ...) (thing))) |
| |
| The set to reg2 is now trivially dead, and the memory reference (or |
| address, or whatever) may be a candidate for further CSEing. |
| |
| In this function, the result of apply_change_group can be ignored; |
| see canon_reg. */ |
| |
| static void |
| canonicalize_insn (rtx_insn *insn, vec<struct set> *psets) |
| { |
| vec<struct set> sets = *psets; |
| int n_sets = sets.length (); |
| rtx tem; |
| rtx x = PATTERN (insn); |
| int i; |
| |
| if (CALL_P (insn)) |
| { |
| for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) |
| if (GET_CODE (XEXP (tem, 0)) != SET) |
| XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn); |
| } |
| |
| if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL) |
| { |
| canon_reg (SET_SRC (x), insn); |
| apply_change_group (); |
| fold_rtx (SET_SRC (x), insn); |
| } |
| else if (GET_CODE (x) == CLOBBER) |
| { |
| /* If we clobber memory, canon the address. |
| This does nothing when a register is clobbered |
| because we have already invalidated the reg. */ |
| if (MEM_P (XEXP (x, 0))) |
| canon_reg (XEXP (x, 0), insn); |
| } |
| else if (GET_CODE (x) == USE |
| && ! (REG_P (XEXP (x, 0)) |
| && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) |
| /* Canonicalize a USE of a pseudo register or memory location. */ |
| canon_reg (x, insn); |
| else if (GET_CODE (x) == ASM_OPERANDS) |
| canon_asm_operands (x, insn); |
| else if (GET_CODE (x) == CALL) |
| { |
| canon_reg (x, insn); |
| apply_change_group (); |
| fold_rtx (x, insn); |
| } |
| else if (DEBUG_INSN_P (insn)) |
| canon_reg (PATTERN (insn), insn); |
| else if (GET_CODE (x) == PARALLEL) |
| { |
| for (i = XVECLEN (x, 0) - 1; i >= 0; i--) |
| { |
| rtx y = XVECEXP (x, 0, i); |
| if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL) |
| { |
| canon_reg (SET_SRC (y), insn); |
| apply_change_group (); |
| fold_rtx (SET_SRC (y), insn); |
| } |
| else if (GET_CODE (y) == CLOBBER) |
| { |
| if (MEM_P (XEXP (y, 0))) |
| canon_reg (XEXP (y, 0), insn); |
| } |
| else if (GET_CODE (y) == USE |
| && ! (REG_P (XEXP (y, 0)) |
| && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) |
| canon_reg (y, insn); |
| else if (GET_CODE (y) == ASM_OPERANDS) |
| canon_asm_operands (y, insn); |
| else if (GET_CODE (y) == CALL) |
| { |
| canon_reg (y, insn); |
| apply_change_group (); |
| fold_rtx (y, insn); |
| } |
| } |
| } |
| |
| if (n_sets == 1 && REG_NOTES (insn) != 0 |
| && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) |
| { |
| /* We potentially will process this insn many times. Therefore, |
| drop the REG_EQUAL note if it is equal to the SET_SRC of the |
| unique set in INSN. |
| |
| Do not do so if the REG_EQUAL note is for a STRICT_LOW_PART, |
| because cse_insn handles those specially. */ |
| if (GET_CODE (SET_DEST (sets[0].rtl)) != STRICT_LOW_PART |
| && rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))) |
| remove_note (insn, tem); |
| else |
| { |
| canon_reg (XEXP (tem, 0), insn); |
| apply_change_group (); |
| XEXP (tem, 0) = fold_rtx (XEXP (tem, 0), insn); |
| df_notes_rescan (insn); |
| } |
| } |
| |
| /* Canonicalize sources and addresses of destinations. |
| We do this in a separate pass to avoid problems when a MATCH_DUP is |
| present in the insn pattern. In that case, we want to ensure that |
| we don't break the duplicate nature of the pattern. So we will replace |
| both operands at the same time. Otherwise, we would fail to find an |
| equivalent substitution in the loop calling validate_change below. |
| |
| We used to suppress canonicalization of DEST if it appears in SRC, |
| but we don't do this any more. */ |
| |
| for (i = 0; i < n_sets; i++) |
| { |
| rtx dest = SET_DEST (sets[i].rtl); |
| rtx src = SET_SRC (sets[i].rtl); |
| rtx new_rtx = canon_reg (src, insn); |
| |
| validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); |
| |
| if (GET_CODE (dest) == ZERO_EXTRACT) |
| { |
| validate_change (insn, &XEXP (dest, 1), |
| canon_reg (XEXP (dest, 1), insn), 1); |
| validate_change (insn, &XEXP (dest, 2), |
| canon_reg (XEXP (dest, 2), insn), 1); |
| } |
| |
| while (GET_CODE (dest) == SUBREG |
| || GET_CODE (dest) == ZERO_EXTRACT |
| || GET_CODE (dest) == STRICT_LOW_PART) |
| dest = XEXP (dest, 0); |
| |
| if (MEM_P (dest)) |
| canon_reg (dest, insn); |
| } |
| |
| /* Now that we have done all the replacements, we can apply the change |
| group and see if they all work. Note that this will cause some |
| canonicalizations that would have worked individually not to be applied |
| because some other canonicalization didn't work, but this should not |
| occur often. |
| |
| The result of apply_change_group can be ignored; see canon_reg. */ |
| |
| apply_change_group (); |
| } |
| |
| /* Main function of CSE. |
| First simplify sources and addresses of all assignments |
| in the instruction, using previously-computed equivalents values. |
| Then install the new sources and destinations in the table |
| of available values. */ |
| |
| static void |
| cse_insn (rtx_insn *insn) |
| { |
| rtx x = PATTERN (insn); |
| int i; |
| rtx tem; |
| int n_sets = 0; |
| |
| rtx src_eqv = 0; |
| struct table_elt *src_eqv_elt = 0; |
| int src_eqv_volatile = 0; |
| int src_eqv_in_memory = 0; |
| unsigned src_eqv_hash = 0; |
| |
| this_insn = insn; |
| |
| /* Find all regs explicitly clobbered in this insn, |
| to ensure they are not replaced with any other regs |
| elsewhere in this insn. */ |
| invalidate_from_sets_and_clobbers (insn); |
| |
| /* Record all the SETs in this instruction. */ |
| auto_vec<struct set, 8> sets; |
| n_sets = find_sets_in_insn (insn, (vec<struct set>*)&sets); |
| |
| /* Substitute the canonical register where possible. */ |
| canonicalize_insn (insn, (vec<struct set>*)&sets); |
| |
| /* If this insn has a REG_EQUAL note, store the equivalent value in SRC_EQV, |
| if different, or if the DEST is a STRICT_LOW_PART/ZERO_EXTRACT. The |
| latter condition is necessary because SRC_EQV is handled specially for |
| this case, and if it isn't set, then there will be no equivalence |
| for the destination. */ |
| if (n_sets == 1 && REG_NOTES (insn) != 0 |
| && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0) |
| { |
| |
| if (GET_CODE (SET_DEST (sets[0].rtl)) != ZERO_EXTRACT |
| && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)) |
| || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART)) |
| src_eqv = copy_rtx (XEXP (tem, 0)); |
| /* If DEST is of the form ZERO_EXTACT, as in: |
| (set (zero_extract:SI (reg:SI 119) |
| (const_int 16 [0x10]) |
| (const_int 16 [0x10])) |
| (const_int 51154 [0xc7d2])) |
| REG_EQUAL note will specify the value of register (reg:SI 119) at this |
| point. Note that this is different from SRC_EQV. We can however |
| calculate SRC_EQV with the position and width of ZERO_EXTRACT. */ |
| else if (GET_CODE (SET_DEST (sets[0].rtl)) == ZERO_EXTRACT |
| && CONST_INT_P (XEXP (tem, 0)) |
| && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 1)) |
| && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 2))) |
| { |
| rtx dest_reg = XEXP (SET_DEST (sets[0].rtl), 0); |
| /* This is the mode of XEXP (tem, 0) as well. */ |
| scalar_int_mode dest_mode |
| = as_a <scalar_int_mode> (GET_MODE (dest_reg)); |
| rtx width = XEXP (SET_DEST (sets[0].rtl), 1); |
| rtx pos = XEXP (SET_DEST (sets[0].rtl), 2); |
| HOST_WIDE_INT val = INTVAL (XEXP (tem, 0)); |
| HOST_WIDE_INT mask; |
| unsigned int shift; |
| if (BITS_BIG_ENDIAN) |
| shift = (GET_MODE_PRECISION (dest_mode) |
| - INTVAL (pos) - INTVAL (width)); |
| else |
| shift = INTVAL (pos); |
| if (INTVAL (width) == HOST_BITS_PER_WIDE_INT) |
| mask = HOST_WIDE_INT_M1; |
| else |
| mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1; |
| val = (val >> shift) & mask; |
| src_eqv = GEN_INT (val); |
| } |
| } |
| |
| /* Set sets[i].src_elt to the class each source belongs to. |
| Detect assignments from or to volatile things |
| and set set[i] to zero so they will be ignored |
| in the rest of this function. |
| |
| Nothing in this loop changes the hash table or the register chains. */ |
| |
| for (i = 0; i < n_sets; i++) |
| { |
| bool repeat = false; |
| bool noop_insn = false; |
| rtx src, dest; |
| rtx src_folded; |
| struct table_elt *elt = 0, *p; |
| machine_mode mode; |
| rtx src_eqv_here; |
| rtx src_const = 0; |
| rtx src_related = 0; |
| rtx dest_related = 0; |
| bool src_related_is_const_anchor = false; |
| struct table_elt *src_const_elt = 0; |
| int src_cost = MAX_COST; |
| int src_eqv_cost = MAX_COST; |
| int src_folded_cost = MAX_COST; |
| int src_related_cost = MAX_COST; |
| int src_elt_cost = MAX_COST; |
| int src_regcost = MAX_COST; |
| int src_eqv_regcost = MAX_COST; |
| int src_folded_regcost = MAX_COST; |
| int src_related_regcost = MAX_COST; |
| int src_elt_regcost = MAX_COST; |
| scalar_int_mode int_mode; |
| bool is_fake_set = sets[i].is_fake_set; |
| |
| dest = SET_DEST (sets[i].rtl); |
| src = SET_SRC (sets[i].rtl); |
| |
| /* If SRC is a constant that has no machine mode, |
| hash it with the destination's machine mode. |
| This way we can keep different modes separate. */ |
| |
| mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); |
| sets[i].mode = mode; |
| |
| if (!is_fake_set && src_eqv) |
| { |
| machine_mode eqvmode = mode; |
| if (GET_CODE (dest) == STRICT_LOW_PART) |
| eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); |
| do_not_record = 0; |
| hash_arg_in_memory = 0; |
| src_eqv_hash = HASH (src_eqv, eqvmode); |
| |
| /* Find the equivalence class for the equivalent expression. */ |
| |
| if (!do_not_record) |
| src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode); |
| |
| src_eqv_volatile = do_not_record; |
| src_eqv_in_memory = hash_arg_in_memory; |
| } |
| |
| /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the |
| value of the INNER register, not the destination. So it is not |
| a valid substitution for the source. But save it for later. */ |
| if (is_fake_set || GET_CODE (dest) == STRICT_LOW_PART) |
| src_eqv_here = 0; |
| else |
| src_eqv_here = src_eqv; |
| |
| /* Simplify and foldable subexpressions in SRC. Then get the fully- |
| simplified result, which may not necessarily be valid. */ |
| src_folded = fold_rtx (src, NULL); |
| |
| #if 0 |
| /* ??? This caused bad code to be generated for the m68k port with -O2. |
| Suppose src is (CONST_INT -1), and that after truncation src_folded |
| is (CONST_INT 3). Suppose src_folded is then used for src_const. |
| At the end we will add src and src_const to the same equivalence |
| class. We now have 3 and -1 on the same equivalence class. This |
| causes later instructions to be mis-optimized. */ |
| /* If storing a constant in a bitfield, pre-truncate the constant |
| so we will be able to record it later. */ |
| if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT) |
| { |
| rtx width = XEXP (SET_DEST (sets[i].rtl), 1); |
| |
| if (CONST_INT_P (src) |
| && CONST_INT_P (width) |
| && INTVAL (width) < HOST_BITS_PER_WIDE_INT |
| && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) |
| src_folded |
| = GEN_INT (INTVAL (src) & ((HOST_WIDE_INT_1 |
| << INTVAL (width)) - 1)); |
| } |
| #endif |
| |
| /* Compute SRC's hash code, and also notice if it |
| should not be recorded at all. In that case, |
| prevent any further processing of this assignment. |
| |
| We set DO_NOT_RECORD if the destination has a REG_UNUSED note. |
| This avoids getting the source register into the tables, where it |
| may be invalidated later (via REG_QTY), then trigger an ICE upon |
| re-insertion. |
| |
| This is only a problem in multi-set insns. If it were a single |
| set the dead copy would have been removed. If the RHS were anything |
| but a simple REG, then we won't call insert_regs and thus there's |
| no potential for triggering the ICE. */ |
| do_not_record = (REG_P (dest) |
| && REG_P (src) |
| && find_reg_note (insn, REG_UNUSED, dest)); |
| hash_arg_in_memory = 0; |
| |
| sets[i].src = src; |
| sets[i].src_hash = HASH (src, mode); |
| sets[i].src_volatile = do_not_record; |
| sets[i].src_in_memory = hash_arg_in_memory; |
| |
| /* If SRC is a MEM, there is a REG_EQUIV note for SRC, and DEST is |
| a pseudo, do not record SRC. Using SRC as a replacement for |
| anything else will be incorrect in that situation. Note that |
| this usually occurs only for stack slots, in which case all the |
| RTL would be referring to SRC, so we don't lose any optimization |
| opportunities by not having SRC in the hash table. */ |
| |
| if (MEM_P (src) |
| && find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0 |
| && REG_P (dest) |
| && REGNO (dest) >= FIRST_PSEUDO_REGISTER) |
| sets[i].src_volatile = 1; |
| |
| else if (GET_CODE (src) == ASM_OPERANDS |
| && GET_CODE (x) == PARALLEL) |
| { |
| /* Do not record result of a non-volatile inline asm with |
| more than one result. */ |
| if (n_sets > 1) |
| sets[i].src_volatile = 1; |
| |
| int j, lim = XVECLEN (x, 0); |
| for (j = 0; j < lim; j++) |
| { |
| rtx y = XVECEXP (x, 0, j); |
| /* And do not record result of a non-volatile inline asm |
| with "memory" clobber. */ |
| if (GET_CODE (y) == CLOBBER && MEM_P (XEXP (y, 0))) |
| { |
| sets[i].src_volatile = 1; |
| break; |
| } |
| } |
| } |
| |
| #if 0 |
| /* It is no longer clear why we used to do this, but it doesn't |
| appear to still be needed. So let's try without it since this |
| code hurts cse'ing widened ops. */ |
| /* If source is a paradoxical subreg (such as QI treated as an SI), |
| treat it as volatile. It may do the work of an SI in one context |
| where the extra bits are not being used, but cannot replace an SI |
| in general. */ |
| if (paradoxical_subreg_p (src)) |
| sets[i].src_volatile = 1; |
| #endif |
| |
| /* Locate all possible equivalent forms for SRC. Try to replace |
| SRC in the insn with each cheaper equivalent. |
| |
| We have the following types of equivalents: SRC itself, a folded |
| version, a value given in a REG_EQUAL note, or a value related |
| to a constant. |
| |
| Each of these equivalents may be part of an additional class |
| of equivalents (if more than one is in the table, they must be in |
| the same class; we check for this). |
| |
| If the source is volatile, we don't do any table lookups. |
| |
| We note any constant equivalent for possible later use in a |
| REG_NOTE. */ |
| |
| if (!sets[i].src_volatile) |
| elt = lookup (src, sets[i].src_hash, mode); |
| |
| sets[i].src_elt = elt; |
| |
| if (elt && src_eqv_here && src_eqv_elt) |
| { |
| if (elt->first_same_value != src_eqv_elt->first_same_value) |
| { |
| /* The REG_EQUAL is indicating that two formerly distinct |
| classes are now equivalent. So merge them. */ |
| merge_equiv_classes (elt, src_eqv_elt); |
| src_eqv_hash = HASH (src_eqv, elt->mode); |
| src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode); |
| } |
| |
| src_eqv_here = 0; |
| } |
| |
| else if (src_eqv_elt) |
| elt = src_eqv_elt; |
| |
| /* Try to find a constant somewhere and record it in `src_const'. |
| Record its table element, if any, in `src_const_elt'. Look in |
| any known equivalences first. (If the constant is not in the |
| table, also set `sets[i].src_const_hash'). */ |
| if (elt) |
| for (p = elt->first_same_value; p; p = p->next_same_value) |
| if (p->is_const) |
| { |
| src_const = p->exp; |
| src_const_elt = elt; |
| break; |
| } |
| |
| if (src_const == 0 |
| && (CONSTANT_P (src_folded) |
| /* Consider (minus (label_ref L1) (label_ref L2)) as |
| "constant" here so we will record it. This allows us |
| to fold switch statements when an ADDR_DIFF_VEC is used. */ |
| || (GET_CODE (src_folded) == MINUS |
| && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF |
| && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF))) |
| src_const = src_folded, src_const_elt = elt; |
| else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here)) |
| src_const = src_eqv_here, src_const_elt = src_eqv_elt; |
| |
| /* If we don't know if the constant is in the table, get its |
| hash code and look it up. */ |
| if (src_const && src_const_elt == 0) |
| { |
| sets[i].src_const_hash = HASH (src_const, mode); |
| src_const_elt = lookup (src_const, sets[i].src_const_hash, mode); |
| } |
| |
| sets[i].src_const = src_const; |
| sets[i].src_const_elt = src_const_elt; |
| |
| /* If the constant and our source are both in the table, mark them as |
| equivalent. Otherwise, if a constant is in the table but the source |
| isn't, set ELT to it. */ |
| if (src_const_elt && elt |
| && src_const_elt->first_same_value != elt->first_same_value) |
| merge_equiv_classes (elt, src_const_elt); |
| else if (src_const_elt && elt == 0) |
| elt = src_const_elt; |
| |
| /* See if there is a register linearly related to a constant |
| equivalent of SRC. */ |
| if (src_const |
| && (GET_CODE (src_const) == CONST |
| || (src_const_elt && src_const_elt->related_value != 0))) |
| { |
| src_related = use_related_value (src_const, src_const_elt); |
| if (src_related) |
| { |
| struct table_elt *src_related_elt |
| = lookup (src_related, HASH (src_related, mode), mode); |
| if (src_related_elt && elt) |
| { |
| if (elt->first_same_value |
| != src_related_elt->first_same_value) |
| /* This can occur when we previously saw a CONST |
| involving a SYMBOL_REF and then see the SYMBOL_REF |
| twice. Merge the involved classes. */ |
| merge_equiv_classes (elt, src_related_elt); |
| |
| src_related = 0; |
| src_related_elt = 0; |
| } |
| else if (src_related_elt && elt == 0) |
| elt = src_related_elt; |
| } |
| } |
| |
| /* See if we have a CONST_INT that is already in a register in a |
| wider mode. */ |
| |
| if (src_const && src_related == 0 && CONST_INT_P (src_const) |
| && is_int_mode (mode, &int_mode) |
| && GET_MODE_PRECISION (int_mode) < BITS_PER_WORD) |
| { |
| opt_scalar_int_mode wider_mode_iter; |
| FOR_EACH_WIDER_MODE (wider_mode_iter, int_mode) |
| { |
| scalar_int_mode wider_mode = wider_mode_iter.require (); |
| if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD) |
| break; |
| |
| struct table_elt *const_elt |
| = lookup (src_const, HASH (src_const, wider_mode), wider_mode); |
| |
| if (const_elt == 0) |
| continue; |
| |
| for (const_elt = const_elt->first_same_value; |
| const_elt; const_elt = const_elt->next_same_value) |
| if (REG_P (const_elt->exp)) |
| { |
| src_related = gen_lowpart (int_mode, const_elt->exp); |
| break; |
| } |
| |
| if (src_related != 0) |
| break; |
| } |
| } |
| |
| /* Another possibility is that we have an AND with a constant in |
| a mode narrower than a word. If so, it might have been generated |
| as part of an "if" which would narrow the AND. If we already |
| have done the AND in a wider mode, we can use a SUBREG of that |
| value. */ |
| |
| if (flag_expensive_optimizations && ! src_related |
| && is_a <scalar_int_mode> (mode, &int_mode) |
| && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1)) |
| && GET_MODE_SIZE (int_mode) < UNITS_PER_WORD) |
| { |
| opt_scalar_int_mode tmode_iter; |
| rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1)); |
| |
| FOR_EACH_WIDER_MODE (tmode_iter, int_mode) |
| { |
| scalar_int_mode tmode = tmode_iter.require (); |
| if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD) |
| break; |
| |
| rtx inner = gen_lowpart (tmode, XEXP (src, 0)); |
| struct table_elt *larger_elt; |
| |
| if (inner) |
| { |
| PUT_MODE (new_and, tmode); |
| XEXP (new_and, 0) = inner; |
| larger_elt = lookup (new_and, HASH (new_and, tmode), tmode); |
| if (larger_elt == 0) |
| continue; |
| |
| for (larger_elt = larger_elt->first_same_value; |
| larger_elt; larger_elt = larger_elt->next_same_value) |
| if (REG_P (larger_elt->exp)) |
| { |
| src_related |
| = gen_lowpart (int_mode, larger_elt->exp); |
| break; |
| } |
| |
| if (src_related) |
| break; |
| } |
| } |
| } |
| |
| /* See if a MEM has already been loaded with a widening operation; |
| if it has, we can use a subreg of that. Many CISC machines |
| also have such operations, but this is only likely to be |
| beneficial on these machines. */ |
| |
| rtx_code extend_op; |
| if (flag_expensive_optimizations && src_related == 0 |
| && MEM_P (src) && ! do_not_record |
| && is_a <scalar_int_mode> (mode, &int_mode) |
| && (extend_op = load_extend_op (int_mode)) != UNKNOWN) |
| { |
| #if GCC_VERSION >= 5000 |
| struct rtx_def memory_extend_buf; |
| rtx memory_extend_rtx = &memory_extend_buf; |
| #else |
| /* Workaround GCC < 5 bug, fixed in r5-3834 as part of PR63362 |
| fix. */ |
| alignas (rtx_def) unsigned char memory_extended_buf[sizeof (rtx_def)]; |
| rtx memory_extend_rtx = (rtx) &memory_extended_buf[0]; |
| #endif |
| |
| /* Set what we are trying to extend and the operation it might |
| have been extended with. */ |
| memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx)); |
| PUT_CODE (memory_extend_rtx, extend_op); |
| XEXP (memory_extend_rtx, 0) = src; |
| |
| opt_scalar_int_mode tmode_iter; |
| FOR_EACH_WIDER_MODE (tmode_iter, int_mode) |
| { |
| struct table_elt *larger_elt; |
| |
| scalar_int_mode tmode = tmode_iter.require (); |
| if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD) |
| break; |
| |
| PUT_MODE (memory_extend_rtx, tmode); |
| larger_elt = lookup (memory_extend_rtx, |
| HASH (memory_extend_rtx, tmode), tmode); |
| if (larger_elt == 0) |
| continue; |
| |
| for (larger_elt = larger_elt->first_same_value; |
| larger_elt; larger_elt = larger_elt->next_same_value) |
| if (REG_P (larger_elt->exp)) |
| { |
| src_related = gen_lowpart (int_mode, larger_elt->exp); |
| break; |
| } |
| |
| if (src_related) |
| break; |
| } |
| } |
| |
| /* Try to express the constant using a register+offset expression |
| derived from a constant anchor. */ |
| |
| if (targetm.const_anchor |
| && !src_related |
| && src_const |
| && GET_CODE (src_const) == CONST_INT) |
| { |
| src_related = try_const_anchors (src_const, mode); |
| src_related_is_const_anchor = src_related != NULL_RTX; |
| } |
| |
| /* Try to re-materialize a vec_dup with an existing constant. */ |
| rtx src_elt; |
| if ((!src_eqv_here || CONSTANT_P (src_eqv_here)) |
| && const_vec_duplicate_p (src, &src_elt)) |
| { |
| machine_mode const_mode = GET_MODE_INNER (GET_MODE (src)); |
| struct table_elt *related_elt |
| = lookup (src_elt, HASH (src_elt, const_mode), const_mode); |
| if (related_elt) |
| { |
| for (related_elt = related_elt->first_same_value; |
| related_elt; related_elt = related_elt->next_same_value) |
| if (REG_P (related_elt->exp)) |
| { |
| /* We don't need to compare costs with an existing (constant) |
| src_eqv_here, since any such src_eqv_here should already be |
| available in src_const. */ |
| src_eqv_here |
| = gen_rtx_VEC_DUPLICATE (GET_MODE (src), |
| related_elt->exp); |
| break; |
| } |
| } |
| } |
| |
| if (src == src_folded) |
| src_folded = 0; |
| |
| /* At this point, ELT, if nonzero, points to a class of expressions |
| equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED, |
| and SRC_RELATED, if nonzero, each contain additional equivalent |
| expressions. Prune these latter expressions by deleting expressions |
| already in the equivalence class. |
| |
| Check for an equivalent identical to the destination. If found, |
| this is the preferred equivalent since it will likely lead to |
| elimination of the insn. Indicate this by placing it in |
| `src_related'. */ |
| |
| if (elt) |
| elt = elt->first_same_value; |
| for (p = elt; p; p = p->next_same_value) |
| { |
| enum rtx_code code = GET_CODE (p->exp); |
| |
| /* If the expression is not valid, ignore it. Then we do not |
| have to check for validity below. In most cases, we can use |
| `rtx_equal_p', since canonicalization has already been done. */ |
| if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false)) |
| continue; |
| |
| /* Also skip paradoxical subregs, unless that's what we're |
| looking for. */ |
| if (paradoxical_subreg_p (p->exp) |
| && ! (src != 0 |
| && GET_CODE (src) == SUBREG |
| && GET_MODE (src) == GET_MODE (p->exp) |
| && partial_subreg_p (GET_MODE (SUBREG_REG (src)), |
| GET_MODE (SUBREG_REG (p->exp))))) |
| continue; |
| |
| if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp)) |
| src = 0; |
| else if (src_folded && GET_CODE (src_folded) == code |
| && rtx_equal_p (src_folded, p->exp)) |
| src_folded = 0; |
| else if (src_eqv_here && GET_CODE (src_eqv_here) == code |
| && rtx_equal_p (src_eqv_here, p->exp)) |
| src_eqv_here = 0; |
| else if (src_related && GET_CODE (src_related) == code |
| && rtx_equal_p (src_related, p->exp)) |
| src_related = 0; |
| |
| /* This is the same as the destination of the insns, we want |
| to prefer it. The code below will then give it a negative |
| cost. */ |
| if (!dest_related |
| && GET_CODE (dest) == code && rtx_equal_p (p->exp, dest)) |
| dest_related = p->exp; |
| } |
| |
| /* Find the cheapest valid equivalent, trying all the available |
| possibilities. Prefer items not in the hash table to ones |
| that are when they are equal cost. Note that we can never |
| worsen an insn as the current contents will also succeed. |
| If we find an equivalent identical to the destination, use it as best, |
| since this insn will probably be eliminated in that case. */ |
| if (src) |
| { |
| if (rtx_equal_p (src, dest)) |
| src_cost = src_regcost = -1; |
| else |
| { |
| src_cost = COST (src, mode); |
| src_regcost = approx_reg_cost (src); |
| } |
| } |
| |
| if (src_eqv_here) |
| { |
| if (rtx_equal_p (src_eqv_here, dest)) |
| src_eqv_cost = src_eqv_regcost = -1; |
| else |
| { |
| src_eqv_cost = COST (src_eqv_here, mode); |
| src_eqv_regcost = approx_reg_cost (src_eqv_here); |
| } |
| } |
| |
| if (src_folded) |
| { |
| if (rtx_equal_p (src_folded, dest)) |
| src_folded_cost = src_folded_regcost = -1; |
| else |
| { |
| src_folded_cost = COST (src_folded, mode); |
| src_folded_regcost = approx_reg_cost (src_folded); |
| } |
| } |
| |
| if (dest_related) |
| { |
| src_related_cost = src_related_regcost = -1; |
| /* Handle it as src_related. */ |
| src_related = dest_related; |
| } |
| else if (src_related) |
| { |
| src_related_cost = COST (src_related, mode); |
| src_related_regcost = approx_reg_cost (src_related); |
| |
| /* If a const-anchor is used to synthesize a constant that |
| normally requires multiple instructions then slightly prefer |
| it over the original sequence. These instructions are likely |
| to become redundant now. We can't compare against the cost |
| of src_eqv_here because, on MIPS for example, multi-insn |
| constants have zero cost; they are assumed to be hoisted from |
| loops. */ |
| if (src_related_is_const_anchor |
| && src_related_cost == src_cost |
| && src_eqv_here) |
| src_related_cost--; |
| } |
| |
| /* If this was an indirect jump insn, a known label will really be |
| cheaper even though it looks more expensive. */ |
| if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF) |
| src_folded = src_const, src_folded_cost = src_folded_regcost = -1; |
| |
| /* Terminate loop when replacement made. This must terminate since |
| the current contents will be tested and will always be valid. */ |
| while (!is_fake_set) |
| { |
| rtx trial; |
| |
| /* Skip invalid entries. */ |
| while (elt && !REG_P (elt->exp) |
| && ! exp_equiv_p (elt->exp, elt->exp, 1, false)) |
| elt = elt->next_same_value; |
| |
| /* A paradoxical subreg would be bad here: it'll be the right |
| size, but later may be adjusted so that the upper bits aren't |
| what we want. So reject it. */ |
| if (elt != 0 |
| && paradoxical_subreg_p (elt->exp) |
| /* It is okay, though, if the rtx we're trying to match |
| will ignore any of the bits we can't predict. */ |
| && ! (src != 0 |
| && GET_CODE (src) == SUBREG |
| && GET_MODE (src) == GET_MODE (elt->exp) |
| && partial_subreg_p (GET_MODE (SUBREG_REG (src)), |
| GET_MODE (SUBREG_REG (elt->exp))))) |
| { |
| elt = elt->next_same_value; |
| continue; |
| } |
| |
| if (elt) |
| { |
| src_elt_cost = elt->cost; |
| src_elt_regcost = elt->regcost; |
| } |
| |
| /* Find cheapest and skip it for the next time. For items |
| of equal cost, use this order: |
| src_folded, src, src_eqv, src_related and hash table entry. */ |
| if (src_folded |
| && preferable (src_folded_cost, src_folded_regcost, |
| src_cost, src_regcost) <= 0 |
| && preferable (src_folded_cost, src_folded_regcost, |
| src_eqv_cost, src_eqv_regcost) <= 0 |
| && preferable (src_folded_cost, src_folded_regcost, |
| src_related_cost, src_related_regcost) <= 0 |
| && preferable (src_folded_cost, src_folded_regcost, |
| src_elt_cost, src_elt_regcost) <= 0) |
| trial = src_folded, src_folded_cost = MAX_COST; |
| else if (src |
| && preferable (src_cost, src_regcost, |
| src_eqv_cost, src_eqv_regcost) <= 0 |
| && preferable (src_cost, src_regcost, |
| src_related_cost, src_related_regcost) <= 0 |
| && preferable (src_cost, src_regcost, |
| src_elt_cost, src_elt_regcost) <= 0) |
| trial = src, src_cost = MAX_COST; |
| else if (src_eqv_here |
| && preferable (src_eqv_cost, src_eqv_regcost, |
| src_related_cost, src_related_regcost) <= 0 |
| && preferable (src_eqv_cost, src_eqv_regcost, |
| src_elt_cost, src_elt_regcost) <= 0) |
| trial = src_eqv_here, src_eqv_cost = MAX_COST; |
| else if (src_related |
| && preferable (src_related_cost, src_related_regcost, |
| src_elt_cost, src_elt_regcost) <= 0) |
| trial = src_related, src_related_cost = MAX_COST; |
| else |
| { |
| trial = elt->exp; |
| elt = elt->next_same_value; |
| src_elt_cost = MAX_COST; |
| } |
| |
| /* Try to optimize |
| (set (reg:M N) (const_int A)) |
| (set (reg:M2 O) (const_int B)) |
| (set (zero_extract:M2 (reg:M N) (const_int C) (const_int D)) |
| (reg:M2 O)). */ |
| if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT |
| && CONST_INT_P (trial) |
| && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1)) |
| && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2)) |
| && REG_P (XEXP (SET_DEST (sets[i].rtl), 0)) |
| && (known_ge |
| (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl))), |
| INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)))) |
| && ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)) |
| + (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2)) |
| <= HOST_BITS_PER_WIDE_INT)) |
| { |
| rtx dest_reg = XEXP (SET_DEST (sets[i].rtl), 0); |
| rtx width = XEXP (SET_DEST (sets[i].rtl), 1); |
| rtx pos = XEXP (SET_DEST (sets[i].rtl), 2); |
| unsigned int dest_hash = HASH (dest_reg, GET_MODE (dest_reg)); |
| struct table_elt *dest_elt |
| = lookup (dest_reg, dest_hash, GET_MODE (dest_reg)); |
| rtx dest_cst = NULL; |
| |
| if (dest_elt) |
| for (p = dest_elt->first_same_value; p; p = p->next_same_value) |
| if (p->is_const && CONST_INT_P (p->exp)) |
| { |
| dest_cst = p->exp; |
| break; |
| } |
| if (dest_cst) |
| { |
| HOST_WIDE_INT val = INTVAL (dest_cst); |
| HOST_WIDE_INT mask; |
| unsigned int shift; |
| /* This is the mode of DEST_CST as well. */ |
| scalar_int_mode dest_mode |
| = as_a <scalar_int_mode> (GET_MODE (dest_reg)); |
| if (BITS_BIG_ENDIAN) |
| shift = GET_MODE_PRECISION (dest_mode) |
| - INTVAL (pos) - INTVAL (width); |
| else |
| shift = INTVAL (pos); |
| if (INTVAL (width) == HOST_BITS_PER_WIDE_INT) |
| mask = HOST_WIDE_INT_M1; |
| else |
| mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1; |
| val &= ~(mask << shift); |
| val |= (INTVAL (trial) & mask) << shift; |
| val = trunc_int_for_mode (val, dest_mode); |
| validate_unshare_change (insn, &SET_DEST (sets[i].rtl), |
| dest_reg, 1); |
| validate_unshare_change (insn, &SET_SRC (sets[i].rtl), |
| GEN_INT (val), 1); |
| if (apply_change_group ()) |
| { |
| rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); |
| if (note) |
| { |
| remove_note (insn, note); |
| df_notes_rescan (insn); |
| } |
| src_eqv = NULL_RTX; |
| src_eqv_elt = NULL; |
| src_eqv_volatile = 0; |
| src_eqv_in_memory = 0; |
| src_eqv_hash = 0; |
| repeat = true; |
| break; |
| } |
| } |
| } |
| |
| /* We don't normally have an insn matching (set (pc) (pc)), so |
| check for this separately here. We will delete such an |
| insn below. |
| |
| For other cases such as a table jump or conditional jump |
| where we know the ultimate target, go ahead and replace the |
| operand. While that may not make a valid insn, we will |
| reemit the jump below (and also insert any necessary |
| barriers). */ |
| if (n_sets == 1 && dest == pc_rtx |
| && (trial == pc_rtx |
| || (GET_CODE (trial) == LABEL_REF |
| && ! condjump_p (insn)))) |
| { |
| /* Don't substitute non-local labels, this confuses CFG. */ |
| if (GET_CODE (trial) == LABEL_REF |
| && LABEL_REF_NONLOCAL_P (trial)) |
| continue; |
| |
| SET_SRC (sets[i].rtl) = trial; |
| cse_jumps_altered = true; |
| break; |
| } |
| |
| /* Similarly, lots of targets don't allow no-op |
| (set (mem x) (mem x)) moves. Even (set (reg x) (reg x)) |
| might be impossible for certain registers (like CC registers). */ |
| else if (n_sets == 1 |
| && !CALL_P (insn) |
| && (MEM_P (trial) || REG_P (trial)) |
| && rtx_equal_p (trial, dest) |
| && !side_effects_p (dest) |
| && (cfun->can_delete_dead_exceptions |
| || insn_nothrow_p (insn)) |
| /* We can only remove the later store if the earlier aliases |
| at least all accesses the later one. */ |
| && (!MEM_P (trial) |
| || ((MEM_ALIAS_SET (dest) == MEM_ALIAS_SET (trial) |
| || alias_set_subset_of (MEM_ALIAS_SET (dest), |
| MEM_ALIAS_SET (trial))) |
| && (!MEM_EXPR (trial) |
| || refs_same_for_tbaa_p (MEM_EXPR (trial), |
| MEM_EXPR (dest)))))) |
| { |
| SET_SRC (sets[i].rtl) = trial; |
| noop_insn = true; |
| break; |
| } |
| |
| /* Reject certain invalid forms of CONST that we create. */ |
| else if (CONSTANT_P (trial) |
| && GET_CODE (trial) == CONST |
| /* Reject cases that will cause decode_rtx_const to |
| die. On the alpha when simplifying a switch, we |
| get (const (truncate (minus (label_ref) |
| (label_ref)))). */ |
| && (GET_CODE (XEXP (trial, 0)) == TRUNCATE |
| /* Likewise on IA-64, except without the |
| truncate. */ |
| || (GET_CODE (XEXP (trial, 0)) == MINUS |
| && GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF |
| && GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF))) |
| /* Do nothing for this case. */ |
| ; |
| |
| /* Do not replace anything with a MEM, except the replacement |
| is a no-op. This allows this loop to terminate. */ |
| else if (MEM_P (trial) && !rtx_equal_p (trial, SET_SRC(sets[i].rtl))) |
| /* Do nothing for this case. */ |
| ; |
| |
| /* Look for a substitution that makes a valid insn. */ |
| else if (validate_unshare_change (insn, &SET_SRC (sets[i].rtl), |
| trial, 0)) |
| { |
| rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn); |
| |
| /* The result of apply_change_group can be ignored; see |
| canon_reg. */ |
| |
| validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); |
| apply_change_group (); |
| |
| break; |
| } |
| |
| /* If the current function uses a constant pool and this is a |
| constant, try making a pool entry. Put it in src_folded |
| unless we already have done this since that is where it |
| likely came from. */ |
| |
| else if (crtl->uses_const_pool |
| && CONSTANT_P (trial) |
| && !CONST_INT_P (trial) |
| && (src_folded == 0 || !MEM_P (src_folded)) |
| && GET_MODE_CLASS (mode) != MODE_CC |
| && mode != VOIDmode) |
| { |
| src_folded = force_const_mem (mode, trial); |
| if (src_folded) |
| { |
| src_folded_cost = COST (src_folded, mode); |
| src_folded_regcost = approx_reg_cost (src_folded); |
| } |
| } |
| } |
| |
| /* If we changed the insn too much, handle this set from scratch. */ |
| if (repeat) |
| { |
| i--; |
| continue; |
| } |
| |
| src = SET_SRC (sets[i].rtl); |
| |
| /* In general, it is good to have a SET with SET_SRC == SET_DEST. |
| However, there is an important exception: If both are registers |
| that are not the head of their equivalence class, replace SET_SRC |
| with the head of the class. If we do not do this, we will have |
| both registers live over a portion of the basic block. This way, |
| their lifetimes will likely abut instead of overlapping. */ |
| if (!is_fake_set |
| && REG_P (dest) |
| && REGNO_QTY_VALID_P (REGNO (dest))) |
| { |
| int dest_q = REG_QTY (REGNO (dest)); |
| struct qty_table_elem *dest_ent = &qty_table[dest_q]; |
| |
| if (dest_ent->mode == GET_MODE (dest) |
| && dest_ent->first_reg != REGNO (dest) |
| && REG_P (src) && REGNO (src) == REGNO (dest) |
| /* Don't do this if the original insn had a hard reg as |
| SET_SRC or SET_DEST. */ |
| && (!REG_P (sets[i].src) |
| || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER) |
| && (!REG_P (dest) || REGNO (dest) >= FIRST_PSEUDO_REGISTER)) |
| /* We can't call canon_reg here because it won't do anything if |
| SRC is a hard register. */ |
| { |
| int src_q = REG_QTY (REGNO (src)); |
| struct qty_table_elem *src_ent = &qty_table[src_q]; |
| int first = src_ent->first_reg; |
| rtx new_src |
| = (first >= FIRST_PSEUDO_REGISTER |
| ? regno_reg_rtx[first] : gen_rtx_REG (GET_MODE (src), first)); |
| |
| /* We must use validate-change even for this, because this |
| might be a special no-op instruction, suitable only to |
| tag notes onto. */ |
| if (validate_change (insn, &SET_SRC (sets[i].rtl), new_src, 0)) |
| { |
| src = new_src; |
| /* If we had a constant that is cheaper than what we are now |
| setting SRC to, use that constant. We ignored it when we |
| thought we could make this into a no-op. */ |
| if (src_const && COST (src_const, mode) < COST (src, mode) |
| && validate_change (insn, &SET_SRC (sets[i].rtl), |
| src_const, 0)) |
| src = src_const; |
| } |
| } |
| } |
| |
| /* If we made a change, recompute SRC values. */ |
| if (src != sets[i].src) |
| { |
| do_not_record = 0; |
| hash_arg_in_memory = 0; |
| sets[i].src = src; |
| sets[i].src_hash = HASH (src, mode); |
| sets[i].src_volatile = do_not_record; |
| sets[i].src_in_memory = hash_arg_in_memory; |
| sets[i].src_elt = lookup (src, sets[i].src_hash, mode); |
| } |
| |
| /* If this is a single SET, we are setting a register, and we have an |
| equivalent constant, we want to add a REG_EQUAL note if the constant |
| is different from the source. We don't want to do it for a constant |
| pseudo since verifying that this pseudo hasn't been eliminated is a |
| pain; moreover such a note won't help anything. |
| |
| Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF))) |
| which can be created for a reference to a compile time computable |
| entry in a jump table. */ |
| if (n_sets == 1 |
| && REG_P (dest) |
| && src_const |
| && !REG_P (src_const) |
| && !(GET_CODE (src_const) == SUBREG |
| && REG_P (SUBREG_REG (src_const))) |
| && !(GET_CODE (src_const) == CONST |
| && GET_CODE (XEXP (src_const, 0)) == MINUS |
| && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF |
| && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF) |
| && !rtx_equal_p (src, src_const)) |
| { |
| /* Make sure that the rtx is not shared. */ |
| src_const = copy_rtx (src_const); |
| |
| /* Record the actual constant value in a REG_EQUAL note, |
| making a new one if one does not already exist. */ |
| set_unique_reg_note (insn, REG_EQUAL, src_const); |
| df_notes_rescan (insn); |
| } |
| |
| /* Now deal with the destination. */ |
| do_not_record = 0; |
| |
| /* Look within any ZERO_EXTRACT to the MEM or REG within it. */ |
| while (GET_CODE (dest) == SUBREG |
| || GET_CODE (dest) == ZERO_EXTRACT |
| || GET_CODE (dest) == STRICT_LOW_PART) |
| dest = XEXP (dest, 0); |
| |
| sets[i].inner_dest = dest; |
| |
| if (MEM_P (dest)) |
| { |
| #ifdef PUSH_ROUNDING |
| /* Stack pushes invalidate the stack pointer. */ |
| rtx addr = XEXP (dest, 0); |
| if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC |
| && XEXP (addr, 0) == stack_pointer_rtx) |
| invalidate (stack_pointer_rtx, VOIDmode); |
| #endif |
| dest = fold_rtx (dest, insn); |
| } |
| |
| /* Compute the hash code of the destination now, |
| before the effects of this instruction are recorded, |
| since the register values used in the address computation |
| are those before this instruction. */ |
| sets[i].dest_hash = HASH (dest, mode); |
| |
| /* Don't enter a bit-field in the hash table |
| because the value in it after the store |
| may not equal what was stored, due to truncation. */ |
| |
| if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT) |
| { |
| rtx width = XEXP (SET_DEST (sets[i].rtl), 1); |
| |
| if (src_const != 0 && CONST_INT_P (src_const) |
| && CONST_INT_P (width) |
| && INTVAL (width) < HOST_BITS_PER_WIDE_INT |
| && ! (INTVAL (src_const) |
| & (HOST_WIDE_INT_M1U << INTVAL (width)))) |
| /* Exception: if the value is constant, |
| and it won't be truncated, record it. */ |
| ; |
| else |
| { |
| /* This is chosen so that the destination will be invalidated |
| but no new value will be recorded. |
| We must invalidate because sometimes constant |
| values can be recorded for bitfields. */ |
| sets[i].src_elt = 0; |
| sets[i].src_volatile = 1; |
| src_eqv = 0; |
| src_eqv_elt = 0; |
| } |
| } |
| |
| /* If only one set in a JUMP_INSN and it is now a no-op, we can delete |
| the insn. */ |
| else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx) |
| { |
| /* One less use of the label this insn used to jump to. */ |
| cse_cfg_altered |= delete_insn_and_edges (insn); |
| cse_jumps_altered = true; |
| /* No more processing for this set. */ |
| sets[i].rtl = 0; |
| } |
| |
| /* Similarly for no-op moves. */ |
| else if (noop_insn) |
| { |
| if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn)) |
| cse_cfg_altered = true; |
| cse_cfg_altered |= delete_insn_and_edges (insn); |
| /* No more processing for this set. */ |
| sets[i].rtl = 0; |
| } |
| |
| /* If this SET is now setting PC to a label, we know it used to |
| be a conditional or computed branch. */ |
| else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF |
| && !LABEL_REF_NONLOCAL_P (src)) |
| { |
| /* We reemit the jump in as many cases as possible just in |
| case the form of an unconditional jump is significantly |
| different than a computed jump or conditional jump. |
| |
| If this insn has multiple sets, then reemitting the |
| jump is nontrivial. So instead we just force rerecognition |
| and hope for the best. */ |
| if (n_sets == 1) |
| { |
| rtx_jump_insn *new_rtx; |
| rtx note; |
| |
| rtx_insn *seq = targetm.gen_jump (XEXP (src, 0)); |
| new_rtx = emit_jump_insn_before (seq, insn); |
| JUMP_LABEL (new_rtx) = XEXP (src, 0); |
| LABEL_NUSES (XEXP (src, 0))++; |
| |
| /* Make sure to copy over REG_NON_LOCAL_GOTO. */ |
| note = find_reg_note (insn, REG_NON_LOCAL_GOTO, 0); |
| if (note) |
| { |
| XEXP (note, 1) = NULL_RTX; |
| REG_NOTES (new_rtx) = note; |
| } |
| |
| cse_cfg_altered |= delete_insn_and_edges (insn); |
| insn = new_rtx; |
| } |
| else |
| INSN_CODE (insn) = -1; |
| |
| /* Do not bother deleting any unreachable code, let jump do it. */ |
| cse_jumps_altered = true; |
| sets[i].rtl = 0; |
| } |
| |
| /* If destination is volatile, invalidate it and then do no further |
| processing for this assignment. */ |
| |
| else if (do_not_record) |
| { |
| invalidate_dest (dest); |
| sets[i].rtl = 0; |
| } |
| |
| if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) |
| { |
| do_not_record = 0; |
| sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode); |
| if (do_not_record) |
| { |
| invalidate_dest (SET_DEST (sets[i].rtl)); |
| sets[i].rtl = 0; |
| } |
| } |
| } |
| |
| /* Now enter all non-volatile source expressions in the hash table |
| if they are not already present. |
| Record their equivalence classes in src_elt. |
| This way we can insert the corresponding destinations into |
| the same classes even if the actual sources are no longer in them |
| (having been invalidated). */ |
| |
| if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile |
| && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl))) |
| { |
| struct table_elt *elt; |
| struct table_elt *classp = sets[0].src_elt; |
| rtx dest = SET_DEST (sets[0].rtl); |
| machine_mode eqvmode = GET_MODE (dest); |
| |
| if (GET_CODE (dest) == STRICT_LOW_PART) |
| { |
| eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); |
| classp = 0; |
| } |
| if (insert_regs (src_eqv, classp, false)) |
| { |
| rehash_using_reg (src_eqv); |
| src_eqv_hash = HASH (src_eqv, eqvmode); |
| } |
| elt = insert (src_eqv, classp, src_eqv_hash, eqvmode); |
| elt->in_memory = src_eqv_in_memory; |
| src_eqv_elt = elt; |
| |
| /* Check to see if src_eqv_elt is the same as a set source which |
| does not yet have an elt, and if so set the elt of the set source |
| to src_eqv_elt. */ |
| for (i = 0; i < n_sets; i++) |
| if (sets[i].rtl && sets[i].src_elt == 0 |
| && rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv)) |
| sets[i].src_elt = src_eqv_elt; |
| } |
| |
| for (i = 0; i < n_sets; i++) |
| if (sets[i].rtl && ! sets[i].src_volatile |
| && ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) |
| { |
| if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) |
| { |
| /* REG_EQUAL in setting a STRICT_LOW_PART |
| gives an equivalent for the entire destination register, |
| not just for the subreg being stored in now. |
| This is a more interesting equivalence, so we arrange later |
| to treat the entire reg as the destination. */ |
| sets[i].src_elt = src_eqv_elt; |
| sets[i].src_hash = src_eqv_hash; |
| } |
| else |
| { |
| /* Insert source and constant equivalent into hash table, if not |
| already present. */ |
| struct table_elt *classp = src_eqv_elt; |
| rtx src = sets[i].src; |
| rtx dest = SET_DEST (sets[i].rtl); |
| machine_mode mode |
| = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); |
| |
| /* It's possible that we have a source value known to be |
| constant but don't have a REG_EQUAL note on the insn. |
| Lack of a note will mean src_eqv_elt will be NULL. This |
| can happen where we've generated a SUBREG to access a |
| CONST_INT that is already in a register in a wider mode. |
| Ensure that the source expression is put in the proper |
| constant class. */ |
| if (!classp) |
| classp = sets[i].src_const_elt; |
| |
| if (sets[i].src_elt == 0) |
| { |
| struct table_elt *elt; |
| |
| /* Note that these insert_regs calls cannot remove |
| any of the src_elt's, because they would have failed to |
| match if not still valid. */ |
| if (insert_regs (src, classp, false)) |
| { |
| rehash_using_reg (src); |
| sets[i].src_hash = HASH (src, mode); |
| } |
| elt = insert (src, classp, sets[i].src_hash, mode); |
| elt->in_memory = sets[i].src_in_memory; |
| /* If inline asm has any clobbers, ensure we only reuse |
| existing inline asms and never try to put the ASM_OPERANDS |
| into an insn that isn't inline asm. */ |
| if (GET_CODE (src) == ASM_OPERANDS |
| && GET_CODE (x) == PARALLEL) |
| elt->cost = MAX_COST; |
| sets[i].src_elt = classp = elt; |
| } |
| if (sets[i].src_const && sets[i].src_const_elt == 0 |
| && src != sets[i].src_const |
| && ! rtx_equal_p (sets[i].src_const, src)) |
| sets[i].src_elt = insert (sets[i].src_const, classp, |
| sets[i].src_const_hash, mode); |
| } |
| } |
| else if (sets[i].src_elt == 0) |
| /* If we did not insert the source into the hash table (e.g., it was |
| volatile), note the equivalence class for the REG_EQUAL value, if any, |
| so that the destination goes into that class. */ |
| sets[i].src_elt = src_eqv_elt; |
| |
| /* Record destination addresses in the hash table. This allows us to |
| check if they are invalidated by other sets. */ |
| for (i = 0; i < n_sets; i++) |
| { |
| if (sets[i].rtl) |
| { |
| rtx x = sets[i].inner_dest; |
| struct table_elt *elt; |
| machine_mode mode; |
| unsigned hash; |
| |
| if (MEM_P (x)) |
| { |
| x = XEXP (x, 0); |
| mode = GET_MODE (x); |
| hash = HASH (x, mode); |
| elt = lookup (x, hash, mode); |
| if (!elt) |
| { |
| if (insert_regs (x, NULL, false)) |
| { |
| rtx dest = SET_DEST (sets[i].rtl); |
| |
| rehash_using_reg (x); |
| hash = HASH (x, mode); |
| sets[i].dest_hash = HASH (dest, GET_MODE (dest)); |
| } |
| elt = insert (x, NULL, hash, mode); |
| } |
| |
| sets[i].dest_addr_elt = elt; |
| } |
| else |
| sets[i].dest_addr_elt = NULL; |
| } |
| } |
| |
| invalidate_from_clobbers (insn); |
| |
| /* Some registers are invalidated by subroutine calls. Memory is |
| invalidated by non-constant calls. */ |
| |
| if (CALL_P (insn)) |
| { |
| if (!(RTL_CONST_OR_PURE_CALL_P (insn))) |
| invalidate_memory (); |
| else |
| /* For const/pure calls, invalidate any argument slots, because |
| those are owned by the callee. */ |
| for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) |
| if (GET_CODE (XEXP (tem, 0)) == USE |
| && MEM_P (XEXP (XEXP (tem, 0), 0))) |
| invalidate (XEXP (XEXP (tem, 0), 0), VOIDmode); |
| invalidate_for_call (insn); |
| } |
| |
| /* Now invalidate everything set by this instruction. |
| If a SUBREG or other funny destination is being set, |
| sets[i].rtl is still nonzero, so here we invalidate the reg |
| a part of which is being set. */ |
| |
| for (i = 0; i < n_sets; i++) |
| if (sets[i].rtl) |
| { |
| /* We can't use the inner dest, because the mode associated with |
| a ZERO_EXTRACT is significant. */ |
| rtx dest = SET_DEST (sets[i].rtl); |
| |
| /* Needed for registers to remove the register from its |
| previous quantity's chain. |
| Needed for memory if this is a nonvarying address, unless |
| we have just done an invalidate_memory that covers even those. */ |
| if (REG_P (dest) || GET_CODE (dest) == SUBREG) |
| invalidate (dest, VOIDmode); |
| else if (MEM_P (dest)) |
| invalidate (dest, VOIDmode); |
| else if (GET_CODE (dest) == STRICT_LOW_PART |
| || GET_CODE (dest) == ZERO_EXTRACT) |
| invalidate (XEXP (dest, 0), GET_MODE (dest)); |
| } |
| |
| /* Don't cse over a call to setjmp; on some machines (eg VAX) |
| the regs restored by the longjmp come from a later time |
| than the setjmp. */ |
| if (CALL_P (insn) && find_reg_note (insn, REG_SETJMP, NULL)) |
| { |
| flush_hash_table (); |
| goto done; |
| } |
| |
| /* Make sure registers mentioned in destinations |
| are safe for use in an expression to be inserted. |
| This removes from the hash table |
| any invalid entry that refers to one of these registers. |
| |
| We don't care about the return value from mention_regs because |
| we are going to hash the SET_DEST values unconditionally. */ |
| |
| for (i = 0; i < n_sets; i++) |
| { |
| if (sets[i].rtl) |
| { |
| rtx x = SET_DEST (sets[i].rtl); |
| |
| if (!REG_P (x)) |
| mention_regs (x); |
| else |
| { |
| /* We used to rely on all references to a register becoming |
| inaccessible when a register changes to a new quantity, |
| since that changes the hash code. However, that is not |
| safe, since after HASH_SIZE new quantities we get a |
| hash 'collision' of a register with its own invalid |
| entries. And since SUBREGs have been changed not to |
| change their hash code with the hash code of the register, |
| it wouldn't work any longer at all. So we have to check |
| for any invalid references lying around now. |
| This code is similar to the REG case in mention_regs, |
| but it knows that reg_tick has been incremented, and |
| it leaves reg_in_table as -1 . */ |
| unsigned int regno = REGNO (x); |
| unsigned int endregno = END_REGNO (x); |
| unsigned int i; |
| |
| for (i = regno; i < endregno; i++) |
| { |
| if (REG_IN_TABLE (i) >= 0) |
| { |
| remove_invalid_refs (i); |
| REG_IN_TABLE (i) = -1; |
| } |
| } |
| } |
| } |
| } |
| |
| /* We may have just removed some of the src_elt's from the hash table. |
| So replace each one with the current head of the same class. |
| Also check if destination addresses have been removed. */ |
| |
| for (i = 0; i < n_sets; i++) |
| if (sets[i].rtl) |
| { |
| if (sets[i].dest_addr_elt |
| && sets[i].dest_addr_elt->first_same_value == 0) |
| { |
| /* The elt was removed, which means this destination is not |
| valid after this instruction. */ |
| sets[i].rtl = NULL_RTX; |
| } |
| else if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) |
| /* If elt was removed, find current head of same class, |
| or 0 if nothing remains of that class. */ |
| { |
| struct table_elt *elt = sets[i].src_elt; |
| |
| while (elt && elt->prev_same_value) |
| elt = elt->prev_same_value; |
| |
| while (elt && elt->first_same_value == 0) |
| elt = elt->next_same_value; |
| sets[i].src_elt = elt ? elt->first_same_value : 0; |
| } |
| } |
| |
| /* Now insert the destinations into their equivalence classes. */ |
| |
| for (i = 0; i < n_sets; i++) |
| if (sets[i].rtl) |
| { |
| rtx dest = SET_DEST (sets[i].rtl); |
| struct table_elt *elt; |
| |
| /* Don't record value if we are not supposed to risk allocating |
| floating-point values in registers that might be wider than |
| memory. */ |
| if ((flag_float_store |
| && MEM_P (dest) |
| && FLOAT_MODE_P (GET_MODE (dest))) |
| /* Don't record BLKmode values, because we don't know the |
| size of it, and can't be sure that other BLKmode values |
| have the same or smaller size. */ |
| || GET_MODE (dest) == BLKmode |
| /* If we didn't put a REG_EQUAL value or a source into the hash |
| table, there is no point is recording DEST. */ |
| || sets[i].src_elt == 0) |
| continue; |
| |
| /* STRICT_LOW_PART isn't part of the value BEING set, |
| and neither is the SUBREG inside it. |
| Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ |
| if (GET_CODE (dest) == STRICT_LOW_PART) |
| dest = SUBREG_REG (XEXP (dest, 0)); |
| |
| if (REG_P (dest) || GET_CODE (dest) == SUBREG) |
| /* Registers must also be inserted into chains for quantities. */ |
| if (insert_regs (dest, sets[i].src_elt, true)) |
| { |
| /* If `insert_regs' changes something, the hash code must be |
| recalculated. */ |
| rehash_using_reg (dest); |
| sets[i].dest_hash = HASH (dest, GET_MODE (dest)); |
| } |
| |
| /* If DEST is a paradoxical SUBREG, don't record DEST since the bits |
| outside the mode of GET_MODE (SUBREG_REG (dest)) are undefined. */ |
| if (paradoxical_subreg_p (dest)) |
| continue; |
| |
| elt = insert (dest, sets[i].src_elt, |
| sets[i].dest_hash, GET_MODE (dest)); |
| |
| /* If this is a constant, insert the constant anchors with the |
| equivalent register-offset expressions using register DEST. */ |
| if (targetm.const_anchor |
| && REG_P (dest) |
| && SCALAR_INT_MODE_P (GET_MODE (dest)) |
| && GET_CODE (sets[i].src_elt->exp) == CONST_INT) |
| insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest)); |
| |
| elt->in_memory = (MEM_P (sets[i].inner_dest) |
| && !MEM_READONLY_P (sets[i].inner_dest)); |
| |
| /* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no |
| narrower than M2, and both M1 and M2 are the same number of words, |
| we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so |
| make that equivalence as well. |
| |
| However, BAR may have equivalences for which gen_lowpart |
| will produce a simpler value than gen_lowpart applied to |
| BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all |
| BAR's equivalences. If we don't get a simplified form, make |
| the SUBREG. It will not be used in an equivalence, but will |
| cause two similar assignments to be detected. |
| |
| Note the loop below will find SUBREG_REG (DEST) since we have |
| already entered SRC and DEST of the SET in the table. */ |
| |
| if (GET_CODE (dest) == SUBREG |
| && (known_equal_after_align_down |
| (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1, |
| GET_MODE_SIZE (GET_MODE (dest)) - 1, |
| UNITS_PER_WORD)) |
| && !partial_subreg_p (dest) |
| && sets[i].src_elt != 0) |
| { |
| machine_mode new_mode = GET_MODE (SUBREG_REG (dest)); |
| struct table_elt *elt, *classp = 0; |
| |
| for (elt = sets[i].src_elt->first_same_value; elt; |
| elt = elt->next_same_value) |
| { |
| rtx new_src = 0; |
| unsigned src_hash; |
| struct table_elt *src_elt; |
| |
| /* Ignore invalid entries. */ |
| if (!REG_P (elt->exp) |
| && ! exp_equiv_p (elt->exp, elt->exp, 1, false)) |
| continue; |
| |
| /* We may have already been playing subreg games. If the |
| mode is already correct for the destination, use it. */ |
| if (GET_MODE (elt->exp) == new_mode) |
| new_src = elt->exp; |
| else |
| { |
| poly_uint64 byte |
| = subreg_lowpart_offset (new_mode, GET_MODE (dest)); |
| new_src = simplify_gen_subreg (new_mode, elt->exp, |
| GET_MODE (dest), byte); |
| } |
| |
| /* The call to simplify_gen_subreg fails if the value |
| is VOIDmode, yet we can't do any simplification, e.g. |
| for EXPR_LISTs denoting function call results. |
| It is invalid to construct a SUBREG with a VOIDmode |
| SUBREG_REG, hence a zero new_src means we can't do |
| this substitution. */ |
| if (! new_src) |
| continue; |
| |
| src_hash = HASH (new_src, new_mode); |
| src_elt = lookup (new_src, src_hash, new_mode); |
| |
| /* Put the new source in the hash table is if isn't |
| already. */ |
| if (src_elt == 0) |
| { |
| if (insert_regs (new_src, classp, false)) |
| { |
| rehash_using_reg (new_src); |
| src_hash = HASH (new_src, new_mode); |
| } |
| src_elt = insert (new_src, classp, src_hash, new_mode); |
| src_elt->in_memory = elt->in_memory; |
| if (GET_CODE (new_src) == ASM_OPERANDS |
| && elt->cost == MAX_COST) |
| src_elt->cost = MAX_COST; |
| } |
| else if (classp && classp != src_elt->first_same_value) |
| /* Show that two things that we've seen before are |
| actually the same. */ |
| merge_equiv_classes (src_elt, classp); |
| |
| classp = src_elt->first_same_value; |
| /* Ignore invalid entries. */ |
| while (classp |
| && !REG_P (classp->exp) |
| && ! exp_equiv_p (classp->exp, classp->exp, 1, false)) |
| classp = classp->next_same_value; |
| } |
| } |
| } |
| |
| /* Special handling for (set REG0 REG1) where REG0 is the |
| "cheapest", cheaper than REG1. After cse, REG1 will probably not |
| be used in the sequel, so (if easily done) change this insn to |
| (set REG1 REG0) and replace REG1 with REG0 in the previous insn |
| that computed their value. Then REG1 will become a dead store |
| and won't cloud the situation for later optimizations. |
| |
| Do not make this change if REG1 is a hard register, because it will |
| then be used in the sequel and we may be changing a two-operand insn |
| into a three-operand insn. |
| |
| Also do not do this if we are operating on a copy of INSN. */ |
| |
| if (n_sets == 1 && sets[0].rtl) |
| try_back_substitute_reg (sets[0].rtl, insn); |
| |
| done:; |
| } |
| |
| /* Remove from the hash table all expressions that reference memory. */ |
| |
| static void |
| invalidate_memory (void) |
| { |
| int i; |
| struct table_elt *p, *next; |
| |
| for (i = 0; i < HASH_SIZE; i++) |
| for (p = table[i]; p; p = next) |
| { |
| next = p->next_same_hash; |
| if (p->in_memory) |
| remove_from_table (p, i); |
| } |
| } |
| |
| /* Perform invalidation on the basis of everything about INSN, |
| except for invalidating the actual places that are SET in it. |
| This includes the places CLOBBERed, and anything that might |
| alias with something that is SET or CLOBBERed. */ |
| |
| static void |
| invalidate_from_clobbers (rtx_insn *insn) |
| { |
| rtx x = PATTERN (insn); |
| |
| if (GET_CODE (x) == CLOBBER) |
| { |
| rtx ref = XEXP (x, 0); |
| if (ref) |
| { |
| if (REG_P (ref) || GET_CODE (ref) == SUBREG |
| || MEM_P (ref)) |
| invalidate (ref, VOIDmode); |
| else if (GET_CODE (ref) == STRICT_LOW_PART |
| || GET_CODE (ref) == ZERO_EXTRACT) |
| invalidate (XEXP (ref, 0), GET_MODE (ref)); |
| } |
| } |
| else if (GET_CODE (x) == PARALLEL) |
| { |
| int i; |
| for (i = XVECLEN (x, 0) - 1; i >= 0; i--) |
| { |
| rtx y = XVECEXP (x, 0, i); |
| if (GET_CODE (y) == CLOBBER) |
| { |
| rtx ref = XEXP (y, 0); |
| if (REG_P (ref) || GET_CODE (ref) == SUBREG |
| || MEM_P (ref)) |
| invalidate (ref, VOIDmode); |
| else if (GET_CODE (ref) == STRICT_LOW_PART |
| || GET_CODE (ref) == ZERO_EXTRACT) |
| invalidate (XEXP (ref, 0), GET_MODE (ref)); |
| } |
| } |
| } |
| } |
| |
| /* Perform invalidation on the basis of everything about INSN. |
| This includes the places CLOBBERed, and anything that might |
| alias with something that is SET or CLOBBERed. */ |
| |
| static void |
| invalidate_from_sets_and_clobbers (rtx_insn *insn) |
| { |
| rtx tem; |
| rtx x = PATTERN (insn); |
| |
| if (CALL_P (insn)) |
| { |
| for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1)) |
| { |
| rtx temx = XEXP (tem, 0); |
| if (GET_CODE (temx) == CLOBBER) |
| invalidate (SET_DEST (temx), VOIDmode); |
| } |
| } |
| |
| /* Ensure we invalidate the destination register of a CALL insn. |
| This is necessary for machines where this register is a fixed_reg, |
| because no other code would invalidate it. */ |
| if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL) |
| invalidate (SET_DEST (x), VOIDmode); |
| |
| else if (GET_CODE (x) == PARALLEL) |
| { |
| int i; |
| |
| for (i = XVECLEN (x, 0) - 1; i >= 0; i--) |
| { |
| rtx y = XVECEXP (x, 0, i); |
| if (GET_CODE (y) == CLOBBER) |
| { |
| rtx clobbered = XEXP (y, 0); |
| |
| if (REG_P (clobbered) |
| || GET_CODE (clobbered) == SUBREG) |
| invalidate (clobbered, VOIDmode); |
| else if (GET_CODE (clobbered) == STRICT_LOW_PART |
| || GET_CODE (clobbered) == ZERO_EXTRACT) |
| invalidate (XEXP (clobbered, 0), GET_MODE (clobbered)); |
| } |
| else if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL) |
| invalidate (SET_DEST (y), VOIDmode); |
| } |
| } |
| } |
| |
| static rtx cse_process_note (rtx); |
| |
| /* A simplify_replace_fn_rtx callback for cse_process_note. Process X, |
| part of the REG_NOTES of an insn. Replace any registers with either |
| an equivalent constant or the canonical form of the register. |
| Only replace addresses if the containing MEM remains valid. |
| |
| Return the replacement for X, or null if it should be simplified |
| recursively. */ |
| |
| static rtx |
| cse_process_note_1 (rtx x, const_rtx, void *) |
| { |
| if (MEM_P (x)) |
| { |
| validate_change (x, &XEXP (x, 0), cse_process_note (XEXP (x, 0)), false); |
| return x; |
| } |
| |
| if (REG_P (x)) |
| { |
| int i = REG_QTY (REGNO (x)); |
| |
| /* Return a constant or a constant register. */ |
| if (REGNO_QTY_VALID_P (REGNO (x))) |
| { |
| struct qty_table_elem *ent = &qty_table[i]; |
| |
| if (ent->const_rtx != NULL_RTX |
| && (CONSTANT_P (ent->const_rtx) |
| || REG_P (ent->const_rtx))) |
| { |
| rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx); |
| if (new_rtx) |
| return copy_rtx (new_rtx); |
| } |
| } |
| |
| /* Otherwise, canonicalize this register. */ |
| return canon_reg (x, NULL); |
| } |
| |
| return NULL_RTX; |
| } |
| |
| /* Process X, part of the REG_NOTES of an insn. Replace any registers in it |
| with either an equivalent constant or the canonical form of the register. |
| Only replace addresses if the containing MEM remains valid. */ |
| |
| static rtx |
| cse_process_note (rtx x) |
| { |
| return simplify_replace_fn_rtx (x, NULL_RTX, cse_process_note_1, NULL); |
| } |
| |
| |
| /* Find a path in the CFG, starting with FIRST_BB to perform CSE on. |
| |
| DATA is a pointer to a struct cse_basic_block_data, that is used to |
| describe the path. |
| It is filled with a queue of basic blocks, starting with FIRST_BB |
| and following a trace through the CFG. |
| |
| If all paths starting at FIRST_BB have been followed, or no new path |
| starting at FIRST_BB can be constructed, this function returns FALSE. |
| Otherwise, DATA->path is filled and the function returns TRUE indicating |
| that a path to follow was found. |
| |
| If FOLLOW_JUMPS is false, the maximum path length is 1 and the only |
| block in the path will be FIRST_BB. */ |
| |
| static bool |
| cse_find_path (basic_block first_bb, struct cse_basic_block_data *data, |
| int follow_jumps) |
| { |
| basic_block bb; |
| edge e; |
| int path_size; |
| |
| bitmap_set_bit (cse_visited_basic_blocks, first_bb->index); |
| |
| /* See if there is a previous path. */ |
| path_size = data->path_size; |
| |
| /* There is a previous path. Make sure it started with FIRST_BB. */ |
| if (path_size) |
| gcc_assert (data->path[0].bb == first_bb); |
| |
| /* There was only one basic block in the last path. Clear the path and |
| return, so that paths starting at another basic block can be tried. */ |
| if (path_size == 1) |
| { |
| path_size = 0; |
| goto done; |
| } |
| |
| /* If the path was empty from the beginning, construct a new path. */ |
| if (path_size == 0) |
| data->path[path_size++].bb = first_bb; |
| else |
| { |
| /* Otherwise, path_size must be equal to or greater than 2, because |
| a previous path exists that is at least two basic blocks long. |
| |
| Update the previous branch path, if any. If the last branch was |
| previously along the branch edge, take the fallthrough edge now. */ |
| while (path_size >= 2) |
| { |
| basic_block last_bb_in_path, previous_bb_in_path; |
| edge e; |
| |
| --path_size; |
| last_bb_in_path = data->path[path_size].bb; |
| previous_bb_in_path = data->path[path_size - 1].bb; |
| |
| /* If we previously followed a path along the branch edge, try |
| the fallthru edge now. */ |
| if (EDGE_COUNT (previous_bb_in_path->succs) == 2 |
| && any_condjump_p (BB_END (previous_bb_in_path)) |
| && (e = find_edge (previous_bb_in_path, last_bb_in_path)) |
| && e == BRANCH_EDGE (previous_bb_in_path)) |
| { |
| bb = FALLTHRU_EDGE (previous_bb_in_path)->dest; |
| if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun) |
| && single_pred_p (bb) |
| /* We used to assert here that we would only see blocks |
| that we have not visited yet. But we may end up |
| visiting basic blocks twice if the CFG has changed |
| in this run of cse_main, because when the CFG changes |
| the topological sort of the CFG also changes. A basic |
| blocks that previously had more than two predecessors |
| may now have a single predecessor, and become part of |
| a path that starts at another basic block. |
| |
| We still want to visit each basic block only once, so |
| halt the path here if we have already visited BB. */ |
| && !bitmap_bit_p (cse_visited_basic_blocks, bb->index)) |
| { |
| bitmap_set_bit (cse_visited_basic_blocks, bb->index); |
| data->path[path_size++].bb = bb; |
| break; |
| } |
| } |
| |
| data->path[path_size].bb = NULL; |
| } |
| |
| /* If only one block remains in the path, bail. */ |
| if (path_size == 1) |
| { |
| path_size = 0; |
| goto done; |
| } |
| } |
| |
| /* Extend the path if possible. */ |
| if (follow_jumps) |
| { |
| bb = data->path[path_size - 1].bb; |
| while (bb && path_size < param_max_cse_path_length) |
| { |
| if (single_succ_p (bb)) |
| e = single_succ_edge (bb); |
| else if (EDGE_COUNT (bb->succs) == 2 |
| && any_condjump_p (BB_END (bb))) |
| { |
| /* First try to follow the branch. If that doesn't lead |
| to a useful path, follow the fallthru edge. */ |
| e = BRANCH_EDGE (bb); |
| if (!single_pred_p (e->dest)) |
| e = FALLTHRU_EDGE (bb); |
| } |
| else |
| e = NULL; |
| |
| if (e |
| && !((e->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label) |
| && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun) |
| && single_pred_p (e->dest) |
| /* Avoid visiting basic blocks twice. The large comment |
| above explains why this can happen. */ |
| && !bitmap_bit_p (cse_visited_basic_blocks, e->dest->index)) |
| { |
| basic_block bb2 = e->dest; |
| bitmap_set_bit (cse_visited_basic_blocks, bb2->index); |
| data->path[path_size++].bb = bb2; |
| bb = bb2; |
| } |
| else |
| bb = NULL; |
| } |
| } |
| |
| done: |
| data->path_size = path_size; |
| return path_size != 0; |
| } |
| |
| /* Dump the path in DATA to file F. NSETS is the number of sets |
| in the path. */ |
| |
| static void |
| cse_dump_path (struct cse_basic_block_data *data, int nsets, FILE *f) |
| { |
| int path_entry; |
| |
| fprintf (f, ";; Following path with %d sets: ", nsets); |
| for (path_entry = 0; path_entry < data->path_size; path_entry++) |
| fprintf (f, "%d ", (data->path[path_entry].bb)->index); |
| fputc ('\n', f); |
| fflush (f); |
| } |
| |
| |
| /* Return true if BB has exception handling successor edges. */ |
| |
| static bool |
| have_eh_succ_edges (basic_block bb) |
| { |
| edge e; |
| edge_iterator ei; |
| |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| if (e->flags & EDGE_EH) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /* Scan to the end of the path described by DATA. Return an estimate of |
| the total number of SETs of all insns in the path. */ |
| |
| static void |
| cse_prescan_path (struct cse_basic_block_data *data) |
| { |
| int nsets = 0; |
| int path_size = data->path_size; |
| int path_entry; |
| |
| /* Scan to end of each basic block in the path. */ |
| for (path_entry = 0; path_entry < path_size; path_entry++) |
| { |
| basic_block bb; |
| rtx_insn *insn; |
| |
| bb = data->path[path_entry].bb; |
| |
| FOR_BB_INSNS (bb, insn) |
| { |
| if (!INSN_P (insn)) |
| continue; |
| |
| /* A PARALLEL can have lots of SETs in it, |
| especially if it is really an ASM_OPERANDS. */ |
| if (GET_CODE (PATTERN (insn)) == PARALLEL) |
| nsets += XVECLEN (PATTERN (insn), 0); |
| else |
| nsets += 1; |
| } |
| } |
| |
| data->nsets = nsets; |
| } |
| |
| /* Return true if the pattern of INSN uses a LABEL_REF for which |
| there isn't a REG_LABEL_OPERAND note. */ |
| |
| static bool |
| check_for_label_ref (rtx_insn *insn) |
| { |
| /* If this insn uses a LABEL_REF and there isn't a REG_LABEL_OPERAND |
| note for it, we must rerun jump since it needs to place the note. If |
| this is a LABEL_REF for a CODE_LABEL that isn't in the insn chain, |
| don't do this since no REG_LABEL_OPERAND will be added. */ |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL) |
| { |
| const_rtx x = *iter; |
| if (GET_CODE (x) == LABEL_REF |
| && !LABEL_REF_NONLOCAL_P (x) |
| && (!JUMP_P (insn) |
| || !label_is_jump_target_p (label_ref_label (x), insn)) |
| && LABEL_P (label_ref_label (x)) |
| && INSN_UID (label_ref_label (x)) != 0 |
| && !find_reg_note (insn, REG_LABEL_OPERAND, label_ref_label (x))) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Process a single extended basic block described by EBB_DATA. */ |
| |
| static void |
| cse_extended_basic_block (struct cse_basic_block_data *ebb_data) |
| { |
| int path_size = ebb_data->path_size; |
| int path_entry; |
| int num_insns = 0; |
| |
| /* Allocate the space needed by qty_table. */ |
| qty_table = XNEWVEC (struct qty_table_elem, max_qty); |
| |
| new_basic_block (); |
| cse_ebb_live_in = df_get_live_in (ebb_data->path[0].bb); |
| cse_ebb_live_out = df_get_live_out (ebb_data->path[path_size - 1].bb); |
| for (path_entry = 0; path_entry < path_size; path_entry++) |
| { |
| basic_block bb; |
| rtx_insn *insn; |
| |
| bb = ebb_data->path[path_entry].bb; |
| |
| /* Invalidate recorded information for eh regs if there is an EH |
| edge pointing to that bb. */ |
| if (bb_has_eh_pred (bb)) |
| { |
| df_ref def; |
| |
| FOR_EACH_ARTIFICIAL_DEF (def, bb->index) |
| if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) |
| invalidate (DF_REF_REG (def), GET_MODE (DF_REF_REG (def))); |
| } |
| |
| optimize_this_for_speed_p = optimize_bb_for_speed_p (bb); |
| FOR_BB_INSNS (bb, insn) |
| { |
| /* If we have processed 1,000 insns, flush the hash table to |
| avoid extreme quadratic behavior. We must not include NOTEs |
| in the count since there may be more of them when generating |
| debugging information. If we clear the table at different |
| times, code generated with -g -O might be different than code |
| generated with -O but not -g. |
| |
| FIXME: This is a real kludge and needs to be done some other |
| way. */ |
| if (NONDEBUG_INSN_P (insn) |
| && num_insns++ > param_max_cse_insns) |
| { |
| flush_hash_table (); |
| num_insns = 0; |
| } |
| |
| if (INSN_P (insn)) |
| { |
| /* Process notes first so we have all notes in canonical forms |
| when looking for duplicate operations. */ |
| bool changed = false; |
| for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1)) |
| if (REG_NOTE_KIND (note) == REG_EQUAL) |
| { |
| rtx newval = cse_process_note (XEXP (note, 0)); |
| if (newval != XEXP (note, 0)) |
| { |
| XEXP (note, 0) = newval; |
| changed = true; |
| } |
| } |
| if (changed) |
| df_notes_rescan (insn); |
| |
| cse_insn (insn); |
| |
| /* If we haven't already found an insn where we added a LABEL_REF, |
| check this one. */ |
| if (INSN_P (insn) && !recorded_label_ref |
| && check_for_label_ref (insn)) |
| recorded_label_ref = true; |
| } |
| } |
| |
| /* With non-call exceptions, we are not always able to update |
| the CFG properly inside cse_insn. So clean up possibly |
| redundant EH edges here. */ |
| if (cfun->can_throw_non_call_exceptions && have_eh_succ_edges (bb)) |
| cse_cfg_altered |= purge_dead_edges (bb); |
| |
| /* If we changed a conditional jump, we may have terminated |
| the path we are following. Check that by verifying that |
| the edge we would take still exists. If the edge does |
| not exist anymore, purge the remainder of the path. |
| Note that this will cause us to return to the caller. */ |
| if (path_entry < path_size - 1) |
| { |
| basic_block next_bb = ebb_data->path[path_entry + 1].bb; |
| if (!find_edge (bb, next_bb)) |
| { |
| do |
| { |
| path_size--; |
| |
| /* If we truncate the path, we must also reset the |
| visited bit on the remaining blocks in the path, |
| or we will never visit them at all. */ |
| bitmap_clear_bit (cse_visited_basic_blocks, |
| ebb_data->path[path_size].bb->index); |
| ebb_data->path[path_size].bb = NULL; |
| } |
| while (path_size - 1 != path_entry); |
| ebb_data->path_size = path_size; |
| } |
| } |
| |
| /* If this is a conditional jump insn, record any known |
| equivalences due to the condition being tested. */ |
| insn = BB_END (bb); |
| if (path_entry < path_size - 1 |
| && EDGE_COUNT (bb->succs) == 2 |
| && JUMP_P (insn) |
| && single_set (insn) |
| && any_condjump_p (insn) |
| /* single_set may return non-NULL even for multiple sets |
| if there are REG_UNUSED notes. record_jump_equiv only |
| looks at pc_set and doesn't consider other sets that |
| could affect the value, and the recorded equivalence |
| can extend the lifetime of the compared REG, so use |
| also !multiple_sets check to verify it is exactly one |
| set. */ |
| && !multiple_sets (insn)) |
| { |
| basic_block next_bb = ebb_data->path[path_entry + 1].bb; |
| bool taken = (next_bb == BRANCH_EDGE (bb)->dest); |
| record_jump_equiv (insn, taken); |
| } |
| } |
| |
| gcc_assert (next_qty <= max_qty); |
| |
| free (qty_table); |
| } |
| |
| |
| /* Perform cse on the instructions of a function. |
| F is the first instruction. |
| NREGS is one plus the highest pseudo-reg number used in the instruction. |
| |
| Return 2 if jump optimizations should be redone due to simplifications |
| in conditional jump instructions. |
| Return 1 if the CFG should be cleaned up because it has been modified. |
| Return 0 otherwise. */ |
| |
| static int |
| cse_main (rtx_insn *f ATTRIBUTE_UNUSED, int nregs) |
| { |
| struct cse_basic_block_data ebb_data; |
| basic_block bb; |
| int *rc_order = XNEWVEC (int, last_basic_block_for_fn (cfun)); |
| int i, n_blocks; |
| |
| /* CSE doesn't use dominane info but can invalidate it in different ways. |
| For simplicity free dominance info here. */ |
| free_dominance_info (CDI_DOMINATORS); |
| |
| df_set_flags (DF_LR_RUN_DCE); |
| df_note_add_problem (); |
| df_analyze (); |
| df_set_flags (DF_DEFER_INSN_RESCAN); |
| |
| reg_scan (get_insns (), max_reg_num ()); |
| init_cse_reg_info (nregs); |
| |
| ebb_data.path = XNEWVEC (struct branch_path, |
| param_max_cse_path_length); |
| |
| cse_cfg_altered = false; |
| cse_jumps_altered = false; |
| recorded_label_ref = false; |
| ebb_data.path_size = 0; |
| ebb_data.nsets = 0; |
| rtl_hooks = cse_rtl_hooks; |
| |
| init_recog (); |
| init_alias_analysis (); |
| |
| reg_eqv_table = XNEWVEC (struct reg_eqv_elem, nregs); |
| |
| /* Set up the table of already visited basic blocks. */ |
| cse_visited_basic_blocks = sbitmap_alloc (last_basic_block_for_fn (cfun)); |
| bitmap_clear (cse_visited_basic_blocks); |
| |
| /* Loop over basic blocks in reverse completion order (RPO), |
| excluding the ENTRY and EXIT blocks. */ |
| n_blocks = pre_and_rev_post_order_compute (NULL, rc_order, false); |
| i = 0; |
| while (i < n_blocks) |
| { |
| /* Find the first block in the RPO queue that we have not yet |
| processed before. */ |
| do |
| { |
| bb = BASIC_BLOCK_FOR_FN (cfun, rc_order[i++]); |
| } |
| while (bitmap_bit_p (cse_visited_basic_blocks, bb->index) |
| && i < n_blocks); |
| |
| /* Find all paths starting with BB, and process them. */ |
| while (cse_find_path (bb, &ebb_data, flag_cse_follow_jumps)) |
| { |
| /* Pre-scan the path. */ |
| cse_prescan_path (&ebb_data); |
| |
| /* If this basic block has no sets, skip it. */ |
| if (ebb_data.nsets == 0) |
| continue; |
| |
| /* Get a reasonable estimate for the maximum number of qty's |
| needed for this path. For this, we take the number of sets |
| and multiply that by MAX_RECOG_OPERANDS. */ |
| max_qty = ebb_data.nsets * MAX_RECOG_OPERANDS; |
| |
| /* Dump the path we're about to process. */ |
| if (dump_file) |
| cse_dump_path (&ebb_data, ebb_data.nsets, dump_file); |
| |
| cse_extended_basic_block (&ebb_data); |
| } |
| } |
| |
| /* Clean up. */ |
| end_alias_analysis (); |
| free (reg_eqv_table); |
| free (ebb_data.path); |
| sbitmap_free (cse_visited_basic_blocks); |
| free (rc_order); |
| rtl_hooks = general_rtl_hooks; |
| |
| if (cse_jumps_altered || recorded_label_ref) |
| return 2; |
| else if (cse_cfg_altered) |
| return 1; |
| else |
| return 0; |
| } |
| |
| /* Count the number of times registers are used (not set) in X. |
| COUNTS is an array in which we accumulate the count, INCR is how much |
| we count each register usage. |
| |
| Don't count a usage of DEST, which is the SET_DEST of a SET which |
| contains X in its SET_SRC. This is because such a SET does not |
| modify the liveness of DEST. |
| DEST is set to pc_rtx for a trapping insn, or for an insn with side effects. |
| We must then count uses of a SET_DEST regardless, because the insn can't be |
| deleted here. */ |
| |
| static void |
| count_reg_usage (rtx x, int *counts, rtx dest, int incr) |
| { |
| enum rtx_code code; |
| rtx note; |
| const char *fmt; |
| int i, j; |
| |
| if (x == 0) |
| return; |
| |
| switch (code = GET_CODE (x)) |
| { |
| case REG: |
| if (x != dest) |
| counts[REGNO (x)] += incr; |
| return; |
| |
| case PC: |
| case CONST: |
| CASE_CONST_ANY: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| return; |
| |
| case CLOBBER: |
| /* If we are clobbering a MEM, mark any registers inside the address |
| as being used. */ |
| if (MEM_P (XEXP (x, 0))) |
| count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr); |
| return; |
| |
| case SET: |
| /* Unless we are setting a REG, count everything in SET_DEST. */ |
| if (!REG_P (SET_DEST (x))) |
| count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr); |
| count_reg_usage (SET_SRC (x), counts, |
| dest ? dest : SET_DEST (x), |
| incr); |
| return; |
| |
| case DEBUG_INSN: |
| return; |
| |
| case CALL_INSN: |
| case INSN: |
| case JUMP_INSN: |
| /* We expect dest to be NULL_RTX here. If the insn may throw, |
| or if it cannot be deleted due to side-effects, mark this fact |
| by setting DEST to pc_rtx. */ |
| if ((!cfun->can_delete_dead_exceptions && !insn_nothrow_p (x)) |
| || side_effects_p (PATTERN (x))) |
| dest = pc_rtx; |
| if (code == CALL_INSN) |
| count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr); |
| count_reg_usage (PATTERN (x), counts, dest, incr); |
| |
| /* Things used in a REG_EQUAL note aren't dead since loop may try to |
| use them. */ |
| |
| note = find_reg_equal_equiv_note (x); |
| if (note) |
| { |
| rtx eqv = XEXP (note, 0); |
| |
| if (GET_CODE (eqv) == EXPR_LIST) |
| /* This REG_EQUAL note describes the result of a function call. |
| Process all the arguments. */ |
| do |
| { |
| count_reg_usage (XEXP (eqv, 0), counts, dest, incr); |
| eqv = XEXP (eqv, 1); |
| } |
| while (eqv && GET_CODE (eqv) == EXPR_LIST); |
| else |
| count_reg_usage (eqv, counts, dest, incr); |
| } |
| return; |
| |
| case EXPR_LIST: |
| if (REG_NOTE_KIND (x) == REG_EQUAL |
| || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE) |
| /* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)), |
| involving registers in the address. */ |
| || GET_CODE (XEXP (x, 0)) == CLOBBER) |
| count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr); |
| |
| count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr); |
| return; |
| |
| case ASM_OPERANDS: |
| /* Iterate over just the inputs, not the constraints as well. */ |
| for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) |
| count_reg_usage (ASM_OPERANDS_INPUT (x, i), counts, dest, incr); |
| return; |
| |
| case INSN_LIST: |
| case INT_LIST: |
| gcc_unreachable (); |
| |
| default: |
| break; |
| } |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| count_reg_usage (XEXP (x, i), counts, dest, incr); |
| else if (fmt[i] == 'E') |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| count_reg_usage (XVECEXP (x, i, j), counts, dest, incr); |
| } |
| } |
| |
| /* Return true if X is a dead register. */ |
| |
| static inline bool |
| is_dead_reg (const_rtx x, int *counts) |
| { |
| return (REG_P (x) |
| && REGNO (x) >= FIRST_PSEUDO_REGISTER |
| && counts[REGNO (x)] == 0); |
| } |
| |
| /* Return true if set is live. */ |
| static bool |
| set_live_p (rtx set, int *counts) |
| { |
| if (set_noop_p (set)) |
| return false; |
| |
| if (!is_dead_reg (SET_DEST (set), counts) |
| || side_effects_p (SET_SRC (set))) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return true if insn is live. */ |
| |
| static bool |
| insn_live_p (rtx_insn *insn, int *counts) |
| { |
| int i; |
| if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn)) |
| return true; |
| else if (GET_CODE (PATTERN (insn)) == SET) |
| return set_live_p (PATTERN (insn), counts); |
| else if (GET_CODE (PATTERN (insn)) == PARALLEL) |
| { |
| for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) |
| { |
| rtx elt = XVECEXP (PATTERN (insn), 0, i); |
| |
| if (GET_CODE (elt) == SET) |
| { |
| if (set_live_p (elt, counts)) |
| return true; |
| } |
| else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE) |
| return true; |
| } |
| return false; |
| } |
| else if (DEBUG_INSN_P (insn)) |
| { |
| if (DEBUG_MARKER_INSN_P (insn)) |
| return true; |
| |
| if (DEBUG_BIND_INSN_P (insn) |
| && TREE_VISITED (INSN_VAR_LOCATION_DECL (insn))) |
| return false; |
| |
| return true; |
| } |
| else |
| return true; |
| } |
| |
| /* Count the number of stores into pseudo. Callback for note_stores. */ |
| |
| static void |
| count_stores (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data) |
| { |
| int *counts = (int *) data; |
| if (REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER) |
| counts[REGNO (x)]++; |
| } |
| |
| /* Return if DEBUG_INSN pattern PAT needs to be reset because some dead |
| pseudo doesn't have a replacement. COUNTS[X] is zero if register X |
| is dead and REPLACEMENTS[X] is null if it has no replacemenet. |
| Set *SEEN_REPL to true if we see a dead register that does have |
| a replacement. */ |
| |
| static bool |
| is_dead_debug_insn (const_rtx pat, int *counts, rtx *replacements, |
| bool *seen_repl) |
| { |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, pat, NONCONST) |
| { |
| const_rtx x = *iter; |
| if (is_dead_reg (x, counts)) |
| { |
| if (replacements && replacements[REGNO (x)] != NULL_RTX) |
| *seen_repl = true; |
| else |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Replace a dead pseudo in a DEBUG_INSN with replacement DEBUG_EXPR. |
| Callback for simplify_replace_fn_rtx. */ |
| |
| static rtx |
| replace_dead_reg (rtx x, const_rtx old_rtx ATTRIBUTE_UNUSED, void *data) |
| { |
| rtx *replacements = (rtx *) data; |
| |
| if (REG_P (x) |
| && REGNO (x) >= FIRST_PSEUDO_REGISTER |
| && replacements[REGNO (x)] != NULL_RTX) |
| { |
| if (GET_MODE (x) == GET_MODE (replacements[REGNO (x)])) |
| return replacements[REGNO (x)]; |
| return lowpart_subreg (GET_MODE (x), replacements[REGNO (x)], |
| GET_MODE (replacements[REGNO (x)])); |
| } |
| return NULL_RTX; |
| } |
| |
| /* Scan all the insns and delete any that are dead; i.e., they store a register |
| that is never used or they copy a register to itself. |
| |
| This is used to remove insns made obviously dead by cse, loop or other |
| optimizations. It improves the heuristics in loop since it won't try to |
| move dead invariants out of loops or make givs for dead quantities. The |
| remaining passes of the compilation are also sped up. */ |
| |
| int |
| delete_trivially_dead_insns (rtx_insn *insns, int nreg) |
| { |
| int *counts; |
| rtx_insn *insn, *prev; |
| rtx *replacements = NULL; |
| int ndead = 0; |
| |
| timevar_push (TV_DELETE_TRIVIALLY_DEAD); |
| /* First count the number of times each register is used. */ |
| if (MAY_HAVE_DEBUG_BIND_INSNS) |
| { |
| counts = XCNEWVEC (int, nreg * 3); |
| for (insn = insns; insn; insn = NEXT_INSN (insn)) |
| if (DEBUG_BIND_INSN_P (insn)) |
| { |
| count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, |
| NULL_RTX, 1); |
| TREE_VISITED (INSN_VAR_LOCATION_DECL (insn)) = 0; |
| } |
| else if (INSN_P (insn)) |
| { |
| count_reg_usage (insn, counts, NULL_RTX, 1); |
| note_stores (insn, count_stores, counts + nreg * 2); |
| } |
| /* If there can be debug insns, COUNTS are 3 consecutive arrays. |
| First one counts how many times each pseudo is used outside |
| of debug insns, second counts how many times each pseudo is |
| used in debug insns and third counts how many times a pseudo |
| is stored. */ |
| } |
| else |
| { |
| counts = XCNEWVEC (int, nreg); |
| for (insn = insns; insn; insn = NEXT_INSN (insn)) |
| if (INSN_P (insn)) |
| count_reg_usage (insn, counts, NULL_RTX, 1); |
| /* If no debug insns can be present, COUNTS is just an array |
| which counts how many times each pseudo is used. */ |
| } |
| /* Pseudo PIC register should be considered as used due to possible |
| new usages generated. */ |
| if (!reload_completed |
| && pic_offset_table_rtx |
| && REGNO (pic_offset_table_rtx) >= FIRST_PSEUDO_REGISTER) |
| counts[REGNO (pic_offset_table_rtx)]++; |
| /* Go from the last insn to the first and delete insns that only set unused |
| registers or copy a register to itself. As we delete an insn, remove |
| usage counts for registers it uses. |
| |
| The first jump optimization pass may leave a real insn as the last |
| insn in the function. We must not skip that insn or we may end |
| up deleting code that is not really dead. |
| |
| If some otherwise unused register is only used in DEBUG_INSNs, |
| try to create a DEBUG_EXPR temporary and emit a DEBUG_INSN before |
| the setter. Then go through DEBUG_INSNs and if a DEBUG_EXPR |
| has been created for the unused register, replace it with |
| the DEBUG_EXPR, otherwise reset the DEBUG_INSN. */ |
| auto_vec<tree, 32> later_debug_set_vars; |
| for (insn = get_last_insn (); insn; insn = prev) |
| { |
| int live_insn = 0; |
| |
| prev = PREV_INSN (insn); |
| if (!INSN_P (insn)) |
| continue; |
| |
| live_insn = insn_live_p (insn, counts); |
| |
| /* If this is a dead insn, delete it and show registers in it aren't |
| being used. */ |
| |
| if (! live_insn && dbg_cnt (delete_trivial_dead)) |
| { |
| if (DEBUG_INSN_P (insn)) |
| { |
| if (DEBUG_BIND_INSN_P (insn)) |
| count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, |
| NULL_RTX, -1); |
| } |
| else |
| { |
| rtx set; |
| if (MAY_HAVE_DEBUG_BIND_INSNS |
| && (set = single_set (insn)) != NULL_RTX |
| && is_dead_reg (SET_DEST (set), counts) |
| /* Used at least once in some DEBUG_INSN. */ |
| && counts[REGNO (SET_DEST (set)) + nreg] > 0 |
| /* And set exactly once. */ |
| && counts[REGNO (SET_DEST (set)) + nreg * 2] == 1 |
| && !side_effects_p (SET_SRC (set)) |
| && asm_noperands (PATTERN (insn)) < 0) |
| { |
| rtx dval, bind_var_loc; |
| rtx_insn *bind; |
| |
| /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL). */ |
| dval = make_debug_expr_from_rtl (SET_DEST (set)); |
| |
| /* Emit a debug bind insn before the insn in which |
| reg dies. */ |
| bind_var_loc = |
| gen_rtx_VAR_LOCATION (GET_MODE (SET_DEST (set)), |
| DEBUG_EXPR_TREE_DECL (dval), |
| SET_SRC (set), |
| VAR_INIT_STATUS_INITIALIZED); |
| count_reg_usage (bind_var_loc, counts + nreg, NULL_RTX, 1); |
| |
| bind = emit_debug_insn_before (bind_var_loc, insn); |
| df_insn_rescan (bind); |
| |
| if (replacements == NULL) |
| replacements = XCNEWVEC (rtx, nreg); |
| replacements[REGNO (SET_DEST (set))] = dval; |
| } |
| |
| count_reg_usage (insn, counts, NULL_RTX, -1); |
| ndead++; |
| } |
| cse_cfg_altered |= delete_insn_and_edges (insn); |
| } |
| else |
| { |
| if (!DEBUG_INSN_P (insn) || DEBUG_MARKER_INSN_P (insn)) |
| { |
| for (tree var : later_debug_set_vars) |
| TREE_VISITED (var) = 0; |
| later_debug_set_vars.truncate (0); |
| } |
| else if (DEBUG_BIND_INSN_P (insn) |
| && !TREE_VISITED (INSN_VAR_LOCATION_DECL (insn))) |
| { |
| later_debug_set_vars.safe_push (INSN_VAR_LOCATION_DECL (insn)); |
| TREE_VISITED (INSN_VAR_LOCATION_DECL (insn)) = 1; |
| } |
| } |
| } |
| |
| if (MAY_HAVE_DEBUG_BIND_INSNS) |
| { |
| for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) |
| if (DEBUG_BIND_INSN_P (insn)) |
| { |
| /* If this debug insn references a dead register that wasn't replaced |
| with an DEBUG_EXPR, reset the DEBUG_INSN. */ |
| bool seen_repl = false; |
| if (is_dead_debug_insn (INSN_VAR_LOCATION_LOC (insn), |
| counts, replacements, &seen_repl)) |
| { |
| INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); |
| df_insn_rescan (insn); |
| } |
| else if (seen_repl) |
| { |
| INSN_VAR_LOCATION_LOC (insn) |
| = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn), |
| NULL_RTX, replace_dead_reg, |
| replacements); |
| df_insn_rescan (insn); |
| } |
| } |
| free (replacements); |
| } |
| |
| if (dump_file && ndead) |
| fprintf (dump_file, "Deleted %i trivially dead insns\n", |
| ndead); |
| /* Clean up. */ |
| free (counts); |
| timevar_pop (TV_DELETE_TRIVIALLY_DEAD); |
| return ndead; |
| } |
| |
| /* If LOC contains references to NEWREG in a different mode, change them |
| to use NEWREG instead. */ |
| |
| static void |
| cse_change_cc_mode (subrtx_ptr_iterator::array_type &array, |
| rtx *loc, rtx_insn *insn, rtx newreg) |
| { |
| FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST) |
| { |
| rtx *loc = *iter; |
| rtx x = *loc; |
| if (x |
| && REG_P (x) |
| && REGNO (x) == REGNO (newreg) |
| && GET_MODE (x) != GET_MODE (newreg)) |
| { |
| validate_change (insn, loc, newreg, 1); |
| iter.skip_subrtxes (); |
| } |
| } |
| } |
| |
| /* Change the mode of any reference to the register REGNO (NEWREG) to |
| GET_MODE (NEWREG) in INSN. */ |
| |
| static void |
| cse_change_cc_mode_insn (rtx_insn *insn, rtx newreg) |
| { |
| int success; |
| |
| if (!INSN_P (insn)) |
| return; |
| |
| subrtx_ptr_iterator::array_type array; |
| cse_change_cc_mode (array, &PATTERN (insn), insn, newreg); |
| cse_change_cc_mode (array, ®_NOTES (insn), insn, newreg); |
| |
| /* If the following assertion was triggered, there is most probably |
| something wrong with the cc_modes_compatible back end function. |
| CC modes only can be considered compatible if the insn - with the mode |
| replaced by any of the compatible modes - can still be recognized. */ |
| success = apply_change_group (); |
| gcc_assert (success); |
| } |
| |
| /* Change the mode of any reference to the register REGNO (NEWREG) to |
| GET_MODE (NEWREG), starting at START. Stop before END. Stop at |
| any instruction which modifies NEWREG. */ |
| |
| static void |
| cse_change_cc_mode_insns (rtx_insn *start, rtx_insn *end, rtx newreg) |
| { |
| rtx_insn *insn; |
| |
| for (insn = start; insn != end; insn = NEXT_INSN (insn)) |
| { |
| if (! INSN_P (insn)) |
| continue; |
| |
| if (reg_set_p (newreg, insn)) |
| return; |
| |
| cse_change_cc_mode_insn (insn, newreg); |
| } |
| } |
| |
| /* BB is a basic block which finishes with CC_REG as a condition code |
| register which is set to CC_SRC. Look through the successors of BB |
| to find blocks which have a single predecessor (i.e., this one), |
| and look through those blocks for an assignment to CC_REG which is |
| equivalent to CC_SRC. CAN_CHANGE_MODE indicates whether we are |
| permitted to change the mode of CC_SRC to a compatible mode. This |
| returns VOIDmode if no equivalent assignments were found. |
| Otherwise it returns the mode which CC_SRC should wind up with. |
| ORIG_BB should be the same as BB in the outermost cse_cc_succs call, |
| but is passed unmodified down to recursive calls in order to prevent |
| endless recursion. |
| |
| The main complexity in this function is handling the mode issues. |
| We may have more than one duplicate which we can eliminate, and we |
| try to find a mode which will work for multiple duplicates. */ |
| |
| static machine_mode |
| cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src, |
| bool can_change_mode) |
| { |
| bool found_equiv; |
| machine_mode mode; |
| unsigned int insn_count; |
| edge e; |
| rtx_insn *insns[2]; |
| machine_mode modes[2]; |
| rtx_insn *last_insns[2]; |
| unsigned int i; |
| rtx newreg; |
| edge_iterator ei; |
| |
| /* We expect to have two successors. Look at both before picking |
| the final mode for the comparison. If we have more successors |
| (i.e., some sort of table jump, although that seems unlikely), |
| then we require all beyond the first two to use the same |
| mode. */ |
| |
| found_equiv = false; |
| mode = GET_MODE (cc_src); |
| insn_count = 0; |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| { |
| rtx_insn *insn; |
| rtx_insn *end; |
| |
| if (e->flags & EDGE_COMPLEX) |
| continue; |
| |
| if (EDGE_COUNT (e->dest->preds) != 1 |
| || e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun) |
| /* Avoid endless recursion on unreachable blocks. */ |
| || e->dest == orig_bb) |
| continue; |
| |
| end = NEXT_INSN (BB_END (e->dest)); |
| for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn)) |
| { |
| rtx set; |
| |
| if (! INSN_P (insn)) |
| continue; |
| |
| /* If CC_SRC is modified, we have to stop looking for |
| something which uses it. */ |
| if (modified_in_p (cc_src, insn)) |
| break; |
| |
| /* Check whether INSN sets CC_REG to CC_SRC. */ |
| set = single_set (insn); |
| if (set |
| && REG_P (SET_DEST (set)) |
| && REGNO (SET_DEST (set)) == REGNO (cc_reg)) |
| { |
| bool found; |
| machine_mode set_mode; |
| machine_mode comp_mode; |
| |
| found = false; |
| set_mode = GET_MODE (SET_SRC (set)); |
| comp_mode = set_mode; |
| if (rtx_equal_p (cc_src, SET_SRC (set))) |
| found = true; |
| else if (GET_CODE (cc_src) == COMPARE |
| && GET_CODE (SET_SRC (set)) == COMPARE |
| && mode != set_mode |
| && rtx_equal_p (XEXP (cc_src, 0), |
| XEXP (SET_SRC (set), 0)) |
| && rtx_equal_p (XEXP (cc_src, 1), |
| XEXP (SET_SRC (set), 1))) |
| |
| { |
| comp_mode = targetm.cc_modes_compatible (mode, set_mode); |
| if (comp_mode != VOIDmode |
| && (can_change_mode || comp_mode == mode)) |
| found = true; |
| } |
| |
| if (found) |
| { |
| found_equiv = true; |
| if (insn_count < ARRAY_SIZE (insns)) |
| { |
| insns[insn_count] = insn; |
| modes[insn_count] = set_mode; |
| last_insns[insn_count] = end; |
| ++insn_count; |
| |
| if (mode != comp_mode) |
| { |
| gcc_assert (can_change_mode); |
| mode = comp_mode; |
| |
| /* The modified insn will be re-recognized later. */ |
| PUT_MODE (cc_src, mode); |
| } |
| } |
| else |
| { |
| if (set_mode != mode) |
| { |
| /* We found a matching expression in the |
| wrong mode, but we don't have room to |
| store it in the array. Punt. This case |
| should be rare. */ |
| break; |
| } |
| /* INSN sets CC_REG to a value equal to CC_SRC |
| with the right mode. We can simply delete |
| it. */ |
| delete_insn (insn); |
| } |
| |
| /* We found an instruction to delete. Keep looking, |
| in the hopes of finding a three-way jump. */ |
| continue; |
| } |
| |
| /* We found an instruction which sets the condition |
| code, so don't look any farther. */ |
| break; |
| } |
| |
| /* If INSN sets CC_REG in some other way, don't look any |
| farther. */ |
| if (reg_set_p (cc_reg, insn)) |
| break; |
| } |
| |
| /* If we fell off the bottom of the block, we can keep looking |
| through successors. We pass CAN_CHANGE_MODE as false because |
| we aren't prepared to handle compatibility between the |
| further blocks and this block. */ |
| if (insn == end) |
| { |
| machine_mode submode; |
| |
| submode = cse_cc_succs (e->dest, orig_bb, cc_reg, cc_src, false); |
| if (submode != VOIDmode) |
| { |
| gcc_assert (submode == mode); |
| found_equiv = true; |
| can_change_mode = false; |
| } |
| } |
| } |
| |
| if (! found_equiv) |
| return VOIDmode; |
| |
| /* Now INSN_COUNT is the number of instructions we found which set |
| CC_REG to a value equivalent to CC_SRC. The instructions are in |
| INSNS. The modes used by those instructions are in MODES. */ |
| |
| newreg = NULL_RTX; |
| for (i = 0; i < insn_count; ++i) |
| { |
| if (modes[i] != mode) |
| { |
| /* We need to change the mode of CC_REG in INSNS[i] and |
| subsequent instructions. */ |
| if (! newreg) |
| { |
| if (GET_MODE (cc_reg) == mode) |
| newreg = cc_reg; |
| else |
| newreg = gen_rtx_REG (mode, REGNO (cc_reg)); |
| } |
| cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i], |
| newreg); |
| } |
| |
| cse_cfg_altered |= delete_insn_and_edges (insns[i]); |
| } |
| |
| return mode; |
| } |
| |
| /* If we have a fixed condition code register (or two), walk through |
| the instructions and try to eliminate duplicate assignments. */ |
| |
| static void |
| cse_condition_code_reg (void) |
| { |
| unsigned int cc_regno_1; |
| unsigned int cc_regno_2; |
| rtx cc_reg_1; |
| rtx cc_reg_2; |
| basic_block bb; |
| |
| if (! targetm.fixed_condition_code_regs (&cc_regno_1, &cc_regno_2)) |
| return; |
| |
| cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1); |
| if (cc_regno_2 != INVALID_REGNUM) |
| cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2); |
| else |
| cc_reg_2 = NULL_RTX; |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| rtx_insn *last_insn; |
| rtx cc_reg; |
| rtx_insn *insn; |
| rtx_insn *cc_src_insn; |
| rtx cc_src; |
| machine_mode mode; |
| machine_mode orig_mode; |
| |
| /* Look for blocks which end with a conditional jump based on a |
| condition code register. Then look for the instruction which |
| sets the condition code register. Then look through the |
| successor blocks for instructions which set the condition |
| code register to the same value. There are other possible |
| uses of the condition code register, but these are by far the |
| most common and the ones which we are most likely to be able |
| to optimize. */ |
| |
| last_insn = BB_END (bb); |
| if (!JUMP_P (last_insn)) |
| continue; |
| |
| if (reg_referenced_p (cc_reg_1, PATTERN (last_insn))) |
| cc_reg = cc_reg_1; |
| else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn))) |
| cc_reg = cc_reg_2; |
| else |
| continue; |
| |
| cc_src_insn = NULL; |
| cc_src = NULL_RTX; |
| for (insn = PREV_INSN (last_insn); |
| insn && insn != PREV_INSN (BB_HEAD (bb)); |
| insn = PREV_INSN (insn)) |
| { |
| rtx set; |
| |
| if (! INSN_P (insn)) |
| continue; |
| set = single_set (insn); |
| if (set |
| && REG_P (SET_DEST (set)) |
| && REGNO (SET_DEST (set)) == REGNO (cc_reg)) |
| { |
| cc_src_insn = insn; |
| cc_src = SET_SRC (set); |
| break; |
| } |
| else if (reg_set_p (cc_reg, insn)) |
| break; |
| } |
| |
| if (! cc_src_insn) |
| continue; |
| |
| if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn))) |
| continue; |
| |
| /* Now CC_REG is a condition code register used for a |
| conditional jump at the end of the block, and CC_SRC, in |
| CC_SRC_INSN, is the value to which that condition code |
| register is set, and CC_SRC is still meaningful at the end of |
| the basic block. */ |
| |
| orig_mode = GET_MODE (cc_src); |
| mode = cse_cc_succs (bb, bb, cc_reg, cc_src, true); |
| if (mode != VOIDmode) |
| { |
| gcc_assert (mode == GET_MODE (cc_src)); |
| if (mode != orig_mode) |
| { |
| rtx newreg = gen_rtx_REG (mode, REGNO (cc_reg)); |
| |
| cse_change_cc_mode_insn (cc_src_insn, newreg); |
| |
| /* Do the same in the following insns that use the |
| current value of CC_REG within BB. */ |
| cse_change_cc_mode_insns (NEXT_INSN (cc_src_insn), |
| NEXT_INSN (last_insn), |
| newreg); |
| } |
| } |
| } |
| } |
| |
| |
| /* Perform common subexpression elimination. Nonzero value from |
| `cse_main' means that jumps were simplified and some code may now |
| be unreachable, so do jump optimization again. */ |
| static unsigned int |
| rest_of_handle_cse (void) |
| { |
| int tem; |
| |
| if (dump_file) |
| dump_flow_info (dump_file, dump_flags); |
| |
| tem = cse_main (get_insns (), max_reg_num ()); |
| |
| /* If we are not running more CSE passes, then we are no longer |
| expecting CSE to be run. But always rerun it in a cheap mode. */ |
| cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse; |
| |
| if (tem == 2) |
| { |
| timevar_push (TV_JUMP); |
| rebuild_jump_labels (get_insns ()); |
| cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); |
| timevar_pop (TV_JUMP); |
| } |
| else if (tem == 1 || optimize > 1) |
| cse_cfg_altered |= cleanup_cfg (0); |
| |
| return 0; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_cse = |
| { |
| RTL_PASS, /* type */ |
| "cse1", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_CSE, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| TODO_df_finish, /* todo_flags_finish */ |
| }; |
| |
| class pass_cse : public rtl_opt_pass |
| { |
| public: |
| pass_cse (gcc::context *ctxt) |
| : rtl_opt_pass (pass_data_cse, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| bool gate (function *) final override { return optimize > 0; } |
| unsigned int execute (function *) final override |
| { |
| return rest_of_handle_cse (); |
| } |
| |
| }; // class pass_cse |
| |
| } // anon namespace |
| |
| rtl_opt_pass * |
| make_pass_cse (gcc::context *ctxt) |
| { |
| return new pass_cse (ctxt); |
| } |
| |
| |
| /* Run second CSE pass after loop optimizations. */ |
| static unsigned int |
| rest_of_handle_cse2 (void) |
| { |
| int tem; |
| |
| if (dump_file) |
| dump_flow_info (dump_file, dump_flags); |
| |
| tem = cse_main (get_insns (), max_reg_num ()); |
| |
| /* Run a pass to eliminate duplicated assignments to condition code |
| registers. We have to run this after bypass_jumps, because it |
| makes it harder for that pass to determine whether a jump can be |
| bypassed safely. */ |
| cse_condition_code_reg (); |
| |
| delete_trivially_dead_insns (get_insns (), max_reg_num ()); |
| |
| if (tem == 2) |
| { |
| timevar_push (TV_JUMP); |
| rebuild_jump_labels (get_insns ()); |
| cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); |
| timevar_pop (TV_JUMP); |
| } |
| else if (tem == 1 || cse_cfg_altered) |
| cse_cfg_altered |= cleanup_cfg (0); |
| |
| cse_not_expected = 1; |
| return 0; |
| } |
| |
| |
| namespace { |
| |
| const pass_data pass_data_cse2 = |
| { |
| RTL_PASS, /* type */ |
| "cse2", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_CSE2, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| TODO_df_finish, /* todo_flags_finish */ |
| }; |
| |
| class pass_cse2 : public rtl_opt_pass |
| { |
| public: |
| pass_cse2 (gcc::context *ctxt) |
| : rtl_opt_pass (pass_data_cse2, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| bool gate (function *) final override |
| { |
| return optimize > 0 && flag_rerun_cse_after_loop; |
| } |
| |
| unsigned int execute (function *) final override |
| { |
| return rest_of_handle_cse2 (); |
| } |
| |
| }; // class pass_cse2 |
| |
| } // anon namespace |
| |
| rtl_opt_pass * |
| make_pass_cse2 (gcc::context *ctxt) |
| { |
| return new pass_cse2 (ctxt); |
| } |
| |
| /* Run second CSE pass after loop optimizations. */ |
| static unsigned int |
| rest_of_handle_cse_after_global_opts (void) |
| { |
| int save_cfj; |
| int tem; |
| |
| /* We only want to do local CSE, so don't follow jumps. */ |
| save_cfj = flag_cse_follow_jumps; |
| flag_cse_follow_jumps = 0; |
| |
| rebuild_jump_labels (get_insns ()); |
| tem = cse_main (get_insns (), max_reg_num ()); |
| cse_cfg_altered |= purge_all_dead_edges (); |
| delete_trivially_dead_insns (get_insns (), max_reg_num ()); |
| |
| cse_not_expected = !flag_rerun_cse_after_loop; |
| |
| /* If cse altered any jumps, rerun jump opts to clean things up. */ |
| if (tem == 2) |
| { |
| timevar_push (TV_JUMP); |
| rebuild_jump_labels (get_insns ()); |
| cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED); |
| timevar_pop (TV_JUMP); |
| } |
| else if (tem == 1 || cse_cfg_altered) |
| cse_cfg_altered |= cleanup_cfg (0); |
| |
| flag_cse_follow_jumps = save_cfj; |
| return 0; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_cse_after_global_opts = |
| { |
| RTL_PASS, /* type */ |
| "cse_local", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_CSE, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| TODO_df_finish, /* todo_flags_finish */ |
| }; |
| |
| class pass_cse_after_global_opts : public rtl_opt_pass |
| { |
| public: |
| pass_cse_after_global_opts (gcc::context *ctxt) |
| : rtl_opt_pass (pass_data_cse_after_global_opts, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| bool gate (function *) final override |
| { |
| return optimize > 0 && flag_rerun_cse_after_global_opts; |
| } |
| |
| unsigned int execute (function *) final override |
| { |
| return rest_of_handle_cse_after_global_opts (); |
| } |
| |
| }; // class pass_cse_after_global_opts |
| |
| } // anon namespace |
| |
| rtl_opt_pass * |
| make_pass_cse_after_global_opts (gcc::context *ctxt) |
| { |
| return new pass_cse_after_global_opts (ctxt); |
| } |