| /* Optimize by combining instructions for GNU compiler. |
| Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, |
| 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
| 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 triples of insns A, B and C when |
| C has a link back to B and B has a link back to A. |
| |
| 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 use_crosses_set_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 "tm.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "tm_p.h" |
| #include "flags.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "basic-block.h" |
| #include "insn-config.h" |
| #include "function.h" |
| /* Include expr.h after insn-config.h so we get HAVE_conditional_move. */ |
| #include "expr.h" |
| #include "insn-attr.h" |
| #include "recog.h" |
| #include "real.h" |
| #include "toplev.h" |
| #include "target.h" |
| #include "optabs.h" |
| #include "insn-codes.h" |
| #include "rtlhooks-def.h" |
| /* Include output.h for dump_file. */ |
| #include "output.h" |
| #include "params.h" |
| #include "timevar.h" |
| #include "tree-pass.h" |
| #include "df.h" |
| #include "cgraph.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 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; |
| |
| typedef struct reg_stat_struct { |
| /* Record last point of death of (hard or pseudo) register n. */ |
| rtx last_death; |
| |
| /* Record last point of modification of (hard or pseudo) register n. */ |
| rtx 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; |
| } reg_stat_type; |
| |
| DEF_VEC_O(reg_stat_type); |
| DEF_VEC_ALLOC_O(reg_stat_type,heap); |
| |
| static VEC(reg_stat_type,heap) *reg_stat; |
| |
| /* 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 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 added_links_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_rtx_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 an INSN_LIST rtx. */ |
| |
| static rtx *uid_log_links; |
| |
| #define INSN_COST(INSN) (uid_insn_cost[INSN_UID (INSN)]) |
| #define LOG_LINKS(INSN) (uid_log_links[INSN_UID (INSN)]) |
| |
| /* Incremented for each basic block. */ |
| |
| static int label_tick; |
| |
| /* Reset to label_tick for each label. */ |
| |
| 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 enum machine_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. */ |
| |
| struct undo |
| { |
| struct undo *next; |
| enum { UNDO_RTX, UNDO_INT, UNDO_MODE } kind; |
| union { rtx r; int i; enum machine_mode m; } old_contents; |
| union { rtx *r; int *i; } 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 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, enum machine_mode, const_rtx, |
| enum machine_mode, |
| unsigned HOST_WIDE_INT, |
| unsigned HOST_WIDE_INT *); |
| static rtx reg_num_sign_bit_copies_for_combine (const_rtx, enum machine_mode, const_rtx, |
| enum machine_mode, |
| unsigned int, 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); |
| static void set_nonzero_bits_and_sign_copies (rtx, const_rtx, void *); |
| static int cant_combine_insn_p (rtx); |
| static int can_combine_p (rtx, rtx, rtx, rtx, rtx *, rtx *); |
| static int combinable_i3pat (rtx, rtx *, rtx, rtx, int, rtx *); |
| static int contains_muldiv (rtx); |
| static rtx try_combine (rtx, rtx, rtx, int *); |
| static void undo_all (void); |
| static void undo_commit (void); |
| static rtx *find_split_point (rtx *, rtx); |
| static rtx subst (rtx, rtx, rtx, int, int); |
| static rtx combine_simplify_rtx (rtx, enum machine_mode, 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 (enum machine_mode, rtx, HOST_WIDE_INT, |
| rtx, unsigned HOST_WIDE_INT, int, int, int); |
| static rtx extract_left_shift (rtx, int); |
| static rtx make_compound_operation (rtx, enum rtx_code); |
| 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_to_mode (rtx, enum 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); |
| 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 (enum machine_mode, rtx, |
| unsigned HOST_WIDE_INT); |
| static rtx simplify_and_const_int (rtx, enum machine_mode, rtx, |
| unsigned HOST_WIDE_INT); |
| static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code, |
| HOST_WIDE_INT, enum machine_mode, int *); |
| static rtx simplify_shift_const_1 (enum rtx_code, enum machine_mode, rtx, int); |
| static rtx simplify_shift_const (rtx, enum rtx_code, enum machine_mode, rtx, |
| int); |
| static int recog_for_combine (rtx *, rtx, rtx *); |
| static rtx gen_lowpart_for_combine (enum machine_mode, 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, rtx); |
| static void check_promoted_subreg (rtx, rtx); |
| static void record_dead_and_set_regs_1 (rtx, const_rtx, void *); |
| static void record_dead_and_set_regs (rtx); |
| static int get_last_value_validate (rtx *, rtx, int, int); |
| static rtx get_last_value (const_rtx); |
| static int use_crosses_set_p (const_rtx, int); |
| static void reg_dead_at_p_1 (rtx, const_rtx, void *); |
| static int reg_dead_at_p (rtx, rtx); |
| static void move_deaths (rtx, rtx, int, rtx, rtx *); |
| static int reg_bitfield_target_p (rtx, rtx); |
| static void distribute_notes (rtx, rtx, rtx, rtx, rtx, rtx); |
| static void distribute_links (rtx); |
| static void mark_used_regs_combine (rtx); |
| static void record_promoted_value (rtx, rtx); |
| static int unmentioned_reg_p_1 (rtx *, void *); |
| static bool unmentioned_reg_p (rtx, rtx); |
| static int record_truncated_value (rtx *, void *); |
| static void record_truncated_values (rtx *, void *); |
| static bool reg_truncated_to_mode (enum machine_mode, const_rtx); |
| static rtx gen_lowpart_or_truncate (enum 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; |
| |
| |
| /* Try to split PATTERN found in INSN. This returns NULL_RTX if |
| PATTERN can not 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 |
| combine_split_insns (rtx pattern, rtx insn) |
| { |
| rtx ret; |
| unsigned int nregs; |
| |
| ret = split_insns (pattern, insn); |
| nregs = max_reg_num (); |
| if (nregs > VEC_length (reg_stat_type, reg_stat)) |
| VEC_safe_grow_cleared (reg_stat_type, heap, reg_stat, nregs); |
| 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_INT: |
| case CONST: |
| case LABEL_REF: |
| case SYMBOL_REF: |
| case CONST_DOUBLE: |
| case CONST_VECTOR: |
| 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))) |
| && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) |
| + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) |
| == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) |
| + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) |
| 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, rtx *ploc) |
| { |
| rtx next; |
| rtx *result; |
| rtx link; |
| |
| #ifdef HAVE_cc0 |
| 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; |
| } |
| #endif |
| |
| if (!REG_P (dest)) |
| return 0; |
| |
| for (next = next_nonnote_insn (insn); |
| next != 0 && !LABEL_P (next); |
| next = next_nonnote_insn (next)) |
| if (INSN_P (next) && dead_or_set_p (next, dest)) |
| { |
| for (link = LOG_LINKS (next); link; link = XEXP (link, 1)) |
| if (XEXP (link, 0) == insn) |
| 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 |
| && GET_CODE (newval) == CONST_INT) |
| { |
| /* 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 |
| && GET_CODE (SUBREG_REG (oldval)) == CONST_INT)); |
| gcc_assert (!(GET_CODE (oldval) == ZERO_EXTEND |
| && GET_CODE (XEXP (oldval, 0)) == CONST_INT)); |
| } |
| |
| 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, enum machine_mode newval) |
| { |
| struct undo *buf; |
| enum 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)) |
| |
| /* Subroutine of try_combine. Determine whether the combine replacement |
| patterns NEWPAT, NEWI2PAT and NEWOTHERPAT are cheaper according to |
| insn_rtx_cost that the original instruction sequence I1, I2, I3 and |
| undobuf.other_insn. Note that I1 and/or NEWI2PAT may be NULL_RTX. |
| NEWOTHERPAT and undobuf.other_insn may also both be NULL_RTX. This |
| function returns false, if the costs of all instructions can be |
| estimated, and the replacements are more expensive than the original |
| sequence. */ |
| |
| static bool |
| combine_validate_cost (rtx i1, rtx i2, rtx i3, rtx newpat, rtx newi2pat, |
| rtx newotherpat) |
| { |
| int i1_cost, i2_cost, i3_cost; |
| int new_i2_cost, new_i3_cost; |
| int old_cost, new_cost; |
| |
| /* Lookup the original insn_rtx_costs. */ |
| i2_cost = INSN_COST (i2); |
| i3_cost = INSN_COST (i3); |
| |
| if (i1) |
| { |
| i1_cost = INSN_COST (i1); |
| old_cost = (i1_cost > 0 && i2_cost > 0 && i3_cost > 0) |
| ? i1_cost + i2_cost + i3_cost : 0; |
| } |
| else |
| { |
| old_cost = (i2_cost > 0 && i3_cost > 0) ? i2_cost + i3_cost : 0; |
| i1_cost = 0; |
| } |
| |
| /* Calculate the replacement insn_rtx_costs. */ |
| new_i3_cost = insn_rtx_cost (newpat, optimize_this_for_speed_p); |
| if (newi2pat) |
| { |
| new_i2_cost = insn_rtx_cost (newi2pat, optimize_this_for_speed_p); |
| 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); |
| new_other_cost = insn_rtx_cost (newotherpat, optimize_this_for_speed_p); |
| 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 recombination if both new_cost and old_cost are |
| greater than zero, and new_cost is greater than old cost. */ |
| if (old_cost > 0 |
| && new_cost > old_cost) |
| { |
| if (dump_file) |
| { |
| if (i1) |
| { |
| fprintf (dump_file, |
| "rejecting combination of insns %d, %d and %d\n", |
| INSN_UID (i1), INSN_UID (i2), INSN_UID (i3)); |
| fprintf (dump_file, "original costs %d + %d + %d = %d\n", |
| i1_cost, i2_cost, i3_cost, old_cost); |
| } |
| else |
| { |
| fprintf (dump_file, |
| "rejecting combination of insns %d and %d\n", |
| INSN_UID (i2), INSN_UID (i3)); |
| fprintf (dump_file, "original costs %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); |
| } |
| |
| 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; |
| |
| return true; |
| } |
| |
| |
| /* Delete any insns that copy a register to itself. */ |
| |
| static void |
| delete_noop_moves (void) |
| { |
| rtx insn, next; |
| basic_block bb; |
| |
| FOR_EACH_BB (bb) |
| { |
| 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)); |
| |
| delete_insn_and_edges (insn); |
| } |
| } |
| } |
| } |
| |
| |
| /* Fill in log links field for all insns. */ |
| |
| static void |
| create_log_links (void) |
| { |
| basic_block bb; |
| rtx *next_use, insn; |
| df_ref *def_vec, *use_vec; |
| |
| next_use = XCNEWVEC (rtx, 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 (bb) |
| { |
| FOR_BB_INSNS_REVERSE (bb, insn) |
| { |
| if (!INSN_P (insn)) |
| continue; |
| |
| /* Log links are created only once. */ |
| gcc_assert (!LOG_LINKS (insn)); |
| |
| for (def_vec = DF_INSN_DEFS (insn); *def_vec; def_vec++) |
| { |
| df_ref def = *def_vec; |
| int regno = DF_REF_REGNO (def); |
| rtx use_insn; |
| |
| if (!next_use[regno]) |
| continue; |
| |
| /* Do not consider if it is pre/post modification in MEM. */ |
| if (DF_REF_FLAGS (def) & DF_REF_PRE_POST_MODIFY) |
| continue; |
| |
| /* Do not make the log link for frame pointer. */ |
| if ((regno == FRAME_POINTER_REGNUM |
| && (! reload_completed || frame_pointer_needed)) |
| #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM |
| || (regno == HARD_FRAME_POINTER_REGNUM |
| && (! reload_completed || frame_pointer_needed)) |
| #endif |
| #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM |
| || (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) |
| #endif |
| ) |
| continue; |
| |
| use_insn = next_use[regno]; |
| if (BLOCK_FOR_INSN (use_insn) == bb) |
| { |
| /* 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) |
| { |
| /* Don't add duplicate links between instructions. */ |
| rtx links; |
| for (links = LOG_LINKS (use_insn); links; |
| links = XEXP (links, 1)) |
| if (insn == XEXP (links, 0)) |
| break; |
| |
| if (!links) |
| LOG_LINKS (use_insn) = |
| alloc_INSN_LIST (insn, LOG_LINKS (use_insn)); |
| } |
| } |
| next_use[regno] = NULL_RTX; |
| } |
| |
| for (use_vec = DF_INSN_USES (insn); *use_vec; use_vec++) |
| { |
| df_ref use = *use_vec; |
| int regno = DF_REF_REGNO (use); |
| |
| /* Do not consider the usage of the stack pointer |
| by function call. */ |
| if (DF_REF_FLAGS (use) & DF_REF_CALL_STACK_USAGE) |
| continue; |
| |
| next_use[regno] = insn; |
| } |
| } |
| } |
| |
| free (next_use); |
| } |
| |
| /* Clear LOG_LINKS fields of insns. */ |
| |
| static void |
| clear_log_links (void) |
| { |
| rtx insn; |
| |
| for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
| if (INSN_P (insn)) |
| free_INSN_LIST_list (&LOG_LINKS (insn)); |
| } |
| |
| |
| |
| |
| /* 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 combiner has turned an indirect jump |
| instruction into a direct jump. */ |
| static int |
| combine_instructions (rtx f, unsigned int nregs) |
| { |
| rtx insn, next; |
| #ifdef HAVE_cc0 |
| rtx prev; |
| #endif |
| rtx links, nextlinks; |
| rtx first; |
| |
| int new_direct_jump_p = 0; |
| |
| for (first = f; first && !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; |
| |
| VEC_safe_grow_cleared (reg_stat_type, heap, reg_stat, nregs); |
| |
| init_recog_no_volatile (); |
| |
| /* Allocate array for insn info. */ |
| max_uid_known = get_max_uid (); |
| uid_log_links = XCNEWVEC (rtx, max_uid_known + 1); |
| uid_insn_cost = XCNEWVEC (int, max_uid_known + 1); |
| |
| nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); |
| |
| /* 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; |
| |
| /* 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. */ |
| |
| label_tick = label_tick_ebb_start = 1; |
| |
| setup_incoming_promotions (first); |
| |
| create_log_links (); |
| FOR_EACH_BB (this_basic_block) |
| { |
| optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); |
| last_call_luid = 0; |
| mem_last_set = -1; |
| label_tick++; |
| FOR_BB_INSNS (this_basic_block, insn) |
| if (INSN_P (insn) && BLOCK_FOR_INSN (insn)) |
| { |
| subst_low_luid = DF_INSN_LUID (insn); |
| subst_insn = insn; |
| |
| note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies, |
| insn); |
| record_dead_and_set_regs (insn); |
| |
| #ifdef 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); |
| #endif |
| |
| /* Record the current insn_rtx_cost of this instruction. */ |
| if (NONJUMP_INSN_P (insn)) |
| INSN_COST (insn) = insn_rtx_cost (PATTERN (insn), |
| optimize_this_for_speed_p); |
| if (dump_file) |
| fprintf(dump_file, "insn_cost %d: %d\n", |
| INSN_UID (insn), INSN_COST (insn)); |
| } |
| else if (LABEL_P (insn)) |
| label_tick_ebb_start = label_tick; |
| } |
| |
| 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); |
| |
| FOR_EACH_BB (this_basic_block) |
| { |
| optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block); |
| last_call_luid = 0; |
| mem_last_set = -1; |
| label_tick++; |
| 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 (INSN_P (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 (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) |
| if ((next = try_combine (insn, XEXP (links, 0), |
| NULL_RTX, &new_direct_jump_p)) != 0) |
| goto retry; |
| |
| /* Try each sequence of three linked insns ending with this one. */ |
| |
| for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) |
| { |
| rtx link = XEXP (links, 0); |
| |
| /* 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 (nextlinks = LOG_LINKS (link); |
| nextlinks; |
| nextlinks = XEXP (nextlinks, 1)) |
| if ((next = try_combine (insn, link, |
| XEXP (nextlinks, 0), |
| &new_direct_jump_p)) != 0) |
| goto retry; |
| } |
| |
| #ifdef HAVE_cc0 |
| /* 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 (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_RTX, &new_direct_jump_p)) != 0) |
| goto retry; |
| |
| for (nextlinks = LOG_LINKS (prev); nextlinks; |
| nextlinks = XEXP (nextlinks, 1)) |
| if ((next = try_combine (insn, prev, |
| XEXP (nextlinks, 0), |
| &new_direct_jump_p)) != 0) |
| goto retry; |
| } |
| |
| /* Do the same for an insn that explicitly references CC0. */ |
| if (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_RTX, &new_direct_jump_p)) != 0) |
| goto retry; |
| |
| for (nextlinks = LOG_LINKS (prev); nextlinks; |
| nextlinks = XEXP (nextlinks, 1)) |
| if ((next = try_combine (insn, prev, |
| XEXP (nextlinks, 0), |
| &new_direct_jump_p)) != 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. */ |
| for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) |
| if (NONJUMP_INSN_P (XEXP (links, 0)) |
| && GET_CODE (PATTERN (XEXP (links, 0))) == SET |
| && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0)))) |
| && (prev = prev_nonnote_insn (XEXP (links, 0))) != 0 |
| && NONJUMP_INSN_P (prev) |
| && sets_cc0_p (PATTERN (prev)) |
| && (next = try_combine (insn, XEXP (links, 0), |
| prev, &new_direct_jump_p)) != 0) |
| goto retry; |
| #endif |
| |
| /* Try combining an insn with two different insns whose results it |
| uses. */ |
| for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) |
| for (nextlinks = XEXP (links, 1); nextlinks; |
| nextlinks = XEXP (nextlinks, 1)) |
| if ((next = try_combine (insn, XEXP (links, 0), |
| XEXP (nextlinks, 0), |
| &new_direct_jump_p)) != 0) |
| goto retry; |
| |
| /* Try this insn with each REG_EQUAL note it links back to. */ |
| for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) |
| { |
| rtx set, note; |
| rtx temp = XEXP (links, 0); |
| if ((set = single_set (temp)) != 0 |
| && (note = find_reg_equal_equiv_note (temp)) != 0 |
| && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST |
| /* 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))) |
| { |
| /* 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 = SET_SRC (set); |
| SET_SRC (set) = note; |
| i2mod = temp; |
| i2mod_old_rhs = copy_rtx (orig); |
| i2mod_new_rhs = copy_rtx (note); |
| next = try_combine (insn, i2mod, NULL_RTX, |
| &new_direct_jump_p); |
| i2mod = NULL_RTX; |
| if (next) |
| goto retry; |
| SET_SRC (set) = orig; |
| } |
| } |
| |
| if (!NOTE_P (insn)) |
| record_dead_and_set_regs (insn); |
| |
| retry: |
| ; |
| } |
| else if (LABEL_P (insn)) |
| label_tick_ebb_start = label_tick; |
| } |
| } |
| |
| default_rtl_profile (); |
| clear_log_links (); |
| clear_bb_flags (); |
| new_direct_jump_p |= purge_all_dead_edges (); |
| delete_noop_moves (); |
| |
| /* Clean up. */ |
| free (uid_log_links); |
| free (uid_insn_cost); |
| VEC_free (reg_stat_type, heap, reg_stat); |
| |
| { |
| 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 (i = 0; VEC_iterate (reg_stat_type, reg_stat, i, p); ++i) |
| 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 first) |
| { |
| tree arg; |
| bool strictly_local = false; |
| |
| if (!targetm.calls.promote_function_args (TREE_TYPE (cfun->decl))) |
| return; |
| |
| for (arg = DECL_ARGUMENTS (current_function_decl); arg; |
| arg = TREE_CHAIN (arg)) |
| { |
| rtx reg = DECL_INCOMING_RTL (arg); |
| int uns1, uns3; |
| enum 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_local_info (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, |
| after any TARGET_PROMOTE_FUNCTION_ARGS-driven ABI promotions. */ |
| mode3 = promote_mode (DECL_ARG_TYPE (arg), mode2, &uns3, 1); |
| |
| /* The mode of the register in which the argument is being passed. */ |
| mode4 = GET_MODE (reg); |
| |
| /* Eliminate sign extensions in the callee when possible. Only |
| do this when: |
| (a) a mode promotion has occurred; |
| (b) the mode of the register is the same as the mode of |
| the argument as it is passed; and |
| (c) the signedness does not change across any of the promotions; and |
| (d) when no language-level promotions (which we cannot guarantee |
| will have been done by an external caller) are necessary, |
| unless we know that this function is only ever called from |
| the current compilation unit -- all of whose call sites will |
| do the mode1 --> mode2 promotion. */ |
| if (mode1 != mode3 |
| && mode3 == mode4 |
| && uns1 == uns3 |
| && (mode1 == mode2 || strictly_local)) |
| { |
| /* 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. */ |
| rtx x; |
| 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); |
| } |
| } |
| } |
| |
| /* 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 = (rtx) data; |
| unsigned int num; |
| |
| 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->next_bb), REGNO (x)) |
| && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT) |
| { |
| reg_stat_type *rsp = VEC_index (reg_stat_type, reg_stat, REGNO (x)); |
| |
| if (set == 0 || GET_CODE (set) == CLOBBER) |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (GET_MODE (x)); |
| 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))) |
| { |
| rtx link; |
| |
| for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) |
| { |
| if (dead_or_set_p (XEXP (link, 0), x)) |
| break; |
| } |
| if (!link) |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (GET_MODE (x)); |
| 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 |
| || (GET_CODE (SET_DEST (set)) == SUBREG |
| && (GET_MODE_SIZE (GET_MODE (SET_DEST (set))) |
| > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set))))) |
| && SUBREG_REG (SET_DEST (set)) == x)) |
| { |
| rtx src = SET_SRC (set); |
| |
| #ifdef SHORT_IMMEDIATES_SIGN_EXTEND |
| /* If X is narrower than a word and SRC is a non-negative |
| constant that would appear negative in the mode of X, |
| sign-extend it for use in reg_stat[].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. */ |
| |
| if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD |
| && GET_CODE (src) == CONST_INT |
| && INTVAL (src) > 0 |
| && 0 != (INTVAL (src) |
| & ((HOST_WIDE_INT) 1 |
| << (GET_MODE_BITSIZE (GET_MODE (x)) - 1)))) |
| src = GEN_INT (INTVAL (src) |
| | ((HOST_WIDE_INT) (-1) |
| << GET_MODE_BITSIZE (GET_MODE (x)))); |
| #endif |
| |
| /* Don't call nonzero_bits if it cannot change anything. */ |
| if (rsp->nonzero_bits != ~(unsigned HOST_WIDE_INT) 0) |
| rsp->nonzero_bits |= nonzero_bits (src, nonzero_bits_mode); |
| num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x)); |
| if (rsp->sign_bit_copies == 0 |
| || rsp->sign_bit_copies > num) |
| rsp->sign_bit_copies = num; |
| } |
| else |
| { |
| rsp->nonzero_bits = GET_MODE_MASK (GET_MODE (x)); |
| rsp->sign_bit_copies = 1; |
| } |
| } |
| } |
| |
| /* See if INSN can be combined into I3. PRED and SUCC are optionally |
| insns that were previously combined into I3 or that will be combined |
| into the merger of INSN and 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, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ, |
| rtx *pdest, rtx *psrc) |
| { |
| int i; |
| const_rtx set = 0; |
| rtx src, dest; |
| rtx p; |
| #ifdef AUTO_INC_DEC |
| rtx link; |
| #endif |
| int all_adjacent = (succ ? (next_active_insn (insn) == succ |
| && next_active_insn (succ) == i3) |
| : next_active_insn (insn) == i3); |
| |
| /* 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); |
| rtx note; |
| |
| 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)) |
| && (!(note = find_reg_note (insn, REG_EH_REGION, NULL_RTX)) |
| || INTVAL (XEXP (note, 0)) <= 0) |
| && ! 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; |
| |
| set = expand_field_assignment (set); |
| src = SET_SRC (set), dest = SET_DEST (set); |
| |
| /* 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)) |
| /* 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 SUCC but before I3. */ |
| || (succ && ! all_adjacent |
| && reg_used_between_p (dest, succ, i3)) |
| /* 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)) |
| && use_crosses_set_p (src, DF_INSN_LUID (insn))) |
| || (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 |
| && ! 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 |
| && ! 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) |
| { |
| /* Don't substitute for a register intended as a clobberable |
| operand. */ |
| rtx reg = XEXP (XVECEXP (PATTERN (i3), 0, i), 0); |
| if (rtx_equal_p (reg, dest)) |
| return 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 succ doesn't contain a volatile reference. */ |
| if (succ != 0 && volatile_refs_p (PATTERN (succ))) |
| return 0; |
| |
| for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) |
| if (INSN_P (p) && p != succ && volatile_refs_p (PATTERN (p))) |
| return 0; |
| } |
| |
| /* 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 there are any volatile insns between INSN and I3, reject, because |
| they might affect machine state. */ |
| |
| for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p)) |
| if (INSN_P (p) && p != succ && volatile_insn_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 it were one |
| and the incremented register were spilled, we would lose. */ |
| |
| #ifdef AUTO_INC_DEC |
| for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) |
| if (REG_NOTE_KIND (link) == REG_INC |
| && (JUMP_P (i3) |
| || reg_used_between_p (XEXP (link, 0), insn, i3) |
| || (pred != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred))) |
| || (succ != NULL_RTX |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ))) |
| || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3)))) |
| return 0; |
| #endif |
| |
| #ifdef HAVE_cc0 |
| /* 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. */ |
| |
| p = prev_nonnote_insn (insn); |
| if (p && p != pred && NONJUMP_INSN_P (p) && sets_cc0_p (PATTERN (p)) |
| && ! all_adjacent) |
| return 0; |
| #endif |
| |
| /* 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 or I1DEST 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. |
| |
| 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 i3, rtx *loc, rtx i2dest, rtx i1dest, |
| int i1_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))) |
| && (reg_overlap_mentioned_p (i2dest, inner_dest) |
| || (i1dest && reg_overlap_mentioned_p (i1dest, 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 |
| && (! HARD_REGNO_MODE_OK (REGNO (inner_dest), |
| GET_MODE (inner_dest)))) |
| || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, 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 |
| && (GET_MODE_SIZE (GET_MODE (subdest)) |
| >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (subdest))))) |
| subdest = SUBREG_REG (subdest); |
| if (pi3dest_killed |
| && REG_P (subdest) |
| && reg_referenced_p (subdest, PATTERN (i3)) |
| && REGNO (subdest) != FRAME_POINTER_REGNUM |
| #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM |
| && REGNO (subdest) != HARD_FRAME_POINTER_REGNUM |
| #endif |
| #if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM |
| && (REGNO (subdest) != ARG_POINTER_REGNUM |
| || ! fixed_regs [REGNO (subdest)]) |
| #endif |
| && 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, |
| i1_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 ! (GET_CODE (XEXP (x, 1)) == CONST_INT |
| && exact_log2 (INTVAL (XEXP (x, 1))) >= 0); |
| 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) |
| { |
| 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 (! 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) |
| && ((REGNO (src) < FIRST_PSEUDO_REGISTER |
| && ! fixed_regs[REGNO (src)] |
| && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (REGNO (src)))) |
| || (REGNO (dest) < FIRST_PSEUDO_REGISTER |
| && ! fixed_regs[REGNO (dest)] |
| && 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 = hard_regno_nregs[regno][GET_MODE (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) |
| { |
| rtx use = BB_END (this_basic_block); |
| rtx reg, 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 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) || !FUNCTION_VALUE_REGNO_P (REGNO (reg))) |
| return 0; |
| regno = REGNO (reg); |
| nregs = hard_regno_nregs[regno][GET_MODE (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 (PATTERN (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) |
| && 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) |
| { |
| /* For notes, be conservative and simply remove them. */ |
| remove_reg_equal_equiv_notes (insn); |
| |
| /* 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. */ |
| distribute_links (gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX)); |
| |
| 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, enum machine_mode mode) |
| { |
| unsigned int regno; |
| |
| if (!REG_P(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 (HARD_REGNO_MODE_OK (regno, mode) |
| && (hard_regno_nregs[regno][GET_MODE (x)] |
| >= hard_regno_nregs[regno][mode])); |
| |
| /* Or a pseudo that is only used once. */ |
| return (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; |
| } |
| |
| |
| /* Try to combine the insns I1 and I2 into I3. |
| Here I1 and I2 appear earlier than I3. |
| I1 can be zero; then we combine just I2 into I3. |
| |
| If we are combining three insns and the resulting insn is not recognized, |
| try splitting it into two insns. If that happens, I2 and I3 are retained |
| and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, 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. */ |
| |
| static rtx |
| try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) |
| { |
| /* New patterns for I3 and I2, respectively. */ |
| rtx newpat, newi2pat = 0; |
| rtvec newpat_vec_with_clobbers = 0; |
| int substed_i2 = 0, substed_i1 = 0; |
| /* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */ |
| int added_sets_1, added_sets_2; |
| /* Total number of SETs to put into I3. */ |
| int total_sets; |
| /* Nonzero if I2's body now appears in I3. */ |
| int i2_is_used; |
| /* 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 and I1. */ |
| rtx i2dest, i2src, i1dest = 0, i1src = 0; |
| /* PATTERN (I1) and PATTERN (I2), or a copy of it in certain cases. */ |
| rtx 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 i2dest_killed = 0, i1dest_killed = 0; |
| int i1_feeds_i3 = 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 changed_i3_dest = 0; |
| |
| int maxreg; |
| rtx temp; |
| rtx link; |
| rtx other_pat = 0; |
| rtx new_other_notes; |
| int i; |
| |
| /* Exit early if one of the insns involved can't be used for |
| combinations. */ |
| if (cant_combine_insn_p (i3) |
| || cant_combine_insn_p (i2) |
| || (i1 && cant_combine_insn_p (i1)) |
| || 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 I1 and I2 both feed I3, they can be in any order. To simplify the |
| code below, set I1 to be the earlier of the two insns. */ |
| if (i1 && DF_INSN_LUID (i1) > DF_INSN_LUID (i2)) |
| temp = i1, i1 = i2, i2 = temp; |
| |
| added_links_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 CLOBBER or PARALLELs. |
| |
| 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_real_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. */ |
| for (i = 0; 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)))) |
| break; |
| |
| if (i == XVECLEN (p2, 0)) |
| for (i = 0; i < XVECLEN (p2, 0); i++) |
| if ((GET_CODE (XVECEXP (p2, 0, i)) == SET |
| || GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER) |
| && 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 = 0; |
| i2dest = SET_SRC (PATTERN (i3)); |
| 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 = single_set (i2)) != 0 |
| && (GET_CODE (SET_SRC (temp)) == CONST_INT |
| || GET_CODE (SET_SRC (temp)) == CONST_DOUBLE) |
| && GET_CODE (PATTERN (i3)) == SET |
| && (GET_CODE (SET_SRC (PATTERN (i3))) == CONST_INT |
| || GET_CODE (SET_SRC (PATTERN (i3))) == CONST_DOUBLE) |
| && reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp))) |
| { |
| rtx dest = SET_DEST (PATTERN (i3)); |
| int offset = -1; |
| int width = 0; |
| |
| if (GET_CODE (dest) == ZERO_EXTRACT) |
| { |
| if (GET_CODE (XEXP (dest, 1)) == CONST_INT |
| && GET_CODE (XEXP (dest, 2)) == CONST_INT) |
| { |
| width = INTVAL (XEXP (dest, 1)); |
| offset = INTVAL (XEXP (dest, 2)); |
| dest = XEXP (dest, 0); |
| if (BITS_BIG_ENDIAN) |
| offset = GET_MODE_BITSIZE (GET_MODE (dest)) - width - offset; |
| } |
| } |
| else |
| { |
| if (GET_CODE (dest) == STRICT_LOW_PART) |
| dest = XEXP (dest, 0); |
| width = GET_MODE_BITSIZE (GET_MODE (dest)); |
| 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_BITSIZE (GET_MODE (SET_DEST (temp))) |
| == 2 * GET_MODE_BITSIZE (GET_MODE (dest))) |
| offset += GET_MODE_BITSIZE (GET_MODE (dest)); |
| /* Otherwise give up for now. */ |
| else |
| offset = -1; |
| } |
| |
| if (offset >= 0 |
| && (GET_MODE_BITSIZE (GET_MODE (SET_DEST (temp))) |
| <= HOST_BITS_PER_WIDE_INT * 2)) |
| { |
| HOST_WIDE_INT mhi, ohi, ihi; |
| HOST_WIDE_INT mlo, olo, ilo; |
| rtx inner = SET_SRC (PATTERN (i3)); |
| rtx outer = SET_SRC (temp); |
| |
| if (GET_CODE (outer) == CONST_INT) |
| { |
| olo = INTVAL (outer); |
| ohi = olo < 0 ? -1 : 0; |
| } |
| else |
| { |
| olo = CONST_DOUBLE_LOW (outer); |
| ohi = CONST_DOUBLE_HIGH (outer); |
| } |
| |
| if (GET_CODE (inner) == CONST_INT) |
| { |
| ilo = INTVAL (inner); |
| ihi = ilo < 0 ? -1 : 0; |
| } |
| else |
| { |
| ilo = CONST_DOUBLE_LOW (inner); |
| ihi = CONST_DOUBLE_HIGH (inner); |
| } |
| |
| if (width < HOST_BITS_PER_WIDE_INT) |
| { |
| mlo = ((unsigned HOST_WIDE_INT) 1 << width) - 1; |
| mhi = 0; |
| } |
| else if (width < HOST_BITS_PER_WIDE_INT * 2) |
| { |
| mhi = ((unsigned HOST_WIDE_INT) 1 |
| << (width - HOST_BITS_PER_WIDE_INT)) - 1; |
| mlo = -1; |
| } |
| else |
| { |
| mlo = -1; |
| mhi = -1; |
| } |
| |
| ilo &= mlo; |
| ihi &= mhi; |
| |
| if (offset >= HOST_BITS_PER_WIDE_INT) |
| { |
| mhi = mlo << (offset - HOST_BITS_PER_WIDE_INT); |
| mlo = 0; |
| ihi = ilo << (offset - HOST_BITS_PER_WIDE_INT); |
| ilo = 0; |
| } |
| else if (offset > 0) |
| { |
| mhi = (mhi << offset) | ((unsigned HOST_WIDE_INT) mlo |
| >> (HOST_BITS_PER_WIDE_INT - offset)); |
| mlo = mlo << offset; |
| ihi = (ihi << offset) | ((unsigned HOST_WIDE_INT) ilo |
| >> (HOST_BITS_PER_WIDE_INT - offset)); |
| ilo = ilo << offset; |
| } |
| |
| olo = (olo & ~mlo) | ilo; |
| ohi = (ohi & ~mhi) | ihi; |
| |
| combine_merges++; |
| subst_insn = i3; |
| subst_low_luid = DF_INSN_LUID (i2); |
| added_sets_2 = added_sets_1 = 0; |
| i2dest = SET_DEST (temp); |
| i2dest_killed = dead_or_set_p (i2, i2dest); |
| |
| SUBST (SET_SRC (temp), |
| immed_double_const (olo, ohi, GET_MODE (SET_DEST (temp)))); |
| |
| newpat = PATTERN (i2); |
| goto validate_replacement; |
| } |
| } |
| |
| #ifndef HAVE_cc0 |
| /* 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 (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL |
| && XVECLEN (PATTERN (i2), 0) >= 2 |
| && GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET |
| && (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 |
| && GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET |
| && REG_P (SET_DEST (XVECEXP (PATTERN (i2), 0, 1))) |
| && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0), |
| SET_SRC (XVECEXP (PATTERN (i2), 0, 1)))) |
| { |
| for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--) |
| if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != CLOBBER) |
| break; |
| |
| if (i == 1) |
| { |
| /* 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, INSN_UID (i2), NULL_RTX, i2, |
| BLOCK_FOR_INSN (i2), INSN_LOCATOR (i2), |
| XVECEXP (PATTERN (i2), 0, 1), -1, NULL_RTX); |
| |
| SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); |
| SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), |
| SET_DEST (PATTERN (i1))); |
| } |
| } |
| #endif |
| |
| /* Verify that I2 and I1 are valid for combining. */ |
| if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src) |
| || (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src))) |
| { |
| undo_all (); |
| return 0; |
| } |
| |
| /* 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); |
| i2dest_killed = dead_or_set_p (i2, i2dest); |
| i1dest_killed = i1 && dead_or_set_p (i1, i1dest); |
| |
| /* See if I1 directly feeds into I3. It does if I1DEST is not used |
| in I2SRC. */ |
| i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src); |
| |
| /* Ensure that I3's pattern can be the destination of combines. */ |
| if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, |
| i1 && i2dest_in_i1src && i1_feeds_i3, |
| &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) |
| || (GET_CODE (PATTERN (i3)) == SET |
| && GET_CODE (SET_SRC (PATTERN (i3))) == MULT)) |
| have_mult = 1; |
| |
| /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd. |
| We used to do this EXCEPT in one case: I3 has a post-inc in an |
| output operand. However, that exception can give rise to insns like |
| mov r3,(r3)+ |
| which is a famous insn on the PDP-11 where the value of r3 used as the |
| source was model-dependent. Avoid this sort of thing. */ |
| |
| #if 0 |
| if (!(GET_CODE (PATTERN (i3)) == SET |
| && REG_P (SET_SRC (PATTERN (i3))) |
| && MEM_P (SET_DEST (PATTERN (i3))) |
| && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC |
| || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC))) |
| /* It's not the exception. */ |
| #endif |
| #ifdef AUTO_INC_DEC |
| for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) |
| if (REG_NOTE_KIND (link) == REG_INC |
| && (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2)) |
| || (i1 != 0 |
| && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1))))) |
| { |
| undo_all (); |
| return 0; |
| } |
| #endif |
| |
| /* See if the SETs in I1 or I2 need to be kept around in the merged |
| instruction: whenever the value set there is still needed past I3. |
| For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3. |
| |
| For the SET in I1, we have two cases: If I1 and I2 independently |
| feed into I3, the set in I1 needs to be kept around if I1DEST dies |
| or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set |
| in I1 needs to be kept around unless I1DEST dies or is set in either |
| I2 or I3. We can distinguish these cases by seeing if I2SRC mentions |
| I1DEST. If so, we know I1 feeds into I2. */ |
| |
| added_sets_2 = ! dead_or_set_p (i3, i2dest); |
| |
| added_sets_1 |
| = i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest) |
| : (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest))); |
| |
| /* If the set in I2 needs to be kept around, we must make a copy of |
| PATTERN (I2), so that when we substitute I1SRC for I1DEST in |
| PATTERN (I2), we are only substituting for the original I1DEST, not into |
| an already-substituted copy. This also prevents making self-referential |
| rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to |
| I2DEST. */ |
| |
| if (added_sets_2) |
| { |
| if (GET_CODE (PATTERN (i2)) == PARALLEL) |
| i2pat = gen_rtx_SET (VOIDmode, i2dest, copy_rtx (i2src)); |
| else |
| i2pat = copy_rtx (PATTERN (i2)); |
| } |
| |
| if (added_sets_1) |
| { |
| if (GET_CODE (PATTERN (i1)) == PARALLEL) |
| i1pat = gen_rtx_SET (VOIDmode, i1dest, copy_rtx (i1src)); |
| else |
| i1pat = copy_rtx (PATTERN (i1)); |
| } |
| |
| combine_merges++; |
| |
| /* Substitute in the latest insn for the regs set by the earlier ones. */ |
| |
| maxreg = max_reg_num (); |
| |
| subst_insn = i3; |
| |
| #ifndef HAVE_cc0 |
| /* Many machines that don't use CC0 have insns that can both perform an |
| arithmetic operation and set the condition code. These operations will |
| be represented as a PARALLEL with the first element of the vector |
| being a COMPARE of an arithmetic operation with the constant zero. |
| The second element of the vector will set some pseudo to the result |
| of the same arithmetic operation. If we simplify the COMPARE, we won't |
| match such a pattern and so will generate an extra insn. Here we test |
| for this case, where both the comparison and the operation result are |
| needed, and make the PARALLEL by just replacing I2DEST in I3SRC with |
| I2SRC. Later we will make the PARALLEL that contains I2. */ |
| |
| if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET |
| && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE |
| && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx |
| && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest)) |
| { |
| #ifdef SELECT_CC_MODE |
| rtx *cc_use; |
| enum machine_mode compare_mode; |
| #endif |
| |
| newpat = PATTERN (i3); |
| SUBST (XEXP (SET_SRC (newpat), 0), i2src); |
| |
| i2_is_used = 1; |
| |
| #ifdef SELECT_CC_MODE |
| /* See if a COMPARE with the operand we substituted in should be done |
| with the mode that is currently being used. If not, do the same |
| processing we do in `subst' for a SET; namely, if the destination |
| is used only once, try to replace it with a register of the proper |
| mode and also replace the COMPARE. */ |
| if (undobuf.other_insn == 0 |
| && (cc_use = find_single_use (SET_DEST (newpat), i3, |
| &undobuf.other_insn)) |
| && ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use), |
| i2src, const0_rtx)) |
| != GET_MODE (SET_DEST (newpat)))) |
| { |
| if (can_change_dest_mode(SET_DEST (newpat), added_sets_2, |
| compare_mode)) |
| { |
| unsigned int regno = REGNO (SET_DEST (newpat)); |
| rtx new_dest; |
| |
| if (regno < FIRST_PSEUDO_REGISTER) |
| new_dest = gen_rtx_REG (compare_mode, regno); |
| else |
| { |
| SUBST_MODE (regno_reg_rtx[regno], compare_mode); |
| new_dest = regno_reg_rtx[regno]; |
| } |
| |
| SUBST (SET_DEST (newpat), new_dest); |
| SUBST (XEXP (*cc_use, 0), new_dest); |
| SUBST (SET_SRC (newpat), |
| gen_rtx_COMPARE (compare_mode, i2src, const0_rtx)); |
| } |
| else |
| undobuf.other_insn = 0; |
| } |
| #endif |
| } |
| else |
| #endif |
| { |
| /* It is possible that the source of I2 or I1 may be performing |
| an unneeded operation, such as a ZERO_EXTEND of something |
| that is known to have the high part zero. Handle that case |
| by letting subst look at the innermost one of them. |
| |
| Another way to do this would be to have a function that tries |
| to simplify a single insn instead of merging two or more |
| insns. We don't do this because of the potential of infinite |
| loops and because of the potential extra memory required. |
| However, doing it the way we are is a bit of a kludge and |
| doesn't catch all cases. |
| |
| But only do this if -fexpensive-optimizations since it slows |
| things down and doesn't usually win. |
| |
| This is not done in the COMPARE case above because the |
| unmodified I2PAT is used in the PARALLEL and so a pattern |
| with a modified I2SRC would not match. */ |
| |
| if (flag_expensive_optimizations) |
| { |
| /* Pass pc_rtx so no substitutions are done, just |
| simplifications. */ |
| if (i1) |
| { |
| subst_low_luid = DF_INSN_LUID (i1); |
| i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0); |
| } |
| else |
| { |
| subst_low_luid = DF_INSN_LUID (i2); |
| i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0); |
| } |
| } |
| |
| n_occurrences = 0; /* `subst' counts here */ |
| |
| /* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we |
| need to make a unique copy of I2SRC each time we substitute it |
| to avoid self-referential rtl. */ |
| |
| subst_low_luid = DF_INSN_LUID (i2); |
| newpat = subst (PATTERN (i3), i2dest, i2src, 0, |
| ! i1_feeds_i3 && i1dest_in_i1src); |
| substed_i2 = 1; |
| |
| /* Record whether i2's body now appears within i3's body. */ |
| i2_is_used = n_occurrences; |
| } |
| |
| /* If we already got a failure, don't try to do more. Otherwise, |
| try to substitute in I1 if we have it. */ |
| |
| if (i1 && GET_CODE (newpat) != CLOBBER) |
| { |
| /* Check that an autoincrement side-effect on I1 has not been lost. |
| This happens if I1DEST is mentioned in I2 and dies there, and |
| has disappeared from the new pattern. */ |
| if ((FIND_REG_INC_NOTE (i1, NULL_RTX) != 0 |
| && !i1_feeds_i3 |
| && dead_or_set_p (i2, i1dest) |
| && !reg_overlap_mentioned_p (i1dest, newpat)) |
| /* Before we can do this substitution, we must redo the test done |
| above (see detailed comments there) that ensures that I1DEST |
| isn't mentioned in any SETs in NEWPAT that are field assignments. */ |
| || !combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX, 0, 0)) |
| { |
| undo_all (); |
| return 0; |
| } |
| |
| n_occurrences = 0; |
| subst_low_luid = DF_INSN_LUID (i1); |
| newpat = subst (newpat, i1dest, i1src, 0, 0); |
| substed_i1 = 1; |
| } |
| |
| /* Fail if an autoincrement side-effect has been duplicated. Be careful |
| to count all the ways that I2SRC and I1SRC can be used. */ |
| if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0 |
| && i2_is_used + added_sets_2 > 1) |
| || (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0 |
| && (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3) |
| > 1)) |
| /* Fail if we tried to make a new register. */ |
| || max_reg_num () != maxreg |
| /* Fail if we couldn't do something and have a CLOBBER. */ |
| || GET_CODE (newpat) == CLOBBER |
| /* Fail if this new pattern is a MULT and we didn't have one before |
| at the outer level. */ |
| || (GET_CODE (newpat) == SET && GET_CODE (SET_SRC (newpat)) == MULT |
| && ! have_mult)) |
| { |
| undo_all (); |
| return 0; |
| } |
| |
| /* If the actions of the earlier insns must be kept |
| in addition to substituting them into the latest one, |
| we must make a new PARALLEL for the latest insn |
| to hold additional the SETs. */ |
| |
| if (added_sets_1 || added_sets_2) |
| { |
| combine_extras++; |
| |
| if (GET_CODE (newpat) == PARALLEL) |
| { |
| rtvec old = XVEC (newpat, 0); |
| total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2; |
| newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets)); |
| memcpy (XVEC (newpat, 0)->elem, &old->elem[0], |
| sizeof (old->elem[0]) * old->num_elem); |
| } |
| else |
| { |
| rtx old = newpat; |
| total_sets = 1 + added_sets_1 + added_sets_2; |
| newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets)); |
| XVECEXP (newpat, 0, 0) = old; |
| } |
| |
| if (added_sets_1) |
| XVECEXP (newpat, 0, --total_sets) = i1pat; |
| |
| if (added_sets_2) |
| { |
| /* If there is no I1, use I2's body as is. We used to also not do |
| the subst call below if I2 was substituted into I3, |
| but that could lose a simplification. */ |
| if (i1 == 0) |
| XVECEXP (newpat, 0, --total_sets) = i2pat; |
| else |
| /* See comment where i2pat is assigned. */ |
| XVECEXP (newpat, 0, --total_sets) |
| = subst (i2pat, i1dest, i1src, 0, 0); |
| } |
| } |
| |
| /* We come here when we are replacing a destination in I2 with the |
| destination of I3. */ |
| validate_replacement: |
| |
| /* Note which hard regs this insn has as inputs. */ |
| mark_used_regs_combine (newpat); |
| |
| /* If recog_for_combine fails, it strips existing clobbers. If we'll |
| consider splitting this pattern, we might need these clobbers. */ |
| if (i1 && GET_CODE (newpat) == PARALLEL |
| && GET_CODE (XVECEXP (newpat, 0, XVECLEN (newpat, 0) - 1)) == CLOBBER) |
| { |
| int len = XVECLEN (newpat, 0); |
| |
| newpat_vec_with_clobbers = rtvec_alloc (len); |
| for (i = 0; i < len; i++) |
| RTVEC_ELT (newpat_vec_with_clobbers, i) = XVECEXP (newpat, 0, i); |
| } |
| |
| /* Is the result of combination a valid instruction? */ |
| insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); |
| |
| /* If the result isn't valid, see if it is a PARALLEL of two SETs where |
| the second SET's destination is a register that is unused and isn't |
| marked as an instruction that might trap in an EH region. In that case, |
| we just need the first SET. This can occur when simplifying a divmod |
| insn. We *must* test for this case here because the code below that |
| splits two independent SETs doesn't handle this case correctly when it |
| updates the register status. |
| |
| It's pointless doing this if we originally had two sets, one from |
| i3, and one from i2. Combining then splitting the parallel results |
| in the original i2 again plus an invalid insn (which we delete). |
| The net effect is only to move instructions around, which makes |
| debug info less accurate. |
| |
| Also check the case where the first SET's destination is unused. |
| That would not cause incorrect code, but does cause an unneeded |
| insn to remain. */ |
| |
| if (insn_code_number < 0 |
| && !(added_sets_2 && i1 == 0) |
| && GET_CODE (newpat) == PARALLEL |
| && XVECLEN (newpat, 0) == 2 |
| && GET_CODE (XVECEXP (newpat, 0, 0)) == SET |
| && GET_CODE (XVECEXP (newpat, 0, 1)) == SET |
| && asm_noperands (newpat) < 0) |
| { |
| rtx set0 = XVECEXP (newpat, 0, 0); |
| rtx set1 = XVECEXP (newpat, 0, 1); |
| rtx note; |
| |
| if (((REG_P (SET_DEST (set1)) |
| && find_reg_note (i3, REG_UNUSED, SET_DEST (set1))) |
| || (GET_CODE (SET_DEST (set1)) == SUBREG |
| && find_reg_note (i3, REG_UNUSED, SUBREG_REG (SET_DEST (set1))))) |
| && (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX)) |
| || INTVAL (XEXP (note, 0)) <= 0) |
| && ! side_effects_p (SET_SRC (set1))) |
| { |
| newpat = set0; |
| insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); |
| } |
| |
| else if (((REG_P (SET_DEST (set0)) |
| && find_reg_note (i3, REG_UNUSED, SET_DEST (set0))) |
| || (GET_CODE (SET_DEST (set0)) == SUBREG |
| && find_reg_note (i3, REG_UNUSED, |
| SUBREG_REG (SET_DEST (set0))))) |
| && (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX)) |
| || INTVAL (XEXP (note, 0)) <= 0) |
| && ! side_effects_p (SET_SRC (set0))) |
| { |
| newpat = set1; |
| insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); |
| |
| if (insn_code_number >= 0) |
| changed_i3_dest = 1; |
| } |
| } |
| |
| /* If we were combining three insns and the result is a simple SET |
| with no ASM_OPERANDS that wasn't recognized, try to split it into two |
| insns. There are two ways to do this. It can be split using a |
| machine-specific method (like when you have an addition of a large |
| constant) or by combine in the function find_split_point. */ |
| |
| if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET |
| && asm_noperands (newpat) < 0) |
| { |
| rtx parallel, m_split, *split; |
| |
| /* See if the MD file can split NEWPAT. If it can't, see if letting it |
| use I2DEST as a scratch register will help. In the latter case, |
| convert I2DEST to the mode of the source of NEWPAT if we can. */ |
| |
| m_split = combine_split_insns (newpat, i3); |
| |
| /* We can only use I2DEST as a scratch reg if it doesn't overlap any |
| inputs of NEWPAT. */ |
| |
| /* ??? If I2DEST is not safe, and I1DEST exists, then it would be |
| possible to try that as a scratch reg. This would require adding |
| more code to make it work though. */ |
| |
| if (m_split == 0 && ! reg_overlap_mentioned_p (i2dest, newpat)) |
| { |
| enum machine_mode new_mode = GET_MODE (SET_DEST (newpat)); |
| |
| /* First try to split using the original register as a |
| scratch register. */ |
| parallel = gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (2, newpat, |
| gen_rtx_CLOBBER (VOIDmode, |
| i2dest))); |
| m_split = combine_split_insns (parallel, i3); |
| |
| /* If that didn't work, try changing the mode of I2DEST if |
| we can. */ |
| if (m_split == 0 |
| && new_mode != GET_MODE (i2dest) |
| && new_mode != VOIDmode |
| && can_change_dest_mode (i2dest, added_sets_2, new_mode)) |
| { |
| enum machine_mode old_mode = GET_MODE (i2dest); |
| rtx ni2dest; |
| |
| if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER) |
| ni2dest = gen_rtx_REG (new_mode, REGNO (i2dest)); |
| else |
| { |
| SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], new_mode); |
| ni2dest = regno_reg_rtx[REGNO (i2dest)]; |
| } |
| |
| parallel = (gen_rtx_PARALLEL |
| (VOIDmode, |
| gen_rtvec (2, newpat, |
| gen_rtx_CLOBBER (VOIDmode, |
| ni2dest)))); |
| m_split = combine_split_insns (parallel, i3); |
| |
| if (m_split == 0 |
| && REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) |
| { |
| struct undo *buf; |
| |
| adjust_reg_mode (regno_reg_rtx[REGNO (i2dest)], old_mode); |
| buf = undobuf.undos; |
| undobuf.undos = buf->next; |
| buf->next = undobuf.frees; |
| undobuf.frees = buf; |
| } |
| } |
| } |
| |
| /* If recog_for_combine has discarded clobbers, try to use them |
| again for the split. */ |
| if (m_split == 0 && newpat_vec_with_clobbers) |
| { |
| parallel = gen_rtx_PARALLEL (VOIDmode, newpat_vec_with_clobbers); |
| m_split = combine_split_insns (parallel, i3); |
| } |
| |
| if (m_split && NEXT_INSN (m_split) == NULL_RTX) |
| { |
| m_split = PATTERN (m_split); |
| insn_code_number = recog_for_combine (&m_split, i3, &new_i3_notes); |
| if (insn_code_number >= 0) |
| newpat = m_split; |
| } |
| else if (m_split && NEXT_INSN (NEXT_INSN (m_split)) == NULL_RTX |
| && (next_real_insn (i2) == i3 |
| || ! use_crosses_set_p (PATTERN (m_split), DF_INSN_LUID (i2)))) |
| { |
| rtx i2set, i3set; |
| rtx newi3pat = PATTERN (NEXT_INSN (m_split)); |
| newi2pat = PATTERN (m_split); |
| |
| i3set = single_set (NEXT_INSN (m_split)); |
| i2set = single_set (m_split); |
| |
| i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); |
| |
| /* If I2 or I3 has multiple SETs, we won't know how to track |
| register status, so don't use these insns. If I2's destination |
| is used between I2 and I3, we also can't use these insns. */ |
| |
| if (i2_code_number >= 0 && i2set && i3set |
| && (next_real_insn (i2) == i3 |
| || ! reg_used_between_p (SET_DEST (i2set), i2, i3))) |
| insn_code_number = recog_for_combine (&newi3pat, i3, |
| &new_i3_notes); |
| if (insn_code_number >= 0) |
| newpat = newi3pat; |
| |
| /* It is possible that both insns now set the destination of I3. |
| If so, we must show an extra use of it. */ |
| |
| if (insn_code_number >= 0) |
| { |
| rtx new_i3_dest = SET_DEST (i3set); |
| rtx new_i2_dest = SET_DEST (i2set); |
| |
| while (GET_CODE (new_i3_dest) == ZERO_EXTRACT |
| || GET_CODE (new_i3_dest) == STRICT_LOW_PART |
| || GET_CODE (new_i3_dest) == SUBREG) |
| new_i3_dest = XEXP (new_i3_dest, 0); |
| |
| while (GET_CODE (new_i2_dest) == ZERO_EXTRACT |
| || GET_CODE (new_i2_dest) == STRICT_LOW_PART |
| || GET_CODE (new_i2_dest) == SUBREG) |
| new_i2_dest = XEXP (new_i2_dest, 0); |
| |
| if (REG_P (new_i3_dest) |
| && REG_P (new_i2_dest) |
| && REGNO (new_i3_dest) == REGNO (new_i2_dest)) |
| INC_REG_N_SETS (REGNO (new_i2_dest), 1); |
| } |
| } |
| |
| /* If we can split it and use I2DEST, go ahead and see if that |
| helps things be recognized. Verify that none of the registers |
| are set between I2 and I3. */ |
| if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0 |
| #ifdef HAVE_cc0 |
| && REG_P (i2dest) |
| #endif |
| /* We need I2DEST in the proper mode. If it is a hard register |
| or the only use of a pseudo, we can change its mode. |
| Make sure we don't change a hard register to have a mode that |
| isn't valid for it, or change the number of registers. */ |
| && (GET_MODE (*split) == GET_MODE (i2dest) |
| || GET_MODE (*split) == VOIDmode |
| || can_change_dest_mode (i2dest, added_sets_2, |
| GET_MODE (*split))) |
| && (next_real_insn (i2) == i3 |
| || ! use_crosses_set_p (*split, DF_INSN_LUID (i2))) |
| /* We can't overwrite I2DEST if its value is still used by |
| NEWPAT. */ |
| && ! reg_referenced_p (i2dest, newpat)) |
| { |
| rtx newdest = i2dest; |
| enum rtx_code split_code = GET_CODE (*split); |
| enum machine_mode split_mode = GET_MODE (*split); |
| bool subst_done = false; |
| newi2pat = NULL_RTX; |
| |
| /* Get NEWDEST as a register in the proper mode. We have already |
| validated that we can do this. */ |
| if (GET_MODE (i2dest) != split_mode && split_mode != VOIDmode) |
| { |
| if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER) |
| newdest = gen_rtx_REG (split_mode, REGNO (i2dest)); |
| else |
| { |
| SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], split_mode); |
| newdest = regno_reg_rtx[REGNO (i2dest)]; |
| } |
| } |
| |
| /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to |
| an ASHIFT. This can occur if it was inside a PLUS and hence |
| appeared to be a memory address. This is a kludge. */ |
| if (split_code == MULT |
| && GET_CODE (XEXP (*split, 1)) == CONST_INT |
| && INTVAL (XEXP (*split, 1)) > 0 |
| && (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0) |
| { |
| SUBST (*split, gen_rtx_ASHIFT (split_mode, |
| XEXP (*split, 0), GEN_INT (i))); |
| /* Update split_code because we may not have a multiply |
| anymore. */ |
| split_code = GET_CODE (*split); |
| } |
| |
| #ifdef INSN_SCHEDULING |
| /* If *SPLIT is a paradoxical SUBREG, when we split it, it should |
| be written as a ZERO_EXTEND. */ |
| if (split_code == SUBREG && MEM_P (SUBREG_REG (*split))) |
| { |
| #ifdef LOAD_EXTEND_OP |
| /* Or as a SIGN_EXTEND if LOAD_EXTEND_OP says that that's |
| what it really is. */ |
| if (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (*split))) |
| == SIGN_EXTEND) |
| SUBST (*split, gen_rtx_SIGN_EXTEND (split_mode, |
| SUBREG_REG (*split))); |
| else |
| #endif |
| SUBST (*split, gen_rtx_ZERO_EXTEND (split_mode, |
| SUBREG_REG (*split))); |
| } |
| #endif |
| |
| /* Attempt to split binary operators using arithmetic identities. */ |
| if (BINARY_P (SET_SRC (newpat)) |
| && split_mode == GET_MODE (SET_SRC (newpat)) |
| && ! side_effects_p (SET_SRC (newpat))) |
| { |
| rtx setsrc = SET_SRC (newpat); |
| enum machine_mode mode = GET_MODE (setsrc); |
| enum rtx_code code = GET_CODE (setsrc); |
| rtx src_op0 = XEXP (setsrc, 0); |
| rtx src_op1 = XEXP (setsrc, 1); |
| |
|