| /* Optimize by combining instructions for GNU compiler. |
| Copyright (C) 1987-2021 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/>. */ |
| |
| /* This module is essentially the "combiner" phase of the U. of Arizona |
| Portable Optimizer, but redone to work on our list-structured |
| representation for RTL instead of their string representation. |
| |
| The LOG_LINKS of each insn identify the most recent assignment |
| to each REG used in the insn. It is a list of previous insns, |
| each of which contains a SET for a REG that is used in this insn |
| and not used or set in between. LOG_LINKs never cross basic blocks. |
| They were set up by the preceding pass (lifetime analysis). |
| |
| We try to combine each pair of insns joined by a logical link. |
| We also try to combine triplets of insns A, B and C when C has |
| a link back to B and B has a link back to A. Likewise for a |
| small number of quadruplets of insns A, B, C and D for which |
| there's high likelihood of success. |
| |
| LOG_LINKS does not have links for use of the CC0. They don't |
| need to, because the insn that sets the CC0 is always immediately |
| before the insn that tests it. So we always regard a branch |
| insn as having a logical link to the preceding insn. The same is true |
| for an insn explicitly using CC0. |
| |
| We check (with modified_between_p) to avoid combining in such a way |
| as to move a computation to a place where its value would be different. |
| |
| Combination is done by mathematically substituting the previous |
| insn(s) values for the regs they set into the expressions in |
| the later insns that refer to these regs. If the result is a valid insn |
| for our target machine, according to the machine description, |
| we install it, delete the earlier insns, and update the data flow |
| information (LOG_LINKS and REG_NOTES) for what we did. |
| |
| There are a few exceptions where the dataflow information isn't |
| completely updated (however this is only a local issue since it is |
| regenerated before the next pass that uses it): |
| |
| - reg_live_length is not updated |
| - reg_n_refs is not adjusted in the rare case when a register is |
| no longer required in a computation |
| - there are extremely rare cases (see distribute_notes) when a |
| REG_DEAD note is lost |
| - a LOG_LINKS entry that refers to an insn with multiple SETs may be |
| removed because there is no way to know which register it was |
| linking |
| |
| To simplify substitution, we combine only when the earlier insn(s) |
| consist of only a single assignment. To simplify updating afterward, |
| we never combine when a subroutine call appears in the middle. |
| |
| Since we do not represent assignments to CC0 explicitly except when that |
| is all an insn does, there is no LOG_LINKS entry in an insn that uses |
| the condition code for the insn that set the condition code. |
| Fortunately, these two insns must be consecutive. |
| Therefore, every JUMP_INSN is taken to have an implicit logical link |
| to the preceding insn. This is not quite right, since non-jumps can |
| also use the condition code; but in practice such insns would not |
| combine anyway. */ |
| |
| #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 "predict.h" |
| #include "df.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "optabs.h" |
| #include "regs.h" |
| #include "emit-rtl.h" |
| #include "recog.h" |
| #include "cgraph.h" |
| #include "stor-layout.h" |
| #include "cfgrtl.h" |
| #include "cfgcleanup.h" |
| /* Include expr.h after insn-config.h so we get HAVE_conditional_move. */ |
| #include "explow.h" |
| #include "insn-attr.h" |
| #include "rtlhooks-def.h" |
| #include "expr.h" |
| #include "tree-pass.h" |
| #include "valtrack.h" |
| #include "rtl-iter.h" |
| #include "print-rtl.h" |
| #include "function-abi.h" |
| |
| /* Number of attempts to combine instructions in this function. */ |
| |
| static int combine_attempts; |
| |
| /* Number of attempts that got as far as substitution in this function. */ |
| |
| static int combine_merges; |
| |
| /* Number of instructions combined with added SETs in this function. */ |
| |
| static int combine_extras; |
| |
| /* Number of instructions combined in this function. */ |
| |
| static int combine_successes; |
| |
| /* Totals over entire compilation. */ |
| |
| static int total_attempts, total_merges, total_extras, total_successes; |
| |
| /* combine_instructions may try to replace the right hand side of the |
| second instruction with the value of an associated REG_EQUAL note |
| before throwing it at try_combine. That is problematic when there |
| is a REG_DEAD note for a register used in the old right hand side |
| and can cause distribute_notes to do wrong things. This is the |
| second instruction if it has been so modified, null otherwise. */ |
| |
| static rtx_insn *i2mod; |
| |
| /* When I2MOD is nonnull, this is a copy of the old right hand side. */ |
| |
| static rtx i2mod_old_rhs; |
| |
| /* When I2MOD is nonnull, this is a copy of the new right hand side. */ |
| |
| static rtx i2mod_new_rhs; |
| |
| struct reg_stat_type { |
| /* Record last point of death of (hard or pseudo) register n. */ |
| rtx_insn *last_death; |
| |
| /* Record last point of modification of (hard or pseudo) register n. */ |
| rtx_insn *last_set; |
| |
| /* The next group of fields allows the recording of the last value assigned |
| to (hard or pseudo) register n. We use this information to see if an |
| operation being processed is redundant given a prior operation performed |
| on the register. For example, an `and' with a constant is redundant if |
| all the zero bits are already known to be turned off. |
| |
| We use an approach similar to that used by cse, but change it in the |
| following ways: |
| |
| (1) We do not want to reinitialize at each label. |
| (2) It is useful, but not critical, to know the actual value assigned |
| to a register. Often just its form is helpful. |
| |
| Therefore, we maintain the following fields: |
| |
| last_set_value the last value assigned |
| last_set_label records the value of label_tick when the |
| register was assigned |
| last_set_table_tick records the value of label_tick when a |
| value using the register is assigned |
| last_set_invalid set to nonzero when it is not valid |
| to use the value of this register in some |
| register's value |
| |
| To understand the usage of these tables, it is important to understand |
| the distinction between the value in last_set_value being valid and |
| the register being validly contained in some other expression in the |
| table. |
| |
| (The next two parameters are out of date). |
| |
| reg_stat[i].last_set_value is valid if it is nonzero, and either |
| reg_n_sets[i] is 1 or reg_stat[i].last_set_label == label_tick. |
| |
| Register I may validly appear in any expression returned for the value |
| of another register if reg_n_sets[i] is 1. It may also appear in the |
| value for register J if reg_stat[j].last_set_invalid is zero, or |
| reg_stat[i].last_set_label < reg_stat[j].last_set_label. |
| |
| If an expression is found in the table containing a register which may |
| not validly appear in an expression, the register is replaced by |
| something that won't match, (clobber (const_int 0)). */ |
| |
| /* Record last value assigned to (hard or pseudo) register n. */ |
| |
| rtx last_set_value; |
| |
| /* Record the value of label_tick when an expression involving register n |
| is placed in last_set_value. */ |
| |
| int last_set_table_tick; |
| |
| /* Record the value of label_tick when the value for register n is placed in |
| last_set_value. */ |
| |
| int last_set_label; |
| |
| /* These fields are maintained in parallel with last_set_value and are |
| used to store the mode in which the register was last set, the bits |
| that were known to be zero when it was last set, and the number of |
| sign bits copies it was known to have when it was last set. */ |
| |
| unsigned HOST_WIDE_INT last_set_nonzero_bits; |
| char last_set_sign_bit_copies; |
| ENUM_BITFIELD(machine_mode) last_set_mode : 8; |
| |
| /* Set nonzero if references to register n in expressions should not be |
| used. last_set_invalid is set nonzero when this register is being |
| assigned to and last_set_table_tick == label_tick. */ |
| |
| char last_set_invalid; |
| |
| /* Some registers that are set more than once and used in more than one |
| basic block are nevertheless always set in similar ways. For example, |
| a QImode register may be loaded from memory in two places on a machine |
| where byte loads zero extend. |
| |
| We record in the following fields if a register has some leading bits |
| that are always equal to the sign bit, and what we know about the |
| nonzero bits of a register, specifically which bits are known to be |
| zero. |
| |
| If an entry is zero, it means that we don't know anything special. */ |
| |
| unsigned char sign_bit_copies; |
| |
| unsigned HOST_WIDE_INT nonzero_bits; |
| |
| /* Record the value of the label_tick when the last truncation |
| happened. The field truncated_to_mode is only valid if |
| truncation_label == label_tick. */ |
| |
| int truncation_label; |
| |
| /* Record the last truncation seen for this register. If truncation |
| is not a nop to this mode we might be able to save an explicit |
| truncation if we know that value already contains a truncated |
| value. */ |
| |
| ENUM_BITFIELD(machine_mode) truncated_to_mode : 8; |
| }; |
| |
| |
| static vec<reg_stat_type> reg_stat; |
| |
| /* One plus the highest pseudo for which we track REG_N_SETS. |
| regstat_init_n_sets_and_refs allocates the array for REG_N_SETS just once, |
| but during combine_split_insns new pseudos can be created. As we don't have |
| updated DF information in that case, it is hard to initialize the array |
| after growing. The combiner only cares about REG_N_SETS (regno) == 1, |
| so instead of growing the arrays, just assume all newly created pseudos |
| during combine might be set multiple times. */ |
| |
| static unsigned int reg_n_sets_max; |
| |
| /* Record the luid of the last insn that invalidated memory |
| (anything that writes memory, and subroutine calls, but not pushes). */ |
| |
| static int mem_last_set; |
| |
| /* Record the luid of the last CALL_INSN |
| so we can tell whether a potential combination crosses any calls. */ |
| |
| static int last_call_luid; |
| |
| /* When `subst' is called, this is the insn that is being modified |
| (by combining in a previous insn). The PATTERN of this insn |
| is still the old pattern partially modified and it should not be |
| looked at, but this may be used to examine the successors of the insn |
| to judge whether a simplification is valid. */ |
| |
| static rtx_insn *subst_insn; |
| |
| /* This is the lowest LUID that `subst' is currently dealing with. |
| get_last_value will not return a value if the register was set at or |
| after this LUID. If not for this mechanism, we could get confused if |
| I2 or I1 in try_combine were an insn that used the old value of a register |
| to obtain a new value. In that case, we might erroneously get the |
| new value of the register when we wanted the old one. */ |
| |
| static int subst_low_luid; |
| |
| /* This contains any hard registers that are used in newpat; reg_dead_at_p |
| must consider all these registers to be always live. */ |
| |
| static HARD_REG_SET newpat_used_regs; |
| |
| /* This is an insn to which a LOG_LINKS entry has been added. If this |
| insn is the earlier than I2 or I3, combine should rescan starting at |
| that location. */ |
| |
| static rtx_insn *added_links_insn; |
| |
| /* And similarly, for notes. */ |
| |
| static rtx_insn *added_notes_insn; |
| |
| /* Basic block in which we are performing combines. */ |
| static basic_block this_basic_block; |
| static bool optimize_this_for_speed_p; |
| |
| |
| /* Length of the currently allocated uid_insn_cost array. */ |
| |
| static int max_uid_known; |
| |
| /* The following array records the insn_cost for every insn |
| in the instruction stream. */ |
| |
| static int *uid_insn_cost; |
| |
| /* The following array records the LOG_LINKS for every insn in the |
| instruction stream as struct insn_link pointers. */ |
| |
| struct insn_link { |
| rtx_insn *insn; |
| unsigned int regno; |
| struct insn_link *next; |
| }; |
| |
| static struct insn_link **uid_log_links; |
| |
| static inline int |
| insn_uid_check (const_rtx insn) |
| { |
| int uid = INSN_UID (insn); |
| gcc_checking_assert (uid <= max_uid_known); |
| return uid; |
| } |
| |
| #define INSN_COST(INSN) (uid_insn_cost[insn_uid_check (INSN)]) |
| #define LOG_LINKS(INSN) (uid_log_links[insn_uid_check (INSN)]) |
| |
| #define FOR_EACH_LOG_LINK(L, INSN) \ |
| for ((L) = LOG_LINKS (INSN); (L); (L) = (L)->next) |
| |
| /* Links for LOG_LINKS are allocated from this obstack. */ |
| |
| static struct obstack insn_link_obstack; |
| |
| /* Allocate a link. */ |
| |
| static inline struct insn_link * |
| alloc_insn_link (rtx_insn *insn, unsigned int regno, struct insn_link *next) |
| { |
| struct insn_link *l |
| = (struct insn_link *) obstack_alloc (&insn_link_obstack, |
| sizeof (struct insn_link)); |
| l->insn = insn; |
| l->regno = regno; |
| l->next = next; |
| return l; |
| } |
| |
| /* Incremented for each basic block. */ |
| |
| static int label_tick; |
| |
| /* Reset to label_tick for each extended basic block in scanning order. */ |
| |
| static int label_tick_ebb_start; |
| |
| /* Mode used to compute significance in reg_stat[].nonzero_bits. It is the |
| largest integer mode that can fit in HOST_BITS_PER_WIDE_INT. */ |
| |
| static scalar_int_mode nonzero_bits_mode; |
| |
| /* Nonzero when reg_stat[].nonzero_bits and reg_stat[].sign_bit_copies can |
| be safely used. It is zero while computing them and after combine has |
| completed. This former test prevents propagating values based on |
| previously set values, which can be incorrect if a variable is modified |
| in a loop. */ |
| |
| static int nonzero_sign_valid; |
| |
| |
| /* Record one modification to rtl structure |
| to be undone by storing old_contents into *where. */ |
| |
| enum undo_kind { UNDO_RTX, UNDO_INT, UNDO_MODE, UNDO_LINKS }; |
| |
| struct undo |
| { |
| struct undo *next; |
| enum undo_kind kind; |
| union { rtx r; int i; machine_mode m; struct insn_link *l; } old_contents; |
| union { rtx *r; int *i; struct insn_link **l; } where; |
| }; |
| |
| /* Record a bunch of changes to be undone, up to MAX_UNDO of them. |
| num_undo says how many are currently recorded. |
| |
| other_insn is nonzero if we have modified some other insn in the process |
| of working on subst_insn. It must be verified too. */ |
| |
| struct undobuf |
| { |
| struct undo *undos; |
| struct undo *frees; |
| rtx_insn *other_insn; |
| }; |
| |
| static struct undobuf undobuf; |
| |
| /* Number of times the pseudo being substituted for |
| was found and replaced. */ |
| |
| static int n_occurrences; |
| |
| static rtx reg_nonzero_bits_for_combine (const_rtx, scalar_int_mode, |
| scalar_int_mode, |
| unsigned HOST_WIDE_INT *); |
| static rtx reg_num_sign_bit_copies_for_combine (const_rtx, scalar_int_mode, |
| scalar_int_mode, |
| unsigned int *); |
| static void do_SUBST (rtx *, rtx); |
| static void do_SUBST_INT (int *, int); |
| static void init_reg_last (void); |
| static void setup_incoming_promotions (rtx_insn *); |
| static void set_nonzero_bits_and_sign_copies (rtx, const_rtx, void *); |
| static int cant_combine_insn_p (rtx_insn *); |
| static int can_combine_p (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *, |
| rtx_insn *, rtx_insn *, rtx *, rtx *); |
| static int combinable_i3pat (rtx_insn *, rtx *, rtx, rtx, rtx, int, int, rtx *); |
| static int contains_muldiv (rtx); |
| static rtx_insn *try_combine (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *, |
| int *, rtx_insn *); |
| static void undo_all (void); |
| static void undo_commit (void); |
| static rtx *find_split_point (rtx *, rtx_insn *, bool); |
| static rtx subst (rtx, rtx, rtx, int, int, int); |
| static rtx combine_simplify_rtx (rtx, machine_mode, int, int); |
| static rtx simplify_if_then_else (rtx); |
| static rtx simplify_set (rtx); |
| static rtx simplify_logical (rtx); |
| static rtx expand_compound_operation (rtx); |
| static const_rtx expand_field_assignment (const_rtx); |
| static rtx make_extraction (machine_mode, rtx, HOST_WIDE_INT, |
| rtx, unsigned HOST_WIDE_INT, int, int, int); |
| static int get_pos_from_mask (unsigned HOST_WIDE_INT, |
| unsigned HOST_WIDE_INT *); |
| static rtx canon_reg_for_combine (rtx, rtx); |
| static rtx force_int_to_mode (rtx, scalar_int_mode, scalar_int_mode, |
| scalar_int_mode, unsigned HOST_WIDE_INT, int); |
| static rtx force_to_mode (rtx, machine_mode, |
| unsigned HOST_WIDE_INT, int); |
| static rtx if_then_else_cond (rtx, rtx *, rtx *); |
| static rtx known_cond (rtx, enum rtx_code, rtx, rtx); |
| static int rtx_equal_for_field_assignment_p (rtx, rtx, bool = false); |
| static rtx make_field_assignment (rtx); |
| static rtx apply_distributive_law (rtx); |
| static rtx distribute_and_simplify_rtx (rtx, int); |
| static rtx simplify_and_const_int_1 (scalar_int_mode, rtx, |
| unsigned HOST_WIDE_INT); |
| static rtx simplify_and_const_int (rtx, scalar_int_mode, rtx, |
| unsigned HOST_WIDE_INT); |
| static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code, |
| HOST_WIDE_INT, machine_mode, int *); |
| static rtx simplify_shift_const_1 (enum rtx_code, machine_mode, rtx, int); |
| static rtx simplify_shift_const (rtx, enum rtx_code, machine_mode, rtx, |
| int); |
| static int recog_for_combine (rtx *, rtx_insn *, rtx *); |
| static rtx gen_lowpart_for_combine (machine_mode, rtx); |
| static enum rtx_code simplify_compare_const (enum rtx_code, machine_mode, |
| rtx, rtx *); |
| static enum rtx_code simplify_comparison (enum rtx_code, rtx *, rtx *); |
| static void update_table_tick (rtx); |
| static void record_value_for_reg (rtx, rtx_insn *, rtx); |
| static void check_promoted_subreg (rtx_insn *, rtx); |
| static void record_dead_and_set_regs_1 (rtx, const_rtx, void *); |
| static void record_dead_and_set_regs (rtx_insn *); |
| static int get_last_value_validate (rtx *, rtx_insn *, int, int); |
| static rtx get_last_value (const_rtx); |
| static void reg_dead_at_p_1 (rtx, const_rtx, void *); |
| static int reg_dead_at_p (rtx, rtx_insn *); |
| static void move_deaths (rtx, rtx, int, rtx_insn *, rtx *); |
| static int reg_bitfield_target_p (rtx, rtx); |
| static void distribute_notes (rtx, rtx_insn *, rtx_insn *, rtx_insn *, rtx, rtx, rtx); |
| static void distribute_links (struct insn_link *); |
| static void mark_used_regs_combine (rtx); |
| static void record_promoted_value (rtx_insn *, rtx); |
| static bool unmentioned_reg_p (rtx, rtx); |
| static void record_truncated_values (rtx *, void *); |
| static bool reg_truncated_to_mode (machine_mode, const_rtx); |
| static rtx gen_lowpart_or_truncate (machine_mode, rtx); |
| |
| |
| /* It is not safe to use ordinary gen_lowpart in combine. |
| See comments in gen_lowpart_for_combine. */ |
| #undef RTL_HOOKS_GEN_LOWPART |
| #define RTL_HOOKS_GEN_LOWPART gen_lowpart_for_combine |
| |
| /* Our implementation of gen_lowpart never emits a new pseudo. */ |
| #undef RTL_HOOKS_GEN_LOWPART_NO_EMIT |
| #define RTL_HOOKS_GEN_LOWPART_NO_EMIT gen_lowpart_for_combine |
| |
| #undef RTL_HOOKS_REG_NONZERO_REG_BITS |
| #define RTL_HOOKS_REG_NONZERO_REG_BITS reg_nonzero_bits_for_combine |
| |
| #undef RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES |
| #define RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES reg_num_sign_bit_copies_for_combine |
| |
| #undef RTL_HOOKS_REG_TRUNCATED_TO_MODE |
| #define RTL_HOOKS_REG_TRUNCATED_TO_MODE reg_truncated_to_mode |
| |
| static const struct rtl_hooks combine_rtl_hooks = RTL_HOOKS_INITIALIZER; |
| |
| |
| /* Convenience wrapper for the canonicalize_comparison target hook. |
| Target hooks cannot use enum rtx_code. */ |
| static inline void |
| target_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1, |
| bool op0_preserve_value) |
| { |
| int code_int = (int)*code; |
| targetm.canonicalize_comparison (&code_int, op0, op1, op0_preserve_value); |
| *code = (enum rtx_code)code_int; |
| } |
| |
| /* Try to split PATTERN found in INSN. This returns NULL_RTX if |
| PATTERN cannot be split. Otherwise, it returns an insn sequence. |
| This is a wrapper around split_insns which ensures that the |
| reg_stat vector is made larger if the splitter creates a new |
| register. */ |
| |
| static rtx_insn * |
| combine_split_insns (rtx pattern, rtx_insn *insn) |
| { |
| rtx_insn *ret; |
| unsigned int nregs; |
| |
| ret = split_insns (pattern, insn); |
| nregs = max_reg_num (); |
| if (nregs > reg_stat.length ()) |
| reg_stat.safe_grow_cleared (nregs, true); |
| return ret; |
| } |
| |
| /* This is used by find_single_use to locate an rtx in LOC that |
| contains exactly one use of DEST, which is typically either a REG |
| or CC0. It returns a pointer to the innermost rtx expression |
| containing DEST. Appearances of DEST that are being used to |
| totally replace it are not counted. */ |
| |
| static rtx * |
| find_single_use_1 (rtx dest, rtx *loc) |
| { |
| rtx x = *loc; |
| enum rtx_code code = GET_CODE (x); |
| rtx *result = NULL; |
| rtx *this_result; |
| int i; |
| const char *fmt; |
| |
| switch (code) |
| { |
| case CONST: |
| case LABEL_REF: |
| case SYMBOL_REF: |
| CASE_CONST_ANY: |
| case CLOBBER: |
| return 0; |
| |
| case SET: |
| /* If the destination is anything other than CC0, PC, a REG or a SUBREG |
| of a REG that occupies all of the REG, the insn uses DEST if |
| it is mentioned in the destination or the source. Otherwise, we |
| need just check the source. */ |
| if (GET_CODE (SET_DEST (x)) != CC0 |
| && GET_CODE (SET_DEST (x)) != PC |
| && !REG_P (SET_DEST (x)) |
| && ! (GET_CODE (SET_DEST (x)) == SUBREG |
| && REG_P (SUBREG_REG (SET_DEST (x))) |
| && !read_modify_subreg_p (SET_DEST (x)))) |
| break; |
| |
| return find_single_use_1 (dest, &SET_SRC (x)); |
| |
| case MEM: |
| case SUBREG: |
| return find_single_use_1 (dest, &XEXP (x, 0)); |
| |
| default: |
| break; |
| } |
| |
| /* If it wasn't one of the common cases above, check each expression and |
| vector of this code. Look for a unique usage of DEST. */ |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if (dest == XEXP (x, i) |
| || (REG_P (dest) && REG_P (XEXP (x, i)) |
| && REGNO (dest) == REGNO (XEXP (x, i)))) |
| this_result = loc; |
| else |
| this_result = find_single_use_1 (dest, &XEXP (x, i)); |
| |
| if (result == NULL) |
| result = this_result; |
| else if (this_result) |
| /* Duplicate usage. */ |
| return NULL; |
| } |
| else if (fmt[i] == 'E') |
| { |
| int j; |
| |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| { |
| if (XVECEXP (x, i, j) == dest |
| || (REG_P (dest) |
| && REG_P (XVECEXP (x, i, j)) |
| && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) |
| this_result = loc; |
| else |
| this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); |
| |
| if (result == NULL) |
| result = this_result; |
| else if (this_result) |
| return NULL; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| /* See if DEST, produced in INSN, is used only a single time in the |
| sequel. If so, return a pointer to the innermost rtx expression in which |
| it is used. |
| |
| If PLOC is nonzero, *PLOC is set to the insn containing the single use. |
| |
| If DEST is cc0_rtx, we look only at the next insn. In that case, we don't |
| care about REG_DEAD notes or LOG_LINKS. |
| |
| Otherwise, we find the single use by finding an insn that has a |
| LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is |
| only referenced once in that insn, we know that it must be the first |
| and last insn referencing DEST. */ |
| |
| static rtx * |
| find_single_use (rtx dest, rtx_insn *insn, rtx_insn **ploc) |
| { |
| basic_block bb; |
| rtx_insn *next; |
| rtx *result; |
| struct insn_link *link; |
| |
| if (dest == cc0_rtx) |
| { |
| next = NEXT_INSN (insn); |
| if (next == 0 |
| || (!NONJUMP_INSN_P (next) && !JUMP_P (next))) |
| return 0; |
| |
| result = find_single_use_1 (dest, &PATTERN (next)); |
| if (result && ploc) |
| *ploc = next; |
| return result; |
| } |
| |
| if (!REG_P (dest)) |
| return 0; |
| |
| bb = BLOCK_FOR_INSN (insn); |
| for (next = NEXT_INSN (insn); |
| next && BLOCK_FOR_INSN (next) == bb; |
| next = NEXT_INSN (next)) |
| if (NONDEBUG_INSN_P (next) && dead_or_set_p (next, dest)) |
| { |
| FOR_EACH_LOG_LINK (link, next) |
| if (link->insn == insn && link->regno == REGNO (dest)) |
| break; |
| |
| if (link) |
| { |
| result = find_single_use_1 (dest, &PATTERN (next)); |
| if (ploc) |
| *ploc = next; |
| return result; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Substitute NEWVAL, an rtx expression, into INTO, a place in some |
| insn. The substitution can be undone by undo_all. If INTO is already |
| set to NEWVAL, do not record this change. Because computing NEWVAL might |
| also call SUBST, we have to compute it before we put anything into |
| the undo table. */ |
| |
| static void |
| do_SUBST (rtx *into, rtx newval) |
| { |
| struct undo *buf; |
| rtx oldval = *into; |
| |
| if (oldval == newval) |
| return; |
| |
| /* We'd like to catch as many invalid transformations here as |
| possible. Unfortunately, there are way too many mode changes |
| that are perfectly valid, so we'd waste too much effort for |
| little gain doing the checks here. Focus on catching invalid |
| transformations involving integer constants. */ |
| if (GET_MODE_CLASS (GET_MODE (oldval)) == MODE_INT |
| && CONST_INT_P (newval)) |
| { |
| /* Sanity check that we're replacing oldval with a CONST_INT |
| that is a valid sign-extension for the original mode. */ |
| gcc_assert (INTVAL (newval) |
| == trunc_int_for_mode (INTVAL (newval), GET_MODE (oldval))); |
| |
| /* Replacing the operand of a SUBREG or a ZERO_EXTEND with a |
| CONST_INT is not valid, because after the replacement, the |
| original mode would be gone. Unfortunately, we can't tell |
| when do_SUBST is called to replace the operand thereof, so we |
| perform this test on oldval instead, checking whether an |
| invalid replacement took place before we got here. */ |
| gcc_assert (!(GET_CODE (oldval) == SUBREG |
| && CONST_INT_P (SUBREG_REG (oldval)))); |
| gcc_assert (!(GET_CODE (oldval) == ZERO_EXTEND |
| && CONST_INT_P (XEXP (oldval, 0)))); |
| } |
| |
| if (undobuf.frees) |
| buf = undobuf.frees, undobuf.frees = buf->next; |
| else |
| buf = XNEW (struct undo); |
| |
| buf->kind = UNDO_RTX; |
| buf->where.r = into; |
| buf->old_contents.r = oldval; |
| *into = newval; |
| |
| buf->next = undobuf.undos, undobuf.undos = buf; |
| } |
| |
| #define SUBST(INTO, NEWVAL) do_SUBST (&(INTO), (NEWVAL)) |
| |
| /* Similar to SUBST, but NEWVAL is an int expression. Note that substitution |
| for the value of a HOST_WIDE_INT value (including CONST_INT) is |
| not safe. */ |
| |
| static void |
| do_SUBST_INT (int *into, int newval) |
| { |
| struct undo *buf; |
| int oldval = *into; |
| |
| if (oldval == newval) |
| return; |
| |
| if (undobuf.frees) |
| buf = undobuf.frees, undobuf.frees = buf->next; |
| else |
| buf = XNEW (struct undo); |
| |
| buf->kind = UNDO_INT; |
| buf->where.i = into; |
| buf->old_contents.i = oldval; |
| *into = newval; |
| |
| buf->next = undobuf.undos, undobuf.undos = buf; |
| } |
| |
| #define SUBST_INT(INTO, NEWVAL) do_SUBST_INT (&(INTO), (NEWVAL)) |
| |
| /* Similar to SUBST, but just substitute the mode. This is used when |
| changing the mode of a pseudo-register, so that any other |
| references to the entry in the regno_reg_rtx array will change as |
| well. */ |
| |
| static void |
| do_SUBST_MODE (rtx *into, machine_mode newval) |
| { |
| struct undo *buf; |
| machine_mode oldval = GET_MODE (*into); |
| |
| if (oldval == newval) |
| return; |
| |
| if (undobuf.frees) |
| buf = undobuf.frees, undobuf.frees = buf->next; |
| else |
| buf = XNEW (struct undo); |
| |
| buf->kind = UNDO_MODE; |
| buf->where.r = into; |
| buf->old_contents.m = oldval; |
| adjust_reg_mode (*into, newval); |
| |
| buf->next = undobuf.undos, undobuf.undos = buf; |
| } |
| |
| #define SUBST_MODE(INTO, NEWVAL) do_SUBST_MODE (&(INTO), (NEWVAL)) |
| |
| /* Similar to SUBST, but NEWVAL is a LOG_LINKS expression. */ |
| |
| static void |
| do_SUBST_LINK (struct insn_link **into, struct insn_link *newval) |
| { |
| struct undo *buf; |
| struct insn_link * oldval = *into; |
| |
| if (oldval == newval) |
| return; |
| |
| if (undobuf.frees) |
| buf = undobuf.frees, undobuf.frees = buf->next; |
| else |
| buf = XNEW (struct undo); |
| |
| buf->kind = UNDO_LINKS; |
| buf->where.l = into; |
| buf->old_contents.l = oldval; |
| *into = newval; |
| |
| buf->next = undobuf.undos, undobuf.undos = buf; |
| } |
| |
| #define SUBST_LINK(oldval, newval) do_SUBST_LINK (&oldval, newval) |
| |
| /* Subroutine of try_combine. Determine whether the replacement patterns |
| NEWPAT, NEWI2PAT and NEWOTHERPAT are cheaper according to insn_cost |
| than the original sequence I0, I1, I2, I3 and undobuf.other_insn. Note |
| that I0, I1 and/or NEWI2PAT may be NULL_RTX. Similarly, NEWOTHERPAT and |
| undobuf.other_insn may also both be NULL_RTX. Return false if the cost |
| of all the instructions can be estimated and the replacements are more |
| expensive than the original sequence. */ |
| |
| static bool |
| combine_validate_cost (rtx_insn *i0, rtx_insn *i1, rtx_insn *i2, rtx_insn *i3, |
| rtx newpat, rtx newi2pat, rtx newotherpat) |
| { |
| int i0_cost, i1_cost, i2_cost, i3_cost; |
| int new_i2_cost, new_i3_cost; |
| int old_cost, new_cost; |
| |
| /* Lookup the original insn_costs. */ |
| i2_cost = INSN_COST (i2); |
| i3_cost = INSN_COST (i3); |
| |
| if (i1) |
| { |
| i1_cost = INSN_COST (i1); |
| if (i0) |
| { |
| i0_cost = INSN_COST (i0); |
| old_cost = (i0_cost > 0 && i1_cost > 0 && i2_cost > 0 && i3_cost > 0 |
| ? i0_cost + i1_cost + i2_cost + i3_cost : 0); |
| } |
| else |
| { |
| old_cost = (i1_cost > 0 && i2_cost > 0 && i3_cost > 0 |
| ? i1_cost + i2_cost + i3_cost : 0); |
| i0_cost = 0; |
| } |
| } |
| else |
| { |
| old_cost = (i2_cost > 0 && i3_cost > 0) ? i2_cost + i3_cost : 0; |
| i1_cost = i0_cost = 0; |
| } |
| |
| /* If we have split a PARALLEL I2 to I1,I2, we have counted its cost twice; |
| correct that. */ |
| if (old_cost && i1 && INSN_UID (i1) == INSN_UID (i2)) |
| old_cost -= i1_cost; |
| |
| |
| /* Calculate the replacement insn_costs. */ |
| rtx tmp = PATTERN (i3); |
| PATTERN (i3) = newpat; |
| int tmpi = INSN_CODE (i3); |
| INSN_CODE (i3) = -1; |
| new_i3_cost = insn_cost (i3, optimize_this_for_speed_p); |
| PATTERN (i3) = tmp; |
| INSN_CODE (i3) = tmpi; |
| if (newi2pat) |
| { |
| tmp = PATTERN (i2); |
| PATTERN (i2) = newi2pat; |
| tmpi = INSN_CODE (i2); |
| INSN_CODE (i2) = -1; |
| new_i2_cost = insn_cost (i2, optimize_this_for_speed_p); |
| PATTERN (i2) = tmp; |
| INSN_CODE (i2) = tmpi; |
| new_cost = (new_i2_cost > 0 && new_i3_cost > 0) |
| ? new_i2_cost + new_i3_cost : 0; |
| } |
| else |
| { |
| new_cost = new_i3_cost; |
| new_i2_cost = 0; |
| } |
| |
| if (undobuf.other_insn) |
| { |
| int old_other_cost, new_other_cost; |
| |
| old_other_cost = INSN_COST (undobuf.other_insn); |
| tmp = PATTERN (undobuf.other_insn); |
| PATTERN (undobuf.other_insn) = newotherpat; |
| tmpi = INSN_CODE (undobuf.other_insn); |
| INSN_CODE (undobuf.other_insn) = -1; |
| new_other_cost = insn_cost (undobuf.other_insn, |
| optimize_this_for_speed_p); |
| PATTERN (undobuf.other_insn) = tmp; |
| INSN_CODE (undobuf.other_insn) = tmpi; |
| if (old_other_cost > 0 && new_other_cost > 0) |
| { |
| old_cost += old_other_cost; |
| new_cost += new_other_cost; |
| } |
| else |
| old_cost = 0; |
| } |
| |
| /* Disallow this combination if both new_cost and old_cost are greater than |
| zero, and new_cost is greater than old cost. */ |
| int reject = old_cost > 0 && new_cost > old_cost; |
| |
| if (dump_file) |
| { |
| fprintf (dump_file, "%s combination of insns ", |
| reject ? "rejecting" : "allowing"); |
| if (i0) |
| fprintf (dump_file, "%d, ", INSN_UID (i0)); |
| if (i1 && INSN_UID (i1) != INSN_UID (i2)) |
| fprintf (dump_file, "%d, ", INSN_UID (i1)); |
| fprintf (dump_file, "%d and %d\n", INSN_UID (i2), INSN_UID (i3)); |
| |
| fprintf (dump_file, "original costs "); |
| if (i0) |
| fprintf (dump_file, "%d + ", i0_cost); |
| if (i1 && INSN_UID (i1) != INSN_UID (i2)) |
| fprintf (dump_file, "%d + ", i1_cost); |
| fprintf (dump_file, "%d + %d = %d\n", i2_cost, i3_cost, old_cost); |
| |
| if (newi2pat) |
| fprintf (dump_file, "replacement costs %d + %d = %d\n", |
| new_i2_cost, new_i3_cost, new_cost); |
| else |
| fprintf (dump_file, "replacement cost %d\n", new_cost); |
| } |
| |
| if (reject) |
| return false; |
| |
| /* Update the uid_insn_cost array with the replacement costs. */ |
| INSN_COST (i2) = new_i2_cost; |
| INSN_COST (i3) = new_i3_cost; |
| if (i1) |
| { |
| INSN_COST (i1) = 0; |
| if (i0) |
| INSN_COST (i0) = 0; |
| } |
| |
| return true; |
| } |
| |
| |
| /* Delete any insns that copy a register to itself. |
| Return true if the CFG was changed. */ |
| |
| static bool |
| delete_noop_moves (void) |
| { |
| rtx_insn *insn, *next; |
| basic_block bb; |
| |
| bool edges_deleted = false; |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next) |
| { |
| next = NEXT_INSN (insn); |
| if (INSN_P (insn) && noop_move_p (insn)) |
| { |
| if (dump_file) |
| fprintf (dump_file, "deleting noop move %d\n", INSN_UID (insn)); |
| |
| edges_deleted |= delete_insn_and_edges (insn); |
| } |
| } |
| } |
| |
| return edges_deleted; |
| } |
| |
| |
| /* Return false if we do not want to (or cannot) combine DEF. */ |
| static bool |
| can_combine_def_p (df_ref def) |
| { |
| /* Do not consider if it is pre/post modification in MEM. */ |
| if (DF_REF_FLAGS (def) & DF_REF_PRE_POST_MODIFY) |
| return false; |
| |
| unsigned int regno = DF_REF_REGNO (def); |
| |
| /* Do not combine frame pointer adjustments. */ |
| if ((regno == FRAME_POINTER_REGNUM |
| && (!reload_completed || frame_pointer_needed)) |
| || (!HARD_FRAME_POINTER_IS_FRAME_POINTER |
| && regno == HARD_FRAME_POINTER_REGNUM |
| && (!reload_completed || frame_pointer_needed)) |
| || (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM |
| && regno == ARG_POINTER_REGNUM && fixed_regs[regno])) |
| return false; |
| |
| return true; |
| } |
| |
| /* Return false if we do not want to (or cannot) combine USE. */ |
| static bool |
| can_combine_use_p (df_ref use) |
| { |
| /* Do not consider the usage of the stack pointer by function call. */ |
| if (DF_REF_FLAGS (use) & DF_REF_CALL_STACK_USAGE) |
| return false; |
| |
| return true; |
| } |
| |
| /* Fill in log links field for all insns. */ |
| |
| static void |
| create_log_links (void) |
| { |
| basic_block bb; |
| rtx_insn **next_use; |
| rtx_insn *insn; |
| df_ref def, use; |
| |
| next_use = XCNEWVEC (rtx_insn *, max_reg_num ()); |
| |
| /* Pass through each block from the end, recording the uses of each |
| register and establishing log links when def is encountered. |
| Note that we do not clear next_use array in order to save time, |
| so we have to test whether the use is in the same basic block as def. |
| |
| There are a few cases below when we do not consider the definition or |
| usage -- these are taken from original flow.c did. Don't ask me why it is |
| done this way; I don't know and if it works, I don't want to know. */ |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| FOR_BB_INSNS_REVERSE (bb, insn) |
| { |
| if (!NONDEBUG_INSN_P (insn)) |
| continue; |
| |
| /* Log links are created only once. */ |
| gcc_assert (!LOG_LINKS (insn)); |
| |
| FOR_EACH_INSN_DEF (def, insn) |
| { |
| unsigned int regno = DF_REF_REGNO (def); |
| rtx_insn *use_insn; |
| |
| if (!next_use[regno]) |
| continue; |
| |
| if (!can_combine_def_p (def)) |
| continue; |
| |
| use_insn = next_use[regno]; |
| next_use[regno] = NULL; |
| |
| if (BLOCK_FOR_INSN (use_insn) != bb) |
| continue; |
| |
| /* flow.c claimed: |
| |
| We don't build a LOG_LINK for hard registers contained |
| in ASM_OPERANDs. If these registers get replaced, |
| we might wind up changing the semantics of the insn, |
| even if reload can make what appear to be valid |
| assignments later. */ |
| if (regno < FIRST_PSEUDO_REGISTER |
| && asm_noperands (PATTERN (use_insn)) >= 0) |
| continue; |
| |
| /* Don't add duplicate links between instructions. */ |
| struct insn_link *links; |
| FOR_EACH_LOG_LINK (links, use_insn) |
| if (insn == links->insn && regno == links->regno) |
| break; |
| |
| if (!links) |
| LOG_LINKS (use_insn) |
| = alloc_insn_link (insn, regno, LOG_LINKS (use_insn)); |
| } |
| |
| FOR_EACH_INSN_USE (use, insn) |
| if (can_combine_use_p (use)) |
| next_use[DF_REF_REGNO (use)] = insn; |
| } |
| } |
| |
| free (next_use); |
| } |
| |
| /* Walk the LOG_LINKS of insn B to see if we find a reference to A. Return |
| true if we found a LOG_LINK that proves that A feeds B. This only works |
| if there are no instructions between A and B which could have a link |
| depending on A, since in that case we would not record a link for B. |
| We also check the implicit dependency created by a cc0 setter/user |
| pair. */ |
| |
| static bool |
| insn_a_feeds_b (rtx_insn *a, rtx_insn *b) |
| { |
| struct insn_link *links; |
| FOR_EACH_LOG_LINK (links, b) |
| if (links->insn == a) |
| return true; |
| if (HAVE_cc0 && sets_cc0_p (a)) |
| return true; |
| return false; |
| } |
| |
| /* Main entry point for combiner. F is the first insn of the function. |
| NREGS is the first unused pseudo-reg number. |
| |
| Return nonzero if the CFG was changed (e.g. if the combiner has |
| turned an indirect jump instruction into a direct jump). */ |
| static int |
| combine_instructions (rtx_insn *f, unsigned int nregs) |
| { |
| rtx_insn *insn, *next; |
| rtx_insn *prev; |
| struct insn_link *links, *nextlinks; |
| rtx_insn *first; |
| basic_block last_bb; |
| |
| int new_direct_jump_p = 0; |
| |
| for (first = f; first && !NONDEBUG_INSN_P (first); ) |
| first = NEXT_INSN (first); |
| if (!first) |
| return 0; |
| |
| combine_attempts = 0; |
| combine_merges = 0; |
| combine_extras = 0; |
| combine_successes = 0; |
| |
| rtl_hooks = combine_rtl_hooks; |
| |
| reg_stat.safe_grow_cleared (nregs, true); |
| |
| init_recog_no_volatile (); |
| |
| /* Allocate array for insn info. */ |
| max_uid_known = get_max_uid (); |
| uid_log_links = XCNEWVEC (struct insn_link *, max_uid_known + 1); |
| uid_insn_cost = XCNEWVEC (int, max_uid_known + 1); |
| gcc_obstack_init (&insn_link_obstack); |
| |
| nonzero_bits_mode = int_mode_for_size (HOST_BITS_PER_WIDE_INT, 0).require (); |
| |
| /* Don't use reg_stat[].nonzero_bits when computing it. This can cause |
| problems when, for example, we have j <<= 1 in a loop. */ |
| |
| nonzero_sign_valid = 0; |
| label_tick = label_tick_ebb_start = 1; |
| |
| /* Scan all SETs and see if we can deduce anything about what |
| bits are known to be zero for some registers and how many copies |
| of the sign bit are known to exist for those registers. |
| |
| Also set any known values so that we can use it while searching |
| for what bits are known to be set. */ |
| |
| setup_incoming_promotions (first); |
| /* Allow the entry block and the first block to fall into the same EBB. |
| Conceptually the incoming promotions are assigned to the entry block. */ |
| last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); |
| |
| create_log_links (); |
| FOR_EACH_BB_FN (this_basic_block, cfun) |
| { |
| optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); |
| last_call_luid = 0; |
| mem_last_set = -1; |
| |
| label_tick++; |
| if (!single_pred_p (this_basic_block) |
| || single_pred (this_basic_block) != last_bb) |
| label_tick_ebb_start = label_tick; |
| last_bb = this_basic_block; |
| |
| FOR_BB_INSNS (this_basic_block, insn) |
| if (INSN_P (insn) && BLOCK_FOR_INSN (insn)) |
| { |
| rtx links; |
| |
| subst_low_luid = DF_INSN_LUID (insn); |
| subst_insn = insn; |
| |
| note_stores (insn, set_nonzero_bits_and_sign_copies, insn); |
| record_dead_and_set_regs (insn); |
| |
| if (AUTO_INC_DEC) |
| for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) |
| if (REG_NOTE_KIND (links) == REG_INC) |
| set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX, |
| insn); |
| |
| /* Record the current insn_cost of this instruction. */ |
| INSN_COST (insn) = insn_cost (insn, optimize_this_for_speed_p); |
| if (dump_file) |
| { |
| fprintf (dump_file, "insn_cost %d for ", INSN_COST (insn)); |
| dump_insn_slim (dump_file, insn); |
| } |
| } |
| } |
| |
| nonzero_sign_valid = 1; |
| |
| /* Now scan all the insns in forward order. */ |
| label_tick = label_tick_ebb_start = 1; |
| init_reg_last (); |
| setup_incoming_promotions (first); |
| last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); |
| int max_combine = param_max_combine_insns; |
| |
| FOR_EACH_BB_FN (this_basic_block, cfun) |
| { |
| rtx_insn *last_combined_insn = NULL; |
| |
| /* Ignore instruction combination in basic blocks that are going to |
| be removed as unreachable anyway. See PR82386. */ |
| if (EDGE_COUNT (this_basic_block->preds) == 0) |
| continue; |
| |
| optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); |
| last_call_luid = 0; |
| mem_last_set = -1; |
| |
| label_tick++; |
| if (!single_pred_p (this_basic_block) |
| || single_pred (this_basic_block) != last_bb) |
| label_tick_ebb_start = label_tick; |
| last_bb = this_basic_block; |
| |
| rtl_profile_for_bb (this_basic_block); |
| for (insn = BB_HEAD (this_basic_block); |
| insn != NEXT_INSN (BB_END (this_basic_block)); |
| insn = next ? next : NEXT_INSN (insn)) |
| { |
| next = 0; |
| if (!NONDEBUG_INSN_P (insn)) |
| continue; |
| |
| while (last_combined_insn |
| && (!NONDEBUG_INSN_P (last_combined_insn) |
| || last_combined_insn->deleted ())) |
| last_combined_insn = PREV_INSN (last_combined_insn); |
| if (last_combined_insn == NULL_RTX |
| || BLOCK_FOR_INSN (last_combined_insn) != this_basic_block |
| || DF_INSN_LUID (last_combined_insn) <= DF_INSN_LUID (insn)) |
| last_combined_insn = insn; |
| |
| /* See if we know about function return values before this |
| insn based upon SUBREG flags. */ |
| check_promoted_subreg (insn, PATTERN (insn)); |
| |
| /* See if we can find hardregs and subreg of pseudos in |
| narrower modes. This could help turning TRUNCATEs |
| into SUBREGs. */ |
| note_uses (&PATTERN (insn), record_truncated_values, NULL); |
| |
| /* Try this insn with each insn it links back to. */ |
| |
| FOR_EACH_LOG_LINK (links, insn) |
| if ((next = try_combine (insn, links->insn, NULL, |
| NULL, &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "two-insn combine", 1); |
| goto retry; |
| } |
| |
| /* Try each sequence of three linked insns ending with this one. */ |
| |
| if (max_combine >= 3) |
| FOR_EACH_LOG_LINK (links, insn) |
| { |
| rtx_insn *link = links->insn; |
| |
| /* If the linked insn has been replaced by a note, then there |
| is no point in pursuing this chain any further. */ |
| if (NOTE_P (link)) |
| continue; |
| |
| FOR_EACH_LOG_LINK (nextlinks, link) |
| if ((next = try_combine (insn, link, nextlinks->insn, |
| NULL, &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "three-insn combine", 1); |
| goto retry; |
| } |
| } |
| |
| /* Try to combine a jump insn that uses CC0 |
| with a preceding insn that sets CC0, and maybe with its |
| logical predecessor as well. |
| This is how we make decrement-and-branch insns. |
| We need this special code because data flow connections |
| via CC0 do not get entered in LOG_LINKS. */ |
| |
| if (HAVE_cc0 |
| && JUMP_P (insn) |
| && (prev = prev_nonnote_insn (insn)) != 0 |
| && NONJUMP_INSN_P (prev) |
| && sets_cc0_p (PATTERN (prev))) |
| { |
| if ((next = try_combine (insn, prev, NULL, NULL, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| goto retry; |
| |
| FOR_EACH_LOG_LINK (nextlinks, prev) |
| if ((next = try_combine (insn, prev, nextlinks->insn, |
| NULL, &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| goto retry; |
| } |
| |
| /* Do the same for an insn that explicitly references CC0. */ |
| if (HAVE_cc0 && NONJUMP_INSN_P (insn) |
| && (prev = prev_nonnote_insn (insn)) != 0 |
| && NONJUMP_INSN_P (prev) |
| && sets_cc0_p (PATTERN (prev)) |
| && GET_CODE (PATTERN (insn)) == SET |
| && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn)))) |
| { |
| if ((next = try_combine (insn, prev, NULL, NULL, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| goto retry; |
| |
| FOR_EACH_LOG_LINK (nextlinks, prev) |
| if ((next = try_combine (insn, prev, nextlinks->insn, |
| NULL, &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| goto retry; |
| } |
| |
| /* Finally, see if any of the insns that this insn links to |
| explicitly references CC0. If so, try this insn, that insn, |
| and its predecessor if it sets CC0. */ |
| if (HAVE_cc0) |
| { |
| FOR_EACH_LOG_LINK (links, insn) |
| if (NONJUMP_INSN_P (links->insn) |
| && GET_CODE (PATTERN (links->insn)) == SET |
| && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (links->insn))) |
| && (prev = prev_nonnote_insn (links->insn)) != 0 |
| && NONJUMP_INSN_P (prev) |
| && sets_cc0_p (PATTERN (prev)) |
| && (next = try_combine (insn, links->insn, |
| prev, NULL, &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| goto retry; |
| } |
| |
| /* Try combining an insn with two different insns whose results it |
| uses. */ |
| if (max_combine >= 3) |
| FOR_EACH_LOG_LINK (links, insn) |
| for (nextlinks = links->next; nextlinks; |
| nextlinks = nextlinks->next) |
| if ((next = try_combine (insn, links->insn, |
| nextlinks->insn, NULL, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| |
| { |
| statistics_counter_event (cfun, "three-insn combine", 1); |
| goto retry; |
| } |
| |
| /* Try four-instruction combinations. */ |
| if (max_combine >= 4) |
| FOR_EACH_LOG_LINK (links, insn) |
| { |
| struct insn_link *next1; |
| rtx_insn *link = links->insn; |
| |
| /* If the linked insn has been replaced by a note, then there |
| is no point in pursuing this chain any further. */ |
| if (NOTE_P (link)) |
| continue; |
| |
| FOR_EACH_LOG_LINK (next1, link) |
| { |
| rtx_insn *link1 = next1->insn; |
| if (NOTE_P (link1)) |
| continue; |
| /* I0 -> I1 -> I2 -> I3. */ |
| FOR_EACH_LOG_LINK (nextlinks, link1) |
| if ((next = try_combine (insn, link, link1, |
| nextlinks->insn, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "four-insn combine", 1); |
| goto retry; |
| } |
| /* I0, I1 -> I2, I2 -> I3. */ |
| for (nextlinks = next1->next; nextlinks; |
| nextlinks = nextlinks->next) |
| if ((next = try_combine (insn, link, link1, |
| nextlinks->insn, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "four-insn combine", 1); |
| goto retry; |
| } |
| } |
| |
| for (next1 = links->next; next1; next1 = next1->next) |
| { |
| rtx_insn *link1 = next1->insn; |
| if (NOTE_P (link1)) |
| continue; |
| /* I0 -> I2; I1, I2 -> I3. */ |
| FOR_EACH_LOG_LINK (nextlinks, link) |
| if ((next = try_combine (insn, link, link1, |
| nextlinks->insn, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "four-insn combine", 1); |
| goto retry; |
| } |
| /* I0 -> I1; I1, I2 -> I3. */ |
| FOR_EACH_LOG_LINK (nextlinks, link1) |
| if ((next = try_combine (insn, link, link1, |
| nextlinks->insn, |
| &new_direct_jump_p, |
| last_combined_insn)) != 0) |
| { |
| statistics_counter_event (cfun, "four-insn combine", 1); |
| goto retry; |
| } |
| } |
| } |
| |
| /* Try this insn with each REG_EQUAL note it links back to. */ |
| FOR_EACH_LOG_LINK (links, insn) |
| { |
| rtx set, note; |
| rtx_insn *temp = links->insn; |
| if ((set = single_set (temp)) != 0 |
| && (note = find_reg_equal_equiv_note (temp)) != 0 |
| && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST |
| && ! side_effects_p (SET_SRC (set)) |
| /* Avoid using a register that may already been marked |
| dead by an earlier instruction. */ |
| && ! unmentioned_reg_p (note, SET_SRC (set)) |
| && (GET_MODE (note) == VOIDmode |
| ? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set))) |
| : (GET_MODE (SET_DEST (set)) == GET_MODE (note) |
| && (GET_CODE (SET_DEST (set)) != ZERO_EXTRACT |
| || (GET_MODE (XEXP (SET_DEST (set), 0)) |
| == GET_MODE (note)))))) |
| { |
| /* Temporarily replace the set's source with the |
| contents of the REG_EQUAL note. The insn will |
| be deleted or recognized by try_combine. */ |
| rtx orig_src = SET_SRC (set); |
| rtx orig_dest = SET_DEST (set); |
| if (GET_CODE (SET_DEST (set)) == ZERO_EXTRACT) |
| SET_DEST (set) = XEXP (SET_DEST (set), 0); |
| SET_SRC (set) = note; |
| i2mod = temp; |
| i2mod_old_rhs = copy_rtx (orig_src); |
| i2mod_new_rhs = copy_rtx (note); |
| next = try_combine (insn, i2mod, NULL, NULL, |
| &new_direct_jump_p, |
| last_combined_insn); |
| i2mod = NULL; |
| if (next) |
| { |
| statistics_counter_event (cfun, "insn-with-note combine", 1); |
| goto retry; |
| } |
| SET_SRC (set) = orig_src; |
| SET_DEST (set) = orig_dest; |
| } |
| } |
| |
| if (!NOTE_P (insn)) |
| record_dead_and_set_regs (insn); |
| |
| retry: |
| ; |
| } |
| } |
| |
| default_rtl_profile (); |
| clear_bb_flags (); |
| new_direct_jump_p |= purge_all_dead_edges (); |
| new_direct_jump_p |= delete_noop_moves (); |
| |
| /* Clean up. */ |
| obstack_free (&insn_link_obstack, NULL); |
| free (uid_log_links); |
| free (uid_insn_cost); |
| reg_stat.release (); |
| |
| { |
| struct undo *undo, *next; |
| for (undo = undobuf.frees; undo; undo = next) |
| { |
| next = undo->next; |
| free (undo); |
| } |
| undobuf.frees = 0; |
| } |
| |
| total_attempts += combine_attempts; |
| total_merges += combine_merges; |
| total_extras += combine_extras; |
| total_successes += combine_successes; |
| |
| nonzero_sign_valid = 0; |
| rtl_hooks = general_rtl_hooks; |
| |
| /* Make recognizer allow volatile MEMs again. */ |
| init_recog (); |
| |
| return new_direct_jump_p; |
| } |
| |
| /* Wipe the last_xxx fields of reg_stat in preparation for another pass. */ |
| |
| static void |
| init_reg_last (void) |
| { |
| unsigned int i; |
| reg_stat_type *p; |
| |
| FOR_EACH_VEC_ELT (reg_stat, i, p) |
| memset (p, 0, offsetof (reg_stat_type, sign_bit_copies)); |
| } |
| |
| /* Set up any promoted values for incoming argument registers. */ |
| |
| static void |
| setup_incoming_promotions (rtx_insn *first) |
| { |
| tree arg; |
| bool strictly_local = false; |
| |
| for (arg = DECL_ARGUMENTS (current_function_decl); arg; |
| arg = DECL_CHAIN (arg)) |
| { |
| rtx x, reg = DECL_INCOMING_RTL (arg); |
| int uns1, uns3; |
| machine_mode mode1, mode2, mode3, mode4; |
| |
| /* Only continue if the incoming argument is in a register. */ |
| if (!REG_P (reg)) |
| continue; |
| |
| /* Determine, if possible, whether all call sites of the current |
| function lie within the current compilation unit. (This does |
| take into account the exporting of a function via taking its |
| address, and so forth.) */ |
| strictly_local |
| = cgraph_node::local_info_node (current_function_decl)->local; |
| |
| /* The mode and signedness of the argument before any promotions happen |
| (equal to the mode of the pseudo holding it at that stage). */ |
| mode1 = TYPE_MODE (TREE_TYPE (arg)); |
| uns1 = TYPE_UNSIGNED (TREE_TYPE (arg)); |
| |
| /* The mode and signedness of the argument after any source language and |
| TARGET_PROMOTE_PROTOTYPES-driven promotions. */ |
| mode2 = TYPE_MODE (DECL_ARG_TYPE (arg)); |
| uns3 = TYPE_UNSIGNED (DECL_ARG_TYPE (arg)); |
| |
| /* The mode and signedness of the argument as it is actually passed, |
| see assign_parm_setup_reg in function.c. */ |
| mode3 = promote_function_mode (TREE_TYPE (arg), mode1, &uns3, |
| TREE_TYPE (cfun->decl), 0); |
| |
| /* The mode of the register in which the argument is being passed. */ |
| mode4 = GET_MODE (reg); |
| |
| /* Eliminate sign extensions in the callee when: |
| (a) A mode promotion has occurred; */ |
| if (mode1 == mode3) |
| continue; |
| /* (b) The mode of the register is the same as the mode of |
| the argument as it is passed; */ |
| if (mode3 != mode4) |
| continue; |
| /* (c) There's no language level extension; */ |
| if (mode1 == mode2) |
| ; |
| /* (c.1) All callers are from the current compilation unit. If that's |
| the case we don't have to rely on an ABI, we only have to know |
| what we're generating right now, and we know that we will do the |
| mode1 to mode2 promotion with the given sign. */ |
| else if (!strictly_local) |
| continue; |
| /* (c.2) The combination of the two promotions is useful. This is |
| true when the signs match, or if the first promotion is unsigned. |
| In the later case, (sign_extend (zero_extend x)) is the same as |
| (zero_extend (zero_extend x)), so make sure to force UNS3 true. */ |
| else if (uns1) |
| uns3 = true; |
| else if (uns3) |
| continue; |
| |
| /* Record that the value was promoted from mode1 to mode3, |
| so that any sign extension at the head of the current |
| function may be eliminated. */ |
| x = gen_rtx_CLOBBER (mode1, const0_rtx); |
| x = gen_rtx_fmt_e ((uns3 ? ZERO_EXTEND : SIGN_EXTEND), mode3, x); |
| record_value_for_reg (reg, first, x); |
| } |
| } |
| |
| /* If MODE has a precision lower than PREC and SRC is a non-negative constant |
| that would appear negative in MODE, sign-extend SRC for use in nonzero_bits |
| because some machines (maybe most) will actually do the sign-extension and |
| this is the conservative approach. |
| |
| ??? For 2.5, try to tighten up the MD files in this regard instead of this |
| kludge. */ |
| |
| static rtx |
| sign_extend_short_imm (rtx src, machine_mode mode, unsigned int prec) |
| { |
| scalar_int_mode int_mode; |
| if (CONST_INT_P (src) |
| && is_a <scalar_int_mode> (mode, &int_mode) |
| && GET_MODE_PRECISION (int_mode) < prec |
| && INTVAL (src) > 0 |
| && val_signbit_known_set_p (int_mode, INTVAL (src))) |
| src = GEN_INT (INTVAL (src) | ~GET_MODE_MASK (int_mode)); |
| |
| return src; |
| } |
| |
| /* Update RSP for pseudo-register X from INSN's REG_EQUAL note (if one exists) |
| and SET. */ |
| |
| static void |
| update_rsp_from_reg_equal (reg_stat_type *rsp, rtx_insn *insn, const_rtx set, |
| rtx x) |
| { |
| rtx reg_equal_note = insn ? find_reg_equal_equiv_note (insn) : NULL_RTX; |
| unsigned HOST_WIDE_INT bits = 0; |
| rtx reg_equal = NULL, src = SET_SRC (set); |
| unsigned int num = 0; |
| |
| if (reg_equal_note) |
| reg_equal = XEXP (reg_equal_note, 0); |
| |
| if (SHORT_IMMEDIATES_SIGN_EXTEND) |
| { |
| src = sign_extend_short_imm (src, GET_MODE (x), BITS_PER_WORD); |
| if (reg_equal) |
| reg_equal = sign_extend_short_imm (reg_equal, GET_MODE (x), BITS_PER_WORD); |
| } |
| |
| /* Don't call nonzero_bits if it cannot change anything. */ |
| if (rsp->nonzero_bits != HOST_WIDE_INT_M1U) |
| { |
| machine_mode mode = GET_MODE (x); |
| if (GET_MODE_CLASS (mode) == MODE_INT |
| && HWI_COMPUTABLE_MODE_P (mode)) |
| mode = nonzero_bits_mode; |
| bits = nonzero_bits (src, mode); |
| if (reg_equal && bits) |
| bits &= nonzero_bits (reg_equal, mode); |
| rsp->nonzero_bits |= bits; |
| } |
| |
| /* Don't call num_sign_bit_copies if it cannot change anything. */ |
| if (rsp->sign_bit_copies != 1) |
| { |
| num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x)); |
| if (reg_equal && maybe_ne (num, GET_MODE_PRECISION (GET_MODE (x)))) |
| { |
| unsigned int numeq = num_sign_bit_copies (reg_equal, GET_MODE (x)); |
| if (num == 0 || numeq > num) |
| num = numeq; |
| } |
| if (rsp->sign_bit_copies == 0 || num < rsp->sign_bit_copies) |
| rsp->sign_bit_copies = num; |
| } |
| } |
| |
| /* Called via note_stores. If X is a pseudo that is narrower than |
| HOST_BITS_PER_WIDE_INT and is being set, record what bits are known zero. |
| |
| If we are setting only a portion of X and we can't figure out what |
| portion, assume all bits will be used since we don't know what will |
| be happening. |
| |
| Similarly, set how many bits of X are known to be copies of the sign bit |
| at all locations in the function. This is the smallest number implied |
| by any set of X. */ |
| |
| static void |
| set_nonzero_bits_and_sign_copies (rtx x, const_rtx set, void *data) |
| { |
| rtx_insn *insn = (rtx_insn *) data; |
| scalar_int_mode mode; |
| |
| if (REG_P (x) |
| && REGNO (x) >= FIRST_PSEUDO_REGISTER |
| /* If this register is undefined at the start of the file, we can't |
| say what its contents were. */ |
| && ! REGNO_REG_SET_P |
| (DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), REGNO (x)) |
| && is_a <scalar_int_mode> (GET_MODE (x), &mode) |
| && HWI_COMPUTABLE_MODE_P (mode)) |
| { |
| reg_stat_type *rsp = ®_stat[REGNO (x)]; |
| |
| if (set == 0 || GET_CODE (set) == CLOBBER) |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (mode); |
| rsp->sign_bit_copies = 1; |
| return; |
| } |
| |
| /* If this register is being initialized using itself, and the |
| register is uninitialized in this basic block, and there are |
| no LOG_LINKS which set the register, then part of the |
| register is uninitialized. In that case we can't assume |
| anything about the number of nonzero bits. |
| |
| ??? We could do better if we checked this in |
| reg_{nonzero_bits,num_sign_bit_copies}_for_combine. Then we |
| could avoid making assumptions about the insn which initially |
| sets the register, while still using the information in other |
| insns. We would have to be careful to check every insn |
| involved in the combination. */ |
| |
| if (insn |
| && reg_referenced_p (x, PATTERN (insn)) |
| && !REGNO_REG_SET_P (DF_LR_IN (BLOCK_FOR_INSN (insn)), |
| REGNO (x))) |
| { |
| struct insn_link *link; |
| |
| FOR_EACH_LOG_LINK (link, insn) |
| if (dead_or_set_p (link->insn, x)) |
| break; |
| if (!link) |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (mode); |
| rsp->sign_bit_copies = 1; |
| return; |
| } |
| } |
| |
| /* If this is a complex assignment, see if we can convert it into a |
| simple assignment. */ |
| set = expand_field_assignment (set); |
| |
| /* If this is a simple assignment, or we have a paradoxical SUBREG, |
| set what we know about X. */ |
| |
| if (SET_DEST (set) == x |
| || (paradoxical_subreg_p (SET_DEST (set)) |
| && SUBREG_REG (SET_DEST (set)) == x)) |
| update_rsp_from_reg_equal (rsp, insn, set, x); |
| else |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (mode); |
| rsp->sign_bit_copies = 1; |
| } |
| } |
| } |
| |
| /* See if INSN can be combined into I3. PRED, PRED2, SUCC and SUCC2 are |
| optionally insns that were previously combined into I3 or that will be |
| combined into the merger of INSN and I3. The order is PRED, PRED2, |
| INSN, SUCC, SUCC2, I3. |
| |
| Return 0 if the combination is not allowed for any reason. |
| |
| If the combination is allowed, *PDEST will be set to the single |
| destination of INSN and *PSRC to the single source, and this function |
| will return 1. */ |
| |
| static int |
| can_combine_p (rtx_insn *insn, rtx_insn *i3, rtx_insn *pred ATTRIBUTE_UNUSED, |
| rtx_insn *pred2 ATTRIBUTE_UNUSED, rtx_insn *succ, rtx_insn *succ2, |
| rtx *pdest, rtx *psrc) |
| { |
| int i; |
| const_rtx set = 0; |
| rtx src, dest; |
| rtx_insn *p; |
| rtx link; |
| bool all_adjacent = true; |
| int (*is_volatile_p) (const_rtx); |
| |
| if (succ) |
| { |
| if (succ2) |
| { |
| if (next_active_insn (succ2) != i3) |
| all_adjacent = false; |
| if (next_active_insn (succ) != succ2) |
| all_adjacent = false; |
| } |
| else if (next_active_insn (succ) != i3) |
| all_adjacent = false; |
| if (next_active_insn (insn) != succ) |
| all_adjacent = false; |
| } |
| else if (next_active_insn (insn) != i3) |
| all_adjacent = false; |
| |
| /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0. |
| or a PARALLEL consisting of such a SET and CLOBBERs. |
| |
| If INSN has CLOBBER parallel parts, ignore them for our processing. |
| By definition, these happen during the execution of the insn. When it |
| is merged with another insn, all bets are off. If they are, in fact, |
| needed and aren't also supplied in I3, they may be added by |
| recog_for_combine. Otherwise, it won't match. |
| |
| We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED |
| note. |
| |
| Get the source and destination of INSN. If more than one, can't |
| combine. */ |
| |
| if (GET_CODE (PATTERN (insn)) == SET) |
| set = PATTERN (insn); |
| else if (GET_CODE (PATTERN (insn)) == PARALLEL |
| && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) |
| { |
| for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) |
| { |
| rtx elt = XVECEXP (PATTERN (insn), 0, i); |
| |
| switch (GET_CODE (elt)) |
| { |
| /* This is important to combine floating point insns |
| for the SH4 port. */ |
| case USE: |
| /* Combining an isolated USE doesn't make sense. |
| We depend here on combinable_i3pat to reject them. */ |
| /* The code below this loop only verifies that the inputs of |
| the SET in INSN do not change. We call reg_set_between_p |
| to verify that the REG in the USE does not change between |
| I3 and INSN. |
| If the USE in INSN was for a pseudo register, the matching |
| insn pattern will likely match any register; combining this |
| with any other USE would only be safe if we knew that the |
| used registers have identical values, or if there was |
| something to tell them apart, e.g. different modes. For |
| now, we forgo such complicated tests and simply disallow |
| combining of USES of pseudo registers with any other USE. */ |
| if (REG_P (XEXP (elt, 0)) |
| && GET_CODE (PATTERN (i3)) == PARALLEL) |
| { |
| rtx i3pat = PATTERN (i3); |
| int i = XVECLEN (i3pat, 0) - 1; |
| unsigned int regno = REGNO (XEXP (elt, 0)); |
| |
| do |
| { |
| rtx i3elt = XVECEXP (i3pat, 0, i); |
| |
| if (GET_CODE (i3elt) == USE |
| && REG_P (XEXP (i3elt, 0)) |
| && (REGNO (XEXP (i3elt, 0)) == regno |
| ? reg_set_between_p (XEXP (elt, 0), |
| PREV_INSN (insn), i3) |
| : regno >= FIRST_PSEUDO_REGISTER)) |
| return 0; |
| } |
| while (--i >= 0); |
| } |
| break; |
| |
| /* We can ignore CLOBBERs. */ |
| case CLOBBER: |
| break; |
| |
| case SET: |
| /* Ignore SETs whose result isn't used but not those that |
| have side-effects. */ |
| if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt)) |
| && insn_nothrow_p (insn) |
| && !side_effects_p (elt)) |
| break; |
| |
| /* If we have already found a SET, this is a second one and |
| so we cannot combine with this insn. */ |
| if (set) |
| return 0; |
| |
| set = elt; |
| break; |
| |
| default: |
| /* Anything else means we can't combine. */ |
| return 0; |
| } |
| } |
| |
| if (set == 0 |
| /* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs, |
| so don't do anything with it. */ |
| || GET_CODE (SET_SRC (set)) == ASM_OPERANDS) |
| return 0; |
| } |
| else |
| return 0; |
| |
| if (set == 0) |
| return 0; |
| |
| /* The simplification in expand_field_assignment may call back to |
| get_last_value, so set safe guard here. */ |
| subst_low_luid = DF_INSN_LUID (insn); |
| |
| set = expand_field_assignment (set); |
| src = SET_SRC (set), dest = SET_DEST (set); |
| |
| /* Do not eliminate user-specified register if it is in an |
| asm input because we may break the register asm usage defined |
| in GCC manual if allow to do so. |
| Be aware that this may cover more cases than we expect but this |
| should be harmless. */ |
| if (REG_P (dest) && REG_USERVAR_P (dest) && HARD_REGISTER_P (dest) |
| && extract_asm_operands (PATTERN (i3))) |
| return 0; |
| |
| /* Don't eliminate a store in the stack pointer. */ |
| if (dest == stack_pointer_rtx |
| /* Don't combine with an insn that sets a register to itself if it has |
| a REG_EQUAL note. This may be part of a LIBCALL sequence. */ |
| || (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX)) |
| /* Can't merge an ASM_OPERANDS. */ |
| || GET_CODE (src) == ASM_OPERANDS |
| /* Can't merge a function call. */ |
| || GET_CODE (src) == CALL |
| /* Don't eliminate a function call argument. */ |
| || (CALL_P (i3) |
| && (find_reg_fusage (i3, USE, dest) |
| || (REG_P (dest) |
| && REGNO (dest) < FIRST_PSEUDO_REGISTER |
| && global_regs[REGNO (dest)]))) |
| /* Don't substitute into an incremented register. */ |
| || FIND_REG_INC_NOTE (i3, dest) |
| || (succ && FIND_REG_INC_NOTE (succ, dest)) |
| || (succ2 && FIND_REG_INC_NOTE (succ2, dest)) |
| /* Don't substitute into a non-local goto, this confuses CFG. */ |
| || (JUMP_P (i3) && find_reg_note (i3, REG_NON_LOCAL_GOTO, NULL_RTX)) |
| /* Make sure that DEST is not used after INSN but before SUCC, or |
| after SUCC and before SUCC2, or after SUCC2 but before I3. */ |
| || (!all_adjacent |
| && ((succ2 |
| && (reg_used_between_p (dest, succ2, i3) |
| || reg_used_between_p (dest, succ, succ2))) |
| || (!succ2 && succ && reg_used_between_p (dest, succ, i3)) |
| || (!succ2 && !succ && reg_used_between_p (dest, insn, i3)) |
| || (succ |
| /* SUCC and SUCC2 can be split halves from a PARALLEL; in |
| that case SUCC is not in the insn stream, so use SUCC2 |
| instead for this test. */ |
| && reg_used_between_p (dest, insn, |
| succ2 |
| && INSN_UID (succ) == INSN_UID (succ2) |
| ? succ2 : succ)))) |
| /* Make sure that the value that is to be substituted for the register |
| does not use any registers whose values alter in between. However, |
| If the insns are adjacent, a use can't cross a set even though we |
| think it might (this can happen for a sequence of insns each setting |
| the same destination; last_set of that register might point to |
| a NOTE). If INSN has a REG_EQUIV note, the register is always |
| equivalent to the memory so the substitution is valid even if there |
| are intervening stores. Also, don't move a volatile asm or |
| UNSPEC_VOLATILE across any other insns. */ |
| || (! all_adjacent |
| && (((!MEM_P (src) |
| || ! find_reg_note (insn, REG_EQUIV, src)) |
| && modified_between_p (src, insn, i3)) |
| || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src)) |
| || GET_CODE (src) == UNSPEC_VOLATILE)) |
| /* Don't combine across a CALL_INSN, because that would possibly |
| change whether the life span of some REGs crosses calls or not, |
| and it is a pain to update that information. |
| Exception: if source is a constant, moving it later can't hurt. |
| Accept that as a special case. */ |
| || (DF_INSN_LUID (insn) < last_call_luid && ! CONSTANT_P (src))) |
| return 0; |
| |
| /* DEST must either be a REG or CC0. */ |
| if (REG_P (dest)) |
| { |
| /* If register alignment is being enforced for multi-word items in all |
| cases except for parameters, it is possible to have a register copy |
| insn referencing a hard register that is not allowed to contain the |
| mode being copied and which would not be valid as an operand of most |
| insns. Eliminate this problem by not combining with such an insn. |
| |
| Also, on some machines we don't want to extend the life of a hard |
| register. */ |
| |
| if (REG_P (src) |
| && ((REGNO (dest) < FIRST_PSEUDO_REGISTER |
| && !targetm.hard_regno_mode_ok (REGNO (dest), GET_MODE (dest))) |
| /* Don't extend the life of a hard register unless it is |
| user variable (if we have few registers) or it can't |
| fit into the desired register (meaning something special |
| is going on). |
| Also avoid substituting a return register into I3, because |
| reload can't handle a conflict with constraints of other |
| inputs. */ |
| || (REGNO (src) < FIRST_PSEUDO_REGISTER |
| && !targetm.hard_regno_mode_ok (REGNO (src), |
| GET_MODE (src))))) |
| return 0; |
| } |
| else if (GET_CODE (dest) != CC0) |
| return 0; |
| |
| |
| if (GET_CODE (PATTERN (i3)) == PARALLEL) |
| for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--) |
| if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER) |
| { |
| rtx reg = XEXP (XVECEXP (PATTERN (i3), 0, i), 0); |
| |
| /* If the clobber represents an earlyclobber operand, we must not |
| substitute an expression containing the clobbered register. |
| As we do not analyze the constraint strings here, we have to |
| make the conservative assumption. However, if the register is |
| a fixed hard reg, the clobber cannot represent any operand; |
| we leave it up to the machine description to either accept or |
| reject use-and-clobber patterns. */ |
| if (!REG_P (reg) |
| || REGNO (reg) >= FIRST_PSEUDO_REGISTER |
| || !fixed_regs[REGNO (reg)]) |
| if (reg_overlap_mentioned_p (reg, src)) |
| return 0; |
| } |
| |
| /* If INSN contains anything volatile, or is an `asm' (whether volatile |
| or not), reject, unless nothing volatile comes between it and I3 */ |
| |
| if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src)) |
| { |
| /* Make sure neither succ nor succ2 contains a volatile reference. */ |
| if (succ2 != 0 && volatile_refs_p (PATTERN (succ2))) |
| return 0; |
| if (succ != 0 && volatile_refs_p (PATTERN (succ))) |
| return 0; |
| /* We'll check insns between INSN and I3 below. */ |
| } |
| |
| /* If INSN is an asm, and DEST is a hard register, reject, since it has |
| to be an explicit register variable, and was chosen for a reason. */ |
| |
| if (GET_CODE (src) == ASM_OPERANDS |
| && REG_P (dest) && REGNO (dest) < FIRST_PSEUDO_REGISTER) |
| return 0; |
| |
| /* If INSN contains volatile references (specifically volatile MEMs), |
| we cannot combine across any other volatile references. |
| Even if INSN doesn't contain volatile references, any intervening |
| volatile insn might affect machine state. */ |
| |
| is_volatile_p = volatile_refs_p (PATTERN (insn)) |
| ? volatile_refs_p |
| : volatile_insn_p; |
| |
| for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) |
| if (INSN_P (p) && p != succ && p != succ2 && is_volatile_p (PATTERN (p))) |
| return 0; |
| |
| /* If INSN contains an autoincrement or autodecrement, make sure that |
| register is not used between there and I3, and not already used in |
| I3 either. Neither must it be used in PRED or SUCC, if they exist. |
| Also insist that I3 not be a jump if using LRA; if it were one |
| and the incremented register were spilled, we would lose. |
| Reload handles this correctly. */ |
| |
| if (AUTO_INC_DEC) |
| for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) |
| if (REG_NOTE_KIND (link) == REG_INC |
| && ((JUMP_P (i3) && targetm.lra_p ()) |
| || reg_used_between_p (XEXP (link, 0), insn, i3) |
| || (pred != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred))) |
| || (pred2 != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred2))) |
| || (succ != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ))) |
| || (succ2 != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ2))) |
| || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3)))) |
| return 0; |
| |
| /* Don't combine an insn that follows a CC0-setting insn. |
| An insn that uses CC0 must not be separated from the one that sets it. |
| We do, however, allow I2 to follow a CC0-setting insn if that insn |
| is passed as I1; in that case it will be deleted also. |
| We also allow combining in this case if all the insns are adjacent |
| because that would leave the two CC0 insns adjacent as well. |
| It would be more logical to test whether CC0 occurs inside I1 or I2, |
| but that would be much slower, and this ought to be equivalent. */ |
| |
| if (HAVE_cc0) |
| { |
| p = prev_nonnote_insn (insn); |
| if (p && p != pred && NONJUMP_INSN_P (p) && sets_cc0_p (PATTERN (p)) |
| && ! all_adjacent) |
| return 0; |
| } |
| |
| /* If we get here, we have passed all the tests and the combination is |
| to be allowed. */ |
| |
| *pdest = dest; |
| *psrc = src; |
| |
| return 1; |
| } |
| |
| /* LOC is the location within I3 that contains its pattern or the component |
| of a PARALLEL of the pattern. We validate that it is valid for combining. |
| |
| One problem is if I3 modifies its output, as opposed to replacing it |
| entirely, we can't allow the output to contain I2DEST, I1DEST or I0DEST as |
| doing so would produce an insn that is not equivalent to the original insns. |
| |
| Consider: |
| |
| (set (reg:DI 101) (reg:DI 100)) |
| (set (subreg:SI (reg:DI 101) 0) <foo>) |
| |
| This is NOT equivalent to: |
| |
| (parallel [(set (subreg:SI (reg:DI 100) 0) <foo>) |
| (set (reg:DI 101) (reg:DI 100))]) |
| |
| Not only does this modify 100 (in which case it might still be valid |
| if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100. |
| |
| We can also run into a problem if I2 sets a register that I1 |
| uses and I1 gets directly substituted into I3 (not via I2). In that |
| case, we would be getting the wrong value of I2DEST into I3, so we |
| must reject the combination. This case occurs when I2 and I1 both |
| feed into I3, rather than when I1 feeds into I2, which feeds into I3. |
| If I1_NOT_IN_SRC is nonzero, it means that finding I1 in the source |
| of a SET must prevent combination from occurring. The same situation |
| can occur for I0, in which case I0_NOT_IN_SRC is set. |
| |
| Before doing the above check, we first try to expand a field assignment |
| into a set of logical operations. |
| |
| If PI3_DEST_KILLED is nonzero, it is a pointer to a location in which |
| we place a register that is both set and used within I3. If more than one |
| such register is detected, we fail. |
| |
| Return 1 if the combination is valid, zero otherwise. */ |
| |
| static int |
| combinable_i3pat (rtx_insn *i3, rtx *loc, rtx i2dest, rtx i1dest, rtx i0dest, |
| int i1_not_in_src, int i0_not_in_src, rtx *pi3dest_killed) |
| { |
| rtx x = *loc; |
| |
| if (GET_CODE (x) == SET) |
| { |
| rtx set = x ; |
| rtx dest = SET_DEST (set); |
| rtx src = SET_SRC (set); |
| rtx inner_dest = dest; |
| rtx subdest; |
| |
| while (GET_CODE (inner_dest) == STRICT_LOW_PART |
| || GET_CODE (inner_dest) == SUBREG |
| || GET_CODE (inner_dest) == ZERO_EXTRACT) |
| inner_dest = XEXP (inner_dest, 0); |
| |
| /* Check for the case where I3 modifies its output, as discussed |
| above. We don't want to prevent pseudos from being combined |
| into the address of a MEM, so only prevent the combination if |
| i1 or i2 set the same MEM. */ |
| if ((inner_dest != dest && |
| (!MEM_P (inner_dest) |
| || rtx_equal_p (i2dest, inner_dest) |
| || (i1dest && rtx_equal_p (i1dest, inner_dest)) |
| || (i0dest && rtx_equal_p (i0dest, inner_dest))) |
| && (reg_overlap_mentioned_p (i2dest, inner_dest) |
| || (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest)) |
| || (i0dest && reg_overlap_mentioned_p (i0dest, inner_dest)))) |
| |
| /* This is the same test done in can_combine_p except we can't test |
| all_adjacent; we don't have to, since this instruction will stay |
| in place, thus we are not considering increasing the lifetime of |
| INNER_DEST. |
| |
| Also, if this insn sets a function argument, combining it with |
| something that might need a spill could clobber a previous |
| function argument; the all_adjacent test in can_combine_p also |
| checks this; here, we do a more specific test for this case. */ |
| |
| || (REG_P (inner_dest) |
| && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER |
| && !targetm.hard_regno_mode_ok (REGNO (inner_dest), |
| GET_MODE (inner_dest))) |
| || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)) |
| || (i0_not_in_src && reg_overlap_mentioned_p (i0dest, src))) |
| return 0; |
| |
| /* If DEST is used in I3, it is being killed in this insn, so |
| record that for later. We have to consider paradoxical |
| subregs here, since they kill the whole register, but we |
| ignore partial subregs, STRICT_LOW_PART, etc. |
| Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the |
| STACK_POINTER_REGNUM, since these are always considered to be |
| live. Similarly for ARG_POINTER_REGNUM if it is fixed. */ |
| subdest = dest; |
| if (GET_CODE (subdest) == SUBREG && !partial_subreg_p (subdest)) |
| subdest = SUBREG_REG (subdest); |
| if (pi3dest_killed |
| && REG_P (subdest) |
| && reg_referenced_p (subdest, PATTERN (i3)) |
| && REGNO (subdest) != FRAME_POINTER_REGNUM |
| && (HARD_FRAME_POINTER_IS_FRAME_POINTER |
| || REGNO (subdest) != HARD_FRAME_POINTER_REGNUM) |
| && (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM |
| || (REGNO (subdest) != ARG_POINTER_REGNUM |
| || ! fixed_regs [REGNO (subdest)])) |
| && REGNO (subdest) != STACK_POINTER_REGNUM) |
| { |
| if (*pi3dest_killed) |
| return 0; |
| |
| *pi3dest_killed = subdest; |
| } |
| } |
| |
| else if (GET_CODE (x) == PARALLEL) |
| { |
| int i; |
| |
| for (i = 0; i < XVECLEN (x, 0); i++) |
| if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest, i0dest, |
| i1_not_in_src, i0_not_in_src, pi3dest_killed)) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Return 1 if X is an arithmetic expression that contains a multiplication |
| and division. We don't count multiplications by powers of two here. */ |
| |
| static int |
| contains_muldiv (rtx x) |
| { |
| switch (GET_CODE (x)) |
| { |
| case MOD: case DIV: case UMOD: case UDIV: |
| return 1; |
| |
| case MULT: |
| return ! (CONST_INT_P (XEXP (x, 1)) |
| && pow2p_hwi (UINTVAL (XEXP (x, 1)))); |
| default: |
| if (BINARY_P (x)) |
| return contains_muldiv (XEXP (x, 0)) |
| || contains_muldiv (XEXP (x, 1)); |
| |
| if (UNARY_P (x)) |
| return contains_muldiv (XEXP (x, 0)); |
| |
| return 0; |
| } |
| } |
| |
| /* Determine whether INSN can be used in a combination. Return nonzero if |
| not. This is used in try_combine to detect early some cases where we |
| can't perform combinations. */ |
| |
| static int |
| cant_combine_insn_p (rtx_insn *insn) |
| { |
| rtx set; |
| rtx src, dest; |
| |
| /* If this isn't really an insn, we can't do anything. |
| This can occur when flow deletes an insn that it has merged into an |
| auto-increment address. */ |
| if (!NONDEBUG_INSN_P (insn)) |
| return 1; |
| |
| /* Never combine loads and stores involving hard regs that are likely |
| to be spilled. The register allocator can usually handle such |
| reg-reg moves by tying. If we allow the combiner to make |
| substitutions of likely-spilled regs, reload might die. |
| As an exception, we allow combinations involving fixed regs; these are |
| not available to the register allocator so there's no risk involved. */ |
| |
| set = single_set (insn); |
| if (! set) |
| return 0; |
| src = SET_SRC (set); |
| dest = SET_DEST (set); |
| if (GET_CODE (src) == SUBREG) |
| src = SUBREG_REG (src); |
| if (GET_CODE (dest) == SUBREG) |
| dest = SUBREG_REG (dest); |
| if (REG_P (src) && REG_P (dest) |
| && ((HARD_REGISTER_P (src) |
| && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (src)) |
| #ifdef LEAF_REGISTERS |
| && ! LEAF_REGISTERS [REGNO (src)]) |
| #else |
| ) |
| #endif |
| || (HARD_REGISTER_P (dest) |
| && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (dest)) |
| && targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (dest)))))) |
| return 1; |
| |
| return 0; |
| } |
| |
| struct likely_spilled_retval_info |
| { |
| unsigned regno, nregs; |
| unsigned mask; |
| }; |
| |
| /* Called via note_stores by likely_spilled_retval_p. Remove from info->mask |
| hard registers that are known to be written to / clobbered in full. */ |
| static void |
| likely_spilled_retval_1 (rtx x, const_rtx set, void *data) |
| { |
| struct likely_spilled_retval_info *const info = |
| (struct likely_spilled_retval_info *) data; |
| unsigned regno, nregs; |
| unsigned new_mask; |
| |
| if (!REG_P (XEXP (set, 0))) |
| return; |
| regno = REGNO (x); |
| if (regno >= info->regno + info->nregs) |
| return; |
| nregs = REG_NREGS (x); |
| if (regno + nregs <= info->regno) |
| return; |
| new_mask = (2U << (nregs - 1)) - 1; |
| if (regno < info->regno) |
| new_mask >>= info->regno - regno; |
| else |
| new_mask <<= regno - info->regno; |
| info->mask &= ~new_mask; |
| } |
| |
| /* Return nonzero iff part of the return value is live during INSN, and |
| it is likely spilled. This can happen when more than one insn is needed |
| to copy the return value, e.g. when we consider to combine into the |
| second copy insn for a complex value. */ |
| |
| static int |
| likely_spilled_retval_p (rtx_insn *insn) |
| { |
| rtx_insn *use = BB_END (this_basic_block); |
| rtx reg; |
| rtx_insn *p; |
| unsigned regno, nregs; |
| /* We assume here that no machine mode needs more than |
| 32 hard registers when the value overlaps with a register |
| for which TARGET_FUNCTION_VALUE_REGNO_P is true. */ |
| unsigned mask; |
| struct likely_spilled_retval_info info; |
| |
| if (!NONJUMP_INSN_P (use) || GET_CODE (PATTERN (use)) != USE || insn == use) |
| return 0; |
| reg = XEXP (PATTERN (use), 0); |
| if (!REG_P (reg) || !targetm.calls.function_value_regno_p (REGNO (reg))) |
| return 0; |
| regno = REGNO (reg); |
| nregs = REG_NREGS (reg); |
| if (nregs == 1) |
| return 0; |
| mask = (2U << (nregs - 1)) - 1; |
| |
| /* Disregard parts of the return value that are set later. */ |
| info.regno = regno; |
| info.nregs = nregs; |
| info.mask = mask; |
| for (p = PREV_INSN (use); info.mask && p != insn; p = PREV_INSN (p)) |
| if (INSN_P (p)) |
| note_stores (p, likely_spilled_retval_1, &info); |
| mask = info.mask; |
| |
| /* Check if any of the (probably) live return value registers is |
| likely spilled. */ |
| nregs --; |
| do |
| { |
| if ((mask & 1 << nregs) |
| && targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno + nregs))) |
| return 1; |
| } while (nregs--); |
| return 0; |
| } |
| |
| /* Adjust INSN after we made a change to its destination. |
| |
| Changing the destination can invalidate notes that say something about |
| the results of the insn and a LOG_LINK pointing to the insn. */ |
| |
| static void |
| adjust_for_new_dest (rtx_insn *insn) |
| { |
| /* For notes, be conservative and simply remove them. */ |
| remove_reg_equal_equiv_notes (insn, true); |
| |
| /* The new insn will have a destination that was previously the destination |
| of an insn just above it. Call distribute_links to make a LOG_LINK from |
| the next use of that destination. */ |
| |
| rtx set = single_set (insn); |
| gcc_assert (set); |
| |
| rtx reg = SET_DEST (set); |
| |
| while (GET_CODE (reg) == ZERO_EXTRACT |
| || GET_CODE (reg) == STRICT_LOW_PART |
| || GET_CODE (reg) == SUBREG) |
| reg = XEXP (reg, 0); |
| gcc_assert (REG_P (reg)); |
| |
| distribute_links (alloc_insn_link (insn, REGNO (reg), NULL)); |
| |
| df_insn_rescan (insn); |
| } |
| |
| /* Return TRUE if combine can reuse reg X in mode MODE. |
| ADDED_SETS is nonzero if the original set is still required. */ |
| static bool |
| can_change_dest_mode (rtx x, int added_sets, machine_mode mode) |
| { |
| unsigned int regno; |
| |
| if (!REG_P (x)) |
| return false; |
| |
| /* Don't change between modes with different underlying register sizes, |
| since this could lead to invalid subregs. */ |
| if (maybe_ne (REGMODE_NATURAL_SIZE (mode), |
| REGMODE_NATURAL_SIZE (GET_MODE (x)))) |
| return false; |
| |
| regno = REGNO (x); |
| /* Allow hard registers if the new mode is legal, and occupies no more |
| registers than the old mode. */ |
| if (regno < FIRST_PSEUDO_REGISTER) |
| return (targetm.hard_regno_mode_ok (regno, mode) |
| && REG_NREGS (x) >= hard_regno_nregs (regno, mode)); |
| |
| /* Or a pseudo that is only used once. */ |
| return (regno < reg_n_sets_max |
| && REG_N_SETS (regno) == 1 |
| && !added_sets |
| && !REG_USERVAR_P (x)); |
| } |
| |
| |
| /* Check whether X, the destination of a set, refers to part of |
| the register specified by REG. */ |
| |
| static bool |
| reg_subword_p (rtx x, rtx reg) |
| { |
| /* Check that reg is an integer mode register. */ |
| if (!REG_P (reg) || GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT) |
| return false; |
| |
| if (GET_CODE (x) == STRICT_LOW_PART |
| || GET_CODE (x) == ZERO_EXTRACT) |
| x = XEXP (x, 0); |
| |
| return GET_CODE (x) == SUBREG |
| && SUBREG_REG (x) == reg |
| && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT; |
| } |
| |
| /* Return whether PAT is a PARALLEL of exactly N register SETs followed |
| by an arbitrary number of CLOBBERs. */ |
| static bool |
| is_parallel_of_n_reg_sets (rtx pat, int n) |
| { |
| if (GET_CODE (pat) != PARALLEL) |
| return false; |
| |
| int len = XVECLEN (pat, 0); |
| if (len < n) |
| return false; |
| |
| int i; |
| for (i = 0; i < n; i++) |
| if (GET_CODE (XVECEXP (pat, 0, i)) != SET |
| || !REG_P (SET_DEST (XVECEXP (pat, 0, i)))) |
| return false; |
| for ( ; i < len; i++) |
| switch (GET_CODE (XVECEXP (pat, 0, i))) |
| { |
| case CLOBBER: |
| if (XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx) |
| return false; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| /* Return whether INSN, a PARALLEL of N register SETs (and maybe some |
| CLOBBERs), can be split into individual SETs in that order, without |
| changing semantics. */ |
| static bool |
| can_split_parallel_of_n_reg_sets (rtx_insn *insn, int n) |
| { |
| if (!insn_nothrow_p (insn)) |
| return false; |
| |
| rtx pat = PATTERN (insn); |
| |
| int i, j; |
| for (i = 0; i < n; i++) |
| { |
| if (side_effects_p (SET_SRC (XVECEXP (pat, 0, i)))) |
| return false; |
| |
| rtx reg = SET_DEST (XVECEXP (pat, 0, i)); |
| |
| for (j = i + 1; j < n; j++) |
| if (reg_referenced_p (reg, XVECEXP (pat, 0, j))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Return whether X is just a single_set, with the source |
| a general_operand. */ |
| static bool |
| is_just_move (rtx_insn *x) |
| { |
| rtx set = single_set (x); |
| if (!set) |
| return false; |
| |
| return general_operand (SET_SRC (set), VOIDmode); |
| } |
| |
| /* Callback function to count autoincs. */ |
| |
| static int |
| count_auto_inc (rtx, rtx, rtx, rtx, rtx, void *arg) |
| { |
| (*((int *) arg))++; |
| |
| return 0; |
| } |
| |
| /* Try to combine the insns I0, I1 and I2 into I3. |
| Here I0, I1 and I2 appear earlier than I3. |
| I0 and I1 can be zero; then we combine just I2 into I3, or I1 and I2 into |
| I3. |
| |
| If we are combining more than two insns and the resulting insn is not |
| recognized, try splitting it into two insns. If that happens, I2 and I3 |
| are retained and I1/I0 are pseudo-deleted by turning them into a NOTE. |
| Otherwise, I0, I1 and I2 are pseudo-deleted. |
| |
| Return 0 if the combination does not work. Then nothing is changed. |
| If we did the combination, return the insn at which combine should |
| resume scanning. |
| |
| Set NEW_DIRECT_JUMP_P to a nonzero value if try_combine creates a |
| new direct jump instruction. |
| |
| LAST_COMBINED_INSN is either I3, or some insn after I3 that has |
| been I3 passed to an earlier try_combine within the same basic |
| block. */ |
| |
| static rtx_insn * |
| try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, |
| int *new_direct_jump_p, rtx_insn *last_combined_insn) |
| { |
| /* New patterns for I3 and I2, respectively. */ |
| rtx newpat, newi2pat = 0; |
| rtvec newpat_vec_with_clobbers = 0; |
| int substed_i2 = 0, substed_i1 = 0, substed_i0 = 0; |
| /* Indicates need to preserve SET in I0, I1 or I2 in I3 if it is not |
| dead. */ |
| int added_sets_0, added_sets_1, added_sets_2; |
| /* Total number of SETs to put into I3. */ |
| int total_sets; |
| /* Nonzero if I2's or I1's body now appears in I3. */ |
| int i2_is_used = 0, i1_is_used = 0; |
| /* INSN_CODEs for new I3, new I2, and user of condition code. */ |
| int insn_code_number, i2_code_number = 0, other_code_number = 0; |
| /* Contains I3 if the destination of I3 is used in its source, which means |
| that the old life of I3 is being killed. If that usage is placed into |
| I2 and not in I3, a REG_DEAD note must be made. */ |
| rtx i3dest_killed = 0; |
| /* SET_DEST and SET_SRC of I2, I1 and I0. */ |
| rtx i2dest = 0, i2src = 0, i1dest = 0, i1src = 0, i0dest = 0, i0src = 0; |
| /* Copy of SET_SRC of I1 and I0, if needed. */ |
| rtx i1src_copy = 0, i0src_copy = 0, i0src_copy2 = 0; |
| /* Set if I2DEST was reused as a scratch register. */ |
| bool i2scratch = false; |
| /* The PATTERNs of I0, I1, and I2, or a copy of them in certain cases. */ |
| rtx i0pat = 0, i1pat = 0, i2pat = 0; |
| /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */ |
| int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0; |
| int i0dest_in_i0src = 0, i1dest_in_i0src = 0, i2dest_in_i0src = 0; |
| int i2dest_killed = 0, i1dest_killed = 0, i0dest_killed = 0; |
| int i1_feeds_i2_n = 0, i0_feeds_i2_n = 0, i0_feeds_i1_n = 0; |
| /* Notes that must be added to REG_NOTES in I3 and I2. */ |
| rtx new_i3_notes, new_i2_notes; |
| /* Notes that we substituted I3 into I2 instead of the normal case. */ |
| int i3_subst_into_i2 = 0; |
| /* Notes that I1, I2 or I3 is a MULT operation. */ |
| int have_mult = 0; |
| int swap_i2i3 = 0; |
| int split_i2i3 = 0; |
| int changed_i3_dest = 0; |
| bool i2_was_move = false, i3_was_move = false; |
| int n_auto_inc = 0; |
| |
| int maxreg; |
| rtx_insn *temp_insn; |
| rtx temp_expr; |
| struct insn_link *link; |
| rtx other_pat = 0; |
| rtx new_other_notes; |
| int i; |
| scalar_int_mode dest_mode, temp_mode; |
| |
| /* Immediately return if any of I0,I1,I2 are the same insn (I3 can |
| never be). */ |
| if (i1 == i2 || i0 == i2 || (i0 && i0 == i1)) |
| return 0; |
| |
| /* Only try four-insn combinations when there's high likelihood of |
| success. Look for simple insns, such as loads of constants or |
| binary operations involving a constant. */ |
| if (i0) |
| { |
| int i; |
| int ngood = 0; |
| int nshift = 0; |
| rtx set0, set3; |
| |
| if (!flag_expensive_optimizations) |
| return 0; |
| |
| for (i = 0; i < 4; i++) |
| { |
| rtx_insn *insn = i == 0 ? i0 : i == 1 ? i1 : i == 2 ? i2 : i3; |
| rtx set = single_set (insn); |
| rtx src; |
| if (!set) |
| continue; |
| src = SET_SRC (set); |
| if (CONSTANT_P (src)) |
| { |
| ngood += 2; |
| break; |
| } |
| else if (BINARY_P (src) && CONSTANT_P (XEXP (src, 1))) |
| ngood++; |
| else if (GET_CODE (src) == ASHIFT || GET_CODE (src) == ASHIFTRT |
| || GET_CODE (src) == LSHIFTRT) |
| nshift++; |
| } |
| |
| /* If I0 loads a memory and I3 sets the same memory, then I1 and I2 |
| are likely manipulating its value. Ideally we'll be able to combine |
| all four insns into a bitfield insertion of some kind. |
| |
| Note the source in I0 might be inside a sign/zero extension and the |
| memory modes in I0 and I3 might be different. So extract the address |
| from the destination of I3 and search for it in the source of I0. |
| |
| In the event that there's a match but the source/dest do not actually |
| refer to the same memory, the worst that happens is we try some |
| combinations that we wouldn't have otherwise. */ |
| if ((set0 = single_set (i0)) |
| /* Ensure the source of SET0 is a MEM, possibly buried inside |
| an extension. */ |
| && (GET_CODE (SET_SRC (set0)) == MEM |
| || ((GET_CODE (SET_SRC (set0)) == ZERO_EXTEND |
| || GET_CODE (SET_SRC (set0)) == SIGN_EXTEND) |
| && GET_CODE (XEXP (SET_SRC (set0), 0)) == MEM)) |
| && (set3 = single_set (i3)) |
| /* Ensure the destination of SET3 is a MEM. */ |
| && GET_CODE (SET_DEST (set3)) == MEM |
| /* Would it be better to extract the base address for the MEM |
| in SET3 and look for that? I don't have cases where it matters |
| but I could envision such cases. */ |
| && rtx_referenced_p (XEXP (SET_DEST (set3), 0), SET_SRC (set0))) |
| ngood += 2; |
| |
| if (ngood < 2 && nshift < 2) |
| return 0; |
| } |
| |
| /* Exit early if one of the insns involved can't be used for |
| combinations. */ |
| if (CALL_P (i2) |
| || (i1 && CALL_P (i1)) |
| || (i0 && CALL_P (i0)) |
| || cant_combine_insn_p (i3) |
| || cant_combine_insn_p (i2) |
| || (i1 && cant_combine_insn_p (i1)) |
| || (i0 && cant_combine_insn_p (i0)) |
| || likely_spilled_retval_p (i3)) |
| return 0; |
| |
| combine_attempts++; |
| undobuf.other_insn = 0; |
| |
| /* Reset the hard register usage information. */ |
| CLEAR_HARD_REG_SET (newpat_used_regs); |
| |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| { |
| if (i0) |
| fprintf (dump_file, "\nTrying %d, %d, %d -> %d:\n", |
| INSN_UID (i0), INSN_UID (i1), INSN_UID (i2), INSN_UID (i3)); |
| else if (i1) |
| fprintf (dump_file, "\nTrying %d, %d -> %d:\n", |
| INSN_UID (i1), INSN_UID (i2), INSN_UID (i3)); |
| else |
| fprintf (dump_file, "\nTrying %d -> %d:\n", |
| INSN_UID (i2), INSN_UID (i3)); |
| |
| if (i0) |
| dump_insn_slim (dump_file, i0); |
| if (i1) |
| dump_insn_slim (dump_file, i1); |
| dump_insn_slim (dump_file, i2); |
| dump_insn_slim (dump_file, i3); |
| } |
| |
| /* If multiple insns feed into one of I2 or I3, they can be in any |
| order. To simplify the code below, reorder them in sequence. */ |
| if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i2)) |
| std::swap (i0, i2); |
| if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i1)) |
| std::swap (i0, i1); |
| if (i1 && DF_INSN_LUID (i1) > DF_INSN_LUID (i2)) |
| std::swap (i1, i2); |
| |
| added_links_insn = 0; |
| added_notes_insn = 0; |
| |
| /* First check for one important special case that the code below will |
| not handle. Namely, the case where I1 is zero, I2 is a PARALLEL |
| and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case, |
| we may be able to replace that destination with the destination of I3. |
| This occurs in the common code where we compute both a quotient and |
| remainder into a structure, in which case we want to do the computation |
| directly into the structure to avoid register-register copies. |
| |
| Note that this case handles both multiple sets in I2 and also cases |
| where I2 has a number of CLOBBERs inside the PARALLEL. |
| |
| We make very conservative checks below and only try to handle the |
| most common cases of this. For example, we only handle the case |
| where I2 and I3 are adjacent to avoid making difficult register |
| usage tests. */ |
| |
| if (i1 == 0 && NONJUMP_INSN_P (i3) && GET_CODE (PATTERN (i3)) == SET |
| && REG_P (SET_SRC (PATTERN (i3))) |
| && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER |
| && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3))) |
| && GET_CODE (PATTERN (i2)) == PARALLEL |
| && ! side_effects_p (SET_DEST (PATTERN (i3))) |
| /* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code |
| below would need to check what is inside (and reg_overlap_mentioned_p |
| doesn't support those codes anyway). Don't allow those destinations; |
| the resulting insn isn't likely to be recognized anyway. */ |
| && GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT |
| && GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART |
| && ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)), |
| SET_DEST (PATTERN (i3))) |
| && next_active_insn (i2) == i3) |
| { |
| rtx p2 = PATTERN (i2); |
| |
| /* Make sure that the destination of I3, |
| which we are going to substitute into one output of I2, |
| is not used within another output of I2. We must avoid making this: |
| (parallel [(set (mem (reg 69)) ...) |
| (set (reg 69) ...)]) |
| which is not well-defined as to order of actions. |
| (Besides, reload can't handle output reloads for this.) |
| |
| The problem can also happen if the dest of I3 is a memory ref, |
| if another dest in I2 is an indirect memory ref. |
| |
| Neither can this PARALLEL be an asm. We do not allow combining |
| that usually (see can_combine_p), so do not here either. */ |
| bool ok = true; |
| for (i = 0; ok && i < XVECLEN (p2, 0); i++) |
| { |
| if ((GET_CODE (XVECEXP (p2, 0, i)) == SET |
| || GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER) |
| && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)), |
| SET_DEST (XVECEXP (p2, 0, i)))) |
| ok = false; |
| else if (GET_CODE (XVECEXP (p2, 0, i)) == SET |
| && GET_CODE (SET_SRC (XVECEXP (p2, 0, i))) == ASM_OPERANDS) |
| ok = false; |
| } |
| |
| if (ok) |
| for (i = 0; i < XVECLEN (p2, 0); i++) |
| if (GET_CODE (XVECEXP (p2, 0, i)) == SET |
| && SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3))) |
| { |
| combine_merges++; |
| |
| subst_insn = i3; |
| subst_low_luid = DF_INSN_LUID (i2); |
| |
| added_sets_2 = added_sets_1 = added_sets_0 = 0; |
| i2src = SET_SRC (XVECEXP (p2, 0, i)); |
| i2dest = SET_DEST (XVECEXP (p2, 0, i)); |
| i2dest_killed = dead_or_set_p (i2, i2dest); |
| |
| /* Replace the dest in I2 with our dest and make the resulting |
| insn the new pattern for I3. Then skip to where we validate |
| the pattern. Everything was set up above. */ |
| SUBST (SET_DEST (XVECEXP (p2, 0, i)), SET_DEST (PATTERN (i3))); |
| newpat = p2; |
| i3_subst_into_i2 = 1; |
| goto validate_replacement; |
| } |
| } |
| |
| /* If I2 is setting a pseudo to a constant and I3 is setting some |
| sub-part of it to another constant, merge them by making a new |
| constant. */ |
| if (i1 == 0 |
| && (temp_expr = single_set (i2)) != 0 |
| && is_a <scalar_int_mode> (GET_MODE (SET_DEST (temp_expr)), &temp_mode) |
| && CONST_SCALAR_INT_P (SET_SRC (temp_expr)) |
| && GET_CODE (PATTERN (i3)) == SET |
| && CONST_SCALAR_INT_P (SET_SRC (PATTERN (i3))) |
| && reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp_expr))) |
| { |
| rtx dest = SET_DEST (PATTERN (i3)); |
| rtx temp_dest = SET_DEST (temp_expr); |
| int offset = -1; |
| int width = 0; |
| |
| if (GET_CODE (dest) == ZERO_EXTRACT) |
| { |
| if (CONST_INT_P (XEXP (dest, 1)) |
| && CONST_INT_P (XEXP (dest, 2)) |
| && is_a <scalar_int_mode> (GET_MODE (XEXP (dest, 0)), |
| &dest_mode)) |
| { |
| width = INTVAL (XEXP (dest, 1)); |
| offset = INTVAL (XEXP (dest, 2)); |
| dest = XEXP (dest, 0); |
| if (BITS_BIG_ENDIAN) |
| offset = GET_MODE_PRECISION (dest_mode) - width - offset; |
| } |
| } |
| else |
| { |
| if (GET_CODE (dest) == STRICT_LOW_PART) |
| dest = XEXP (dest, 0); |
| if (is_a <scalar_int_mode> (GET_MODE (dest), &dest_mode)) |
| { |
| width = GET_MODE_PRECISION (dest_mode); |
| offset = 0; |
| } |
| } |
| |
| if (offset >= 0) |
| { |
| /* If this is the low part, we're done. */ |
| if (subreg_lowpart_p (dest)) |
| ; |
| /* Handle the case where inner is twice the size of outer. */ |
| else if (GET_MODE_PRECISION (temp_mode) |
| == 2 * GET_MODE_PRECISION (dest_mode)) |
| offset += GET_MODE_PRECISION (dest_mode); |
| /* Otherwise give up for now. */ |
| else |
| offset = -1; |
| } |
| |
| if (offset >= 0) |
| { |
| rtx inner = SET_SRC (PATTERN (i3)); |
| rtx outer = SET_SRC (temp_expr); |
| |
| wide_int o = wi::insert (rtx_mode_t (outer, temp_mode), |
| rtx_mode_t (inner, dest_mode), |
| offset, width); |
| |
| combine_merges++; |
| subst_insn = i3; |
| subst_low_luid = DF_INSN_LUID (i2); |
| added_sets_2 = added_sets_1 = added_sets_0 = 0; |
| i2dest = temp_dest; |
| i2dest_killed = dead_or_set_p (i2, i2dest); |
| |
| /* Replace the source in I2 with the new constant and make the |
| resulting insn the new pattern for I3. Then skip to where we |
| validate the pattern. Everything was set up above. */ |
| SUBST (SET_SRC (temp_expr), |
| immed_wide_int_const (o, temp_mode)); |
| |
| newpat = PATTERN (i2); |
| |
| /* The dest of I3 has been replaced with the dest of I2. */ |
| changed_i3_dest = 1; |
| goto validate_replacement; |
| } |
| } |
| |
| /* If we have no I1 and I2 looks like: |
| (parallel [(set (reg:CC X) (compare:CC OP (const_int 0))) |
| (set Y OP)]) |
| make up a dummy I1 that is |
| (set Y OP) |
| and change I2 to be |
| (set (reg:CC X) (compare:CC Y (const_int 0))) |
| |
| (We can ignore any trailing CLOBBERs.) |
| |
| This undoes a previous combination and allows us to match a branch-and- |
| decrement insn. */ |
| |
| if (!HAVE_cc0 && i1 == 0 |
| && is_parallel_of_n_reg_sets (PATTERN (i2), 2) |
| && (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)))) |
| == MODE_CC) |
| && GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE |
| && XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx |
| && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0), |
| SET_SRC (XVECEXP (PATTERN (i2), 0, 1))) |
| && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) |
| && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3)) |
| { |
| /* We make I1 with the same INSN_UID as I2. This gives it |
| the same DF_INSN_LUID for value tracking. Our fake I1 will |
| never appear in the insn stream so giving it the same INSN_UID |
| as I2 will not cause a problem. */ |
| |
| i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2), |
| XVECEXP (PATTERN (i2), 0, 1), INSN_LOCATION (i2), |
| -1, NULL_RTX); |
| INSN_UID (i1) = INSN_UID (i2); |
| |
| SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); |
| SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), |
| SET_DEST (PATTERN (i1))); |
| unsigned int regno = REGNO (SET_DEST (PATTERN (i1))); |
| SUBST_LINK (LOG_LINKS (i2), |
| alloc_insn_link (i1, regno, LOG_LINKS (i2))); |
| } |
| |
| /* If I2 is a PARALLEL of two SETs of REGs (and perhaps some CLOBBERs), |
| make those two SETs separate I1 and I2 insns, and make an I0 that is |
| the original I1. */ |
| if (!HAVE_cc0 && i0 == 0 |
| && is_parallel_of_n_reg_sets (PATTERN (i2), 2) |
| && can_split_parallel_of_n_reg_sets (i2, 2) |
| && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) |
| && !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3) |
| && !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3) |
| && !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3)) |
| { |
| /* If there is no I1, there is no I0 either. */ |
| i0 = i1; |
| |
| /* We make I1 with the same INSN_UID as I2. This gives it |
| the same DF_INSN_LUID for value tracking. Our fake I1 will |
| never appear in the insn stream so giving it the same INSN_UID |
| as I2 will not cause a problem. */ |
| |
| i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2), |
| XVECEXP (PATTERN (i2), 0, 0), INSN_LOCATION (i2), |
| -1, NULL_RTX); |
| INSN_UID (i1) = INSN_UID (i2); |
| |
| SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 1)); |
| } |
| |
| /* Verify that I2 and maybe I1 and I0 can be combined into I3. */ |
| if (!can_combine_p (i2, i3, i0, i1, NULL, NULL, &i2dest, &i2src)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, "Can't combine i2 into i3\n"); |
| undo_all (); |
| return 0; |
| } |
| if (i1 && !can_combine_p (i1, i3, i0, NULL, i2, NULL, &i1dest, &i1src)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, "Can't combine i1 into i3\n"); |
| undo_all (); |
| return 0; |
| } |
| if (i0 && !can_combine_p (i0, i3, NULL, NULL, i1, i2, &i0dest, &i0src)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, "Can't combine i0 into i3\n"); |
| undo_all (); |
| return 0; |
| } |
| |
| /* Record whether i2 and i3 are trivial moves. */ |
| i2_was_move = is_just_move (i2); |
| i3_was_move = is_just_move (i3); |
| |
| /* Record whether I2DEST is used in I2SRC and similarly for the other |
| cases. Knowing this will help in register status updating below. */ |
| i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src); |
| i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src); |
| i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src); |
| i0dest_in_i0src = i0 && reg_overlap_mentioned_p (i0dest, i0src); |
| i1dest_in_i0src = i0 && reg_overlap_mentioned_p (i1dest, i0src); |
| i2dest_in_i0src = i0 && reg_overlap_mentioned_p (i2dest, i0src); |
| i2dest_killed = dead_or_set_p (i2, i2dest); |
| i1dest_killed = i1 && dead_or_set_p (i1, i1dest); |
| i0dest_killed = i0 && dead_or_set_p (i0, i0dest); |
| |
| /* For the earlier insns, determine which of the subsequent ones they |
| feed. */ |
| i1_feeds_i2_n = i1 && insn_a_feeds_b (i1, i2); |
| i0_feeds_i1_n = i0 && insn_a_feeds_b (i0, i1); |
| i0_feeds_i2_n = (i0 && (!i0_feeds_i1_n ? insn_a_feeds_b (i0, i2) |
| : (!reg_overlap_mentioned_p (i1dest, i0dest) |
| && reg_overlap_mentioned_p (i0dest, i2src)))); |
| |
| /* Ensure that I3's pattern can be the destination of combines. */ |
| if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, i0dest, |
| i1 && i2dest_in_i1src && !i1_feeds_i2_n, |
| i0 && ((i2dest_in_i0src && !i0_feeds_i2_n) |
| || (i1dest_in_i0src && !i0_feeds_i1_n)), |
| &i3dest_killed)) |
| { |
| undo_all (); |
| return 0; |
| } |
| |
| /* See if any of the insns is a MULT operation. Unless one is, we will |
| reject a combination that is, since it must be slower. Be conservative |
| here. */ |
| if (GET_CODE (i2src) == MULT |
| || (i1 != 0 && GET_CODE (i1src) == MULT) |
| || (i0 != 0 && GET_CODE (i0src) == MULT) |
| || (GET_CODE (PATTERN (i3)) == SET |
| && GET_CODE (SET_SRC (PATTERN (i3))) == MULT)) |
| |