diff options
Diffstat (limited to 'gcc-1.40/combine.c')
| -rw-r--r-- | gcc-1.40/combine.c | 2796 |
1 files changed, 2796 insertions, 0 deletions
diff --git a/gcc-1.40/combine.c b/gcc-1.40/combine.c new file mode 100644 index 0000000..cc6676e --- /dev/null +++ b/gcc-1.40/combine.c @@ -0,0 +1,2796 @@ +/* Optimize by combining instructions for GNU compiler. + Copyright (C) 1987, 1988 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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 1, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* 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. + + 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. + + 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 <stdio.h> + +#include "config.h" +#include "rtl.h" +#include "flags.h" +#include "regs.h" +#include "basic-block.h" +#include "insn-config.h" +#include "recog.h" + +#define max(A,B) ((A) > (B) ? (A) : (B)) +#define min(A,B) ((A) < (B) ? (A) : (B)) + +/* It is not safe to use ordinary gen_lowpart in combine. + Use gen_lowpart_for_combine instead. See comments there. */ +#define gen_lowpart dont_use_gen_lowpart_you_dummy + +/* Number of attempts to combine instructions in this function. */ + +static int combine_attempts; +static int distrib_attempts; + +/* Number of attempts that got as far as substitution in this function. */ + +static int combine_merges; +static int distrib_merges_1, distrib_merges_2; + +/* 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; +static int distrib_successes; + +/* Totals over entire compilation. */ + +static int total_attempts, total_merges, total_extras, total_successes; +static int total_distrib_attempts, total_distrib_merges_1, total_distrib_merges_2, total_distrib_successes; + + +/* Vector mapping INSN_UIDs to cuids. + The cuids are like uids but increase monononically always. + Combine always uses cuids so that it can compare them. + But actually renumbering the uids, which we used to do, + proves to be a bad idea because it makes it hard to compare + the dumps produced by earlier passes with those from later passes. */ + +static int *uid_cuid; + +/* Get the cuid of an insn. */ + +#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) + + +/* Record last point of death of (hard or pseudo) register n. */ + +static rtx *reg_last_death; + +/* Record last point of modification of (hard or pseudo) register n. */ + +static rtx *reg_last_set; + +/* Record the cuid of the last insn that invalidated memory + (anything that writes memory, and subroutine calls). */ + +static int mem_last_set; + +/* Record the cuid of the last CALL_INSN + so we can tell whether a potential combination crosses any calls. */ + +static int last_call_cuid; + +/* 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; + +/* Record one modification to rtl structure + to be undone by storing old_contents into *where. + is_int is 1 if the contents are an int. */ + +struct undo +{ + rtx *where; + rtx old_contents; + int is_int; +}; + +struct undo_int +{ + int *where; + int old_contents; + int is_int; +}; + +/* Record a bunch of changes to be undone, up to MAX_UNDO of them. + num_undo says how many are currently recorded. + storage is nonzero if we must undo the allocation of new storage. + The value of storage is what to pass to obfree. */ + +#define MAX_UNDO 10 + +struct undobuf +{ + int num_undo; + char *storage; + struct undo undo[MAX_UNDO]; +}; + +static struct undobuf undobuf; + +/* Number of times the pseudo being substituted for + was found and replaced. */ + +static int n_occurrences; + +static void move_deaths (); +static void move_deaths_2 (); +void remove_death (); +static void record_dead_and_set_regs (); +int regno_dead_p (); +static int use_crosses_set_p (); +static int try_combine (); +static rtx try_distrib (); +static rtx subst (); +static void undo_all (); +static void copy_substitutions (); +static void add_links (); +static void remove_links (); +static void add_incs (); +static int adjacent_insns_p (); +static int check_asm_operands (); +static rtx simplify_and_const_int (); +static rtx gen_lowpart_for_combine (); +static void simplify_set_cc0_and (); + +/* Main entry point for combiner. F is the first insn of the function. + NREGS is the first unused pseudo-reg number. */ + +void +combine_instructions (f, nregs) + rtx f; + int nregs; +{ + register rtx insn; + register int i; + register rtx links, nextlinks; + rtx prev; + + combine_attempts = 0; + combine_merges = 0; + combine_extras = 0; + combine_successes = 0; + distrib_attempts = 0; + distrib_merges_1 = 0; + distrib_merges_2 = 0; + distrib_successes = 0; + + reg_last_death = (rtx *) alloca (nregs * sizeof (rtx)); + reg_last_set = (rtx *) alloca (nregs * sizeof (rtx)); + bzero (reg_last_death, nregs * sizeof (rtx)); + bzero (reg_last_set, nregs * sizeof (rtx)); + + init_recog (); + + /* Compute maximum uid value so uid_cuid can be allocated. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) > i) + i = INSN_UID (insn); + + uid_cuid = (int *) alloca ((i + 1) * sizeof (int)); + + /* Compute the mapping from uids to cuids. + Cuids are numbers assigned to insns, like uids, + except that cuids increase monotonically through the code. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + INSN_CUID (insn) = ++i; + + /* Now scan all the insns in forward order. */ + + last_call_cuid = 0; + mem_last_set = 0; + prev = 0; + + for (insn = f; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN + || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + { + retry: + /* Try this insn with each insn it links back to. */ + + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if (try_combine (insn, XEXP (links, 0), 0)) + goto retry; + + /* Try each sequence of three linked insns ending with this one. */ + + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if (GET_CODE (XEXP (links, 0)) != NOTE) + for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if (try_combine (insn, XEXP (links, 0), XEXP (nextlinks, 0))) + goto retry; + + /* Try to combine a jump insn that uses CC0 + with a preceding insn that sets CC0, and maybe with its + logical predecessor as well. + This is how we make decrement-and-branch insns. + We need this special code because data flow connections + via CC0 do not get entered in LOG_LINKS. */ + + if (GET_CODE (insn) == JUMP_INSN + && prev != 0 + && GET_CODE (prev) == INSN + && GET_CODE (PATTERN (prev)) == SET + && GET_CODE (SET_DEST (PATTERN (prev))) == CC0) + { + if (try_combine (insn, prev, 0)) + goto retry; + + if (GET_CODE (prev) != NOTE) + for (nextlinks = LOG_LINKS (prev); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if (try_combine (insn, prev, XEXP (nextlinks, 0))) + goto retry; + } + + /* Try to apply the distributive law to this insn + and two insns that compute the operands of this one. */ + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if (GET_CODE (XEXP (links, 0)) != NOTE) + for (nextlinks = XEXP (links, 1); nextlinks; nextlinks = XEXP (nextlinks, 1)) + if (GET_CODE (XEXP (nextlinks, 0)) != NOTE) + { + rtx try_from = 0; + + if (GET_CODE (PATTERN (XEXP (links, 0))) == SET + && find_reg_note (insn, REG_DEAD, SET_DEST (PATTERN (XEXP (links, 0)))) + && GET_CODE (PATTERN (XEXP (nextlinks, 0))) == SET + && find_reg_note (insn, REG_DEAD, SET_DEST (PATTERN (XEXP (nextlinks, 0))))) + try_from = try_distrib (insn, XEXP (links, 0), XEXP (nextlinks, 0)); + if (try_from != 0) + { + insn = try_from; + goto retry; + } + } +#if 0 +/* Turned off because on 68020 it takes four insns to make + something like (a[b / 32] & (1 << (31 - (b % 32)))) != 0 + that could actually be optimized, and that's an unlikely piece of code. */ + /* If an insn gets or sets a bit field, try combining it + with two different insns whose results it uses. */ + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && (GET_CODE (SET_DEST (PATTERN (insn))) == ZERO_EXTRACT + || GET_CODE (SET_DEST (PATTERN (insn))) == SIGN_EXTRACT + || GET_CODE (SET_SRC (PATTERN (insn))) == ZERO_EXTRACT + || GET_CODE (SET_SRC (PATTERN (insn))) == SIGN_EXTRACT)) + { + for (links = LOG_LINKS (insn); links; links = XEXP (links, 1)) + if (GET_CODE (XEXP (links, 0)) != NOTE) + for (nextlinks = XEXP (links, 1); nextlinks; + nextlinks = XEXP (nextlinks, 1)) + if (try_combine (insn, XEXP (links, 0), XEXP (nextlinks, 0))) + goto retry; + } +#endif + if (GET_CODE (insn) != NOTE) + record_dead_and_set_regs (insn); + prev = insn; + } + else if (GET_CODE (insn) != NOTE) + prev = 0; + } + total_attempts += combine_attempts; + total_merges += combine_merges; + total_extras += combine_extras; + total_successes += combine_successes; +} + +/* Try to combine the insns I1 and I2 into I3. + Here I1 appears earlier than I2, which is earlier than I3. + I1 can be zero; then we combine just I2 into I3. + + Return 1 if successful; if that happens, I1 and I2 are pseudo-deleted + by turning them into NOTEs, and I3 is modified. + Return 0 if the combination does not work. Then nothing is changed. */ + +static int +try_combine (i3, i2, i1) + register rtx i3, i2, i1; +{ + register rtx newpat; + int added_sets_1 = 0; + int added_sets_2 = 0; + int total_sets; + int i2_is_used; + register rtx link; + int insn_code_number; + rtx i2dest, i2src; + rtx i1dest, i1src; + int maxreg; + rtx temp; + int i; + + combine_attempts++; + + /* Don't combine with something already used up by combination. */ + + if (GET_CODE (i2) == NOTE + || (i1 && GET_CODE (i1) == NOTE)) + return 0; + + /* 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. */ + + if (INSN_CUID (i2) < last_call_cuid + || (i1 && INSN_CUID (i1) < last_call_cuid)) + return 0; + + /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0. + That REG must be either set or dead by the final instruction + (so that we can safely forget about setting it). + Also test use_crosses_set_p to make sure that the value + that is to be substituted for the register + does not use any registers whose values alter in between. + Do not try combining with moves from one register to another + since it is better to let them be tied by register allocation. + (There is a switch to permit such combination; except the insns + that copy a function value into another register are never combined + because moving that too far away from the function call could cause + something else to be stored in that register in the interim.) + + A set of a SUBREG is considered as if it were a set from + SUBREG. Thus, (SET (SUBREG:X (REG:Y...)) (something:X...)) + is handled by substituting (SUBREG:Y (something:X...)) for (REG:Y...). */ + + if (GET_CODE (PATTERN (i2)) != SET) + return 0; + i2dest = SET_DEST (PATTERN (i2)); + i2src = SET_SRC (PATTERN (i2)); + if (GET_CODE (i2dest) == SUBREG) + { + i2dest = SUBREG_REG (i2dest); + i2src = gen_rtx (SUBREG, GET_MODE (i2dest), i2src, 0); + } + /* Don't eliminate a store in the stack pointer. */ + if (i2dest == stack_pointer_rtx) + return 0; + /* Don't install a subreg involving two modes not tieable. + It can worsen register allocation, and can even make invalid reload insns, + since the reg inside may need to be copied from in the outside mode, + and that may be invalid if it is an fp reg copied in integer mode. */ + if (GET_CODE (i2src) == SUBREG + && ! MODES_TIEABLE_P (GET_MODE (i2src), GET_MODE (SUBREG_REG (i2src)))) + return 0; + if (GET_CODE (i2dest) != CC0 + && (GET_CODE (i2dest) != REG + || (GET_CODE (i2src) == REG + /* Do allow the combination of y = x; x = y; (with x dead) + because the result will turn into nothing. */ + && !(GET_CODE (PATTERN (i3)) == SET + && i2src == SET_DEST (PATTERN (i3))) + && (!flag_combine_regs + /* Don't substitute a function value reg for any other. */ + || FUNCTION_VALUE_REGNO_P (REGNO (i2src)))) + || GET_CODE (i2src) == CALL + /* Don't substitute into an incremented register. */ + || find_reg_note (i3, REG_INC, i2dest) + || use_crosses_set_p (i2src, INSN_CUID (i2)))) + return 0; + if (GET_CODE (i2src) == ASM_OPERANDS && MEM_VOLATILE_P (i2src)) + return 0; + /* Don't substitute for a register intended as a clobberable operand. */ + if (GET_CODE (PATTERN (i3)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (i3), 0); i++) + if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER + && XEXP (XVECEXP (PATTERN (i3), 0, i), 0) == i2dest) + return 0; + + if (i1 != 0) + { + if (GET_CODE (PATTERN (i1)) != SET) + return 0; + i1dest = SET_DEST (PATTERN (i1)); + i1src = SET_SRC (PATTERN (i1)); + if (GET_CODE (i1dest) == SUBREG) + { + i1dest = SUBREG_REG (i1dest); + i1src = gen_rtx (SUBREG, GET_MODE (i1dest), i1src, 0); + } + if (i1dest == stack_pointer_rtx) + return 0; + if (GET_CODE (i1src) == SUBREG + && ! MODES_TIEABLE_P (GET_MODE (i1src), + GET_MODE (SUBREG_REG (i1src)))) + return 0; + if (GET_CODE (i1dest) != CC0 + && (GET_CODE (i1dest) != REG + || (GET_CODE (i1src) == REG + && (!flag_combine_regs + || FUNCTION_VALUE_REGNO_P (REGNO (i1src)))) + || GET_CODE (i1src) == CALL + || find_reg_note (i3, REG_INC, i1dest) + || find_reg_note (i2, REG_INC, i1dest) + || use_crosses_set_p (i1src, INSN_CUID (i1)))) + return 0; + if (GET_CODE (i1src) == ASM_OPERANDS && MEM_VOLATILE_P (i1src)) + return 0; + /* Don't substitute for a register intended as a clobberable operand. */ + if (GET_CODE (PATTERN (i3)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (i3), 0); i++) + if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER + && XEXP (XVECEXP (PATTERN (i3), 0, i), 0) == i1dest) + return 0; + } + + /* If it is better that two different modes keep two different pseudos, + avoid combining them. */ + if (GET_CODE (PATTERN (i3)) == SET) + { + rtx i3dest = SET_DEST (PATTERN (i3)); + while (GET_CODE (i3dest) == SUBREG + || GET_CODE (i3dest) == STRICT_LOW_PART + || GET_CODE (i3dest) == SIGN_EXTRACT + || GET_CODE (i3dest) == ZERO_EXTRACT) + i3dest = SUBREG_REG (i3dest); + + if (SET_SRC (PATTERN (i3)) == i2dest + && GET_CODE (i3dest) == REG + && ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (i3dest))) + return 0; + } + + /* If I2 contains anything volatile, reject, unless nothing + volatile comes between it and I3. */ + if (volatile_refs_p (PATTERN (i2))) + { + rtx insn; + for (insn = NEXT_INSN (i2); insn != i3; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + if (volatile_refs_p (PATTERN (insn))) + return 0; + } + /* Likewise for I1; nothing volatile can come between it and I3, + except optionally I2. */ + if (i1 && volatile_refs_p (PATTERN (i1))) + { + rtx insn; + rtx end = (volatile_refs_p (PATTERN (i2)) ? i2 : i3); + for (insn = NEXT_INSN (i1); insn != end; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + if (volatile_refs_p (PATTERN (insn))) + return 0; + } + + /* If I1 or I2 contains an autoincrement or autodecrement, + make sure that register is not used between there and I3, + and not already used in I3 either. + Also insist that I3 not be a jump; if it were one + and the incremented register were spilled, we would lose. */ + for (link = REG_NOTES (i2); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (GET_CODE (i3) == JUMP_INSN + || reg_used_between_p (XEXP (link, 0), i2, i3) + || reg_mentioned_p (XEXP (link, 0), PATTERN (i3)))) + return 0; + + if (i1) + for (link = REG_NOTES (i1); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (GET_CODE (i3) == JUMP_INSN + || reg_used_between_p (XEXP (link, 0), i1, i3) + || reg_mentioned_p (XEXP (link, 0), PATTERN (i3)))) + return 0; + + /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd, + EXCEPT in one case: I3 has a post-inc in an output operand. */ + if (!(GET_CODE (PATTERN (i3)) == SET + && GET_CODE (SET_SRC (PATTERN (i3))) == REG + && GET_CODE (SET_DEST (PATTERN (i3))) == MEM + && (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. */ + for (link = REG_NOTES (i3); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC + && (reg_mentioned_p (XEXP (link, 0), PATTERN (i2)) + || (i1 != 0 + && reg_mentioned_p (XEXP (link, 0), PATTERN (i1))))) + return 0; + + /* Don't combine an insn I1 or I2 that follows a CC0-setting insn. + An insn that uses CC0 must not be separated from the one that sets it. + 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. */ + temp = PREV_INSN (i2); + while (temp && GET_CODE (temp) == NOTE) + temp = PREV_INSN (temp); + if (temp && GET_CODE (temp) == INSN && sets_cc0_p (PATTERN (temp))) + return 0; + if (i1) + { + temp = PREV_INSN (i2); + while (temp && GET_CODE (temp) == NOTE) + temp = PREV_INSN (temp); + if (temp && GET_CODE (temp) == INSN && sets_cc0_p (PATTERN (temp))) + return 0; + } + + /* 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. */ + added_sets_2 = (GET_CODE (i2dest) != CC0 + && ! dead_or_set_p (i3, i2dest)); + if (i1) + added_sets_1 = ! (dead_or_set_p (i3, i1dest) + || dead_or_set_p (i2, i1dest)); + + combine_merges++; + + undobuf.num_undo = 0; + undobuf.storage = 0; + + /* Substitute in the latest insn for the regs set by the earlier ones. */ + + maxreg = max_reg_num (); + + subst_insn = i3; + n_occurrences = 0; /* `subst' counts here */ + + newpat = subst (PATTERN (i3), i2dest, i2src); + /* Record whether i2's body now appears within i3's body. */ + i2_is_used = n_occurrences; + + if (i1) + { + n_occurrences = 0; + newpat = subst (newpat, i1dest, i1src); + } + + if (GET_CODE (PATTERN (i3)) == SET + && SET_DEST (PATTERN (i3)) == cc0_rtx + && (GET_CODE (SET_SRC (PATTERN (i3))) == AND + || GET_CODE (SET_SRC (PATTERN (i3))) == LSHIFTRT) + && next_insn_tests_no_inequality (i3)) + simplify_set_cc0_and (i3); + + if (max_reg_num () != maxreg) + abort (); + + /* If the actions of the earler 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++; + + /* Arrange to free later what we allocate now + if we don't accept this combination. */ + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + + 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)); + bcopy (&old->elem[0], &XVECEXP (newpat, 0, 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) = PATTERN (i1); + } + if (added_sets_2) + { + /* If there is no I1, use I2's body as is. */ + if (i1 == 0 + /* If I2 was stuck into I3, then anything within it has + already had I1 substituted into it when that was done to I3. */ + || i2_is_used) + { + XVECEXP (newpat, 0, --total_sets) = PATTERN (i2); + } + else + XVECEXP (newpat, 0, --total_sets) + = subst (PATTERN (i2), i1dest, i1src); + } + } + + /* Fail if an autoincrement side-effect has been duplicated. */ + if ((i2_is_used > 1 && find_reg_note (i2, REG_INC, 0) != 0) + || (i1 != 0 && n_occurrences > 1 && find_reg_note (i1, REG_INC, 0) != 0)) + { + undo_all (); + return 0; + } + + /* Is the result of combination a valid instruction? */ + insn_code_number = recog (newpat, i3); + + if (insn_code_number >= 0 + /* Is the result a reasonable ASM_OPERANDS? */ + || (check_asm_operands (newpat) && ! added_sets_1 && ! added_sets_2)) + { + /* Yes. Install it. */ + register int regno; + INSN_CODE (i3) = insn_code_number; + PATTERN (i3) = newpat; + /* If anything was substituted more than once, + copy it to avoid invalid shared rtl structure. */ + copy_substitutions (); + /* The data flowing into I2 now flows into I3. + But we cannot always move all of I2's LOG_LINKS into I3, + since they must go to a setting of a REG from the + first use following. If I2 was the first use following a set, + I3 is now a use, but it is not the first use + if some instruction between I2 and I3 is also a use. + Here, for simplicity, we move all the links only if + there are no real insns between I2 and I3. + Otherwise, we move only links that correspond to regs + that used to die in I2. They are always safe to move. */ + add_links (i3, i2, adjacent_insns_p (i2, i3)); + /* Most REGs that previously died in I2 now die in I3. */ + move_deaths (i2src, INSN_CUID (i2), i3); + if (GET_CODE (i2dest) == REG) + { + /* If the reg formerly set in I2 died only once and that was in I3, + zero its use count so it won't make `reload' do any work. */ + regno = REGNO (i2dest); + if (! added_sets_2) + { + reg_n_sets[regno]--; + /* Used to check && regno_dead_p (regno, i3) also here. */ + if (reg_n_sets[regno] == 0 + && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] + & (1 << (regno % HOST_BITS_PER_INT)))) + reg_n_refs[regno] = 0; + } + /* If a ref to REGNO was substituted into I3 from I2, + then it still dies there if it previously did. + Otherwise either REGNO never did die in I3 so remove_death is safe + or this entire life of REGNO is gone so remove its death. */ + if (!added_sets_2 + && ! reg_mentioned_p (i2dest, PATTERN (i3))) + remove_death (regno, i3); + } + /* Any registers previously autoincremented in I2 + are now incremented in I3. */ + add_incs (i3, REG_NOTES (i2)); + if (i1) + { + /* Likewise, merge the info from I1 and get rid of it. */ + add_links (i3, i1, + adjacent_insns_p (i1, i2) && adjacent_insns_p (i2, i3)); + move_deaths (i1src, INSN_CUID (i1), i3); + if (GET_CODE (i1dest) == REG) + { + regno = REGNO (i1dest); + if (! added_sets_1) + { + reg_n_sets[regno]--; + /* Used to also check && regno_dead_p (regno, i3) here. */ + + if (reg_n_sets[regno] == 0 + && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] + & (1 << (regno % HOST_BITS_PER_INT)))) + + reg_n_refs[regno] = 0; + } + /* If a ref to REGNO was substituted into I3 from I1, + then it still dies there if it previously did. + Else either REGNO never did die in I3 so remove_death is safe + or this entire life of REGNO is gone so remove its death. */ + if (! added_sets_1 + && ! reg_mentioned_p (i1dest, PATTERN (i3))) + remove_death (regno, i3); + } + add_incs (i3, REG_NOTES (i1)); + LOG_LINKS (i1) = 0; + PUT_CODE (i1, NOTE); + NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (i1) = 0; + } + /* Get rid of I2. */ + LOG_LINKS (i2) = 0; + PUT_CODE (i2, NOTE); + NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (i2) = 0; + + combine_successes++; + return 1; + } + + /* Failure: change I3 back the way it was. */ + undo_all (); + + return 0; +} + +/* Undo all the modifications recorded in undobuf. */ + +static void +undo_all () +{ + register int i; + if (undobuf.num_undo > MAX_UNDO) + undobuf.num_undo = MAX_UNDO; + for (i = undobuf.num_undo - 1; i >= 0; i--) + *undobuf.undo[i].where = undobuf.undo[i].old_contents; + if (undobuf.storage) + obfree (undobuf.storage); + undobuf.num_undo = 0; + undobuf.storage = 0; +} + +/* If this insn had more than one substitution, + copy all but one, so that no invalid shared substructure is introduced. */ + +static void +copy_substitutions () +{ + register int i; + if (undobuf.num_undo > 1) + { + for (i = undobuf.num_undo - 1; i >= 1; i--) + if (! undobuf.undo[i].is_int) + *undobuf.undo[i].where = copy_rtx (*undobuf.undo[i].where); + } +} + +/* Throughout X, replace FROM with TO, and return the result. + The result is TO if X is FROM; + otherwise the result is X, but its contents may have been modified. + If they were modified, a record was made in undobuf so that + undo_all will (among other things) return X to its original state. + + If the number of changes necessary is too much to record to undo, + the excess changes are not made, so the result is invalid. + The changes already made can still be undone. + undobuf.num_undo is incremented for such changes, so by testing that + the caller can tell whether the result is valid. + + `n_occurrences' is incremented each time FROM is replaced. */ + +static rtx +subst (x, from, to) + register rtx x, from, to; +{ + register char *fmt; + register int len, i; + register enum rtx_code code; + char was_replaced[2]; + +#define SUBST(INTO, NEWVAL) \ + do { if (undobuf.num_undo < MAX_UNDO) \ + { \ + undobuf.undo[undobuf.num_undo].where = &INTO; \ + undobuf.undo[undobuf.num_undo].old_contents = INTO; \ + undobuf.undo[undobuf.num_undo].is_int = 0; \ + INTO = NEWVAL; \ + } \ + undobuf.num_undo++; } while (0) + +#define SUBST_INT(INTO, NEWVAL) \ + do { if (undobuf.num_undo < MAX_UNDO) \ + { \ + struct undo_int *u = (struct undo_int *)&undobuf.undo[undobuf.num_undo];\ + u->where = &INTO; \ + u->old_contents = INTO; \ + u->is_int = 1; \ + INTO = NEWVAL; \ + } \ + undobuf.num_undo++; } while (0) + +/* FAKE_EXTEND_SAFE_P (MODE, FROM) is 1 if (subreg:MODE FROM 0) is a safe + replacement for (zero_extend:MODE FROM) or (sign_extend:MODE FROM). + If it is 0, that cannot be done. We can now do this for any MEM + because (SUBREG (MEM...)) is guaranteed to cause the MEM to be reloaded. + If not for that, MEM's would very rarely be safe. */ + +/* Reject MODEs bigger than a word, because we might not be able + to reference a two-register group starting with an arbitrary register + (and currently gen_lowpart might crash for a SUBREG). */ + +#define FAKE_EXTEND_SAFE_P(MODE, FROM) \ + (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD \ + && (GET_CODE (FROM) == REG || GET_CODE (FROM) == SUBREG \ + || GET_CODE (FROM) == MEM)) + + if (x == from) + return to; + + /* It is possible to have a subexpression appear twice in the insn. + Suppose that FROM is a register that appears within TO. + Then, after that subexpression has been scanned once by `subst', + the second time it is scanned, TO may be found. If we were + to scan TO here, we would find FROM within it and create a + self-referent rtl structure which is completely wrong. */ + if (x == to) + return to; + + code = GET_CODE (x); + + /* A little bit of algebraic simplification here. */ + switch (code) + { + /* This case has no effect except to speed things up. */ + case REG: + case CONST_INT: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case PC: + case CC0: + return x; + } + + was_replaced[0] = 0; + was_replaced[1] = 0; + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + /* Don't replace FROM where it is being stored in rather than used. */ + if (code == SET && SET_DEST (x) == from) + fmt = "ie"; + if (code == SET && GET_CODE (SET_DEST (x)) == SUBREG + && SUBREG_REG (SET_DEST (x)) == from) + fmt = "ie"; + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + register rtx new; + if (XVECEXP (x, i, j) == from) + new = to, n_occurrences++; + else + new = subst (XVECEXP (x, i, j), from, to); + if (new != XVECEXP (x, i, j)) + SUBST (XVECEXP (x, i, j), new); + } + } + else if (fmt[i] == 'e') + { + register rtx new; + + if (XEXP (x, i) == from) + { + new = to; + n_occurrences++; + if (i < 2) + was_replaced[i] = 1; + } + else + new = subst (XEXP (x, i), from, to); + + if (new != XEXP (x, i)) + SUBST (XEXP (x, i), new); + } + } + + /* A little bit of algebraic simplification here. */ + switch (code) + { + case SUBREG: + /* Changing mode twice with SUBREG => just change it once, + or not at all if changing back to starting mode. */ + if (SUBREG_REG (x) == to + && GET_CODE (to) == SUBREG) + { + if (GET_MODE (x) == GET_MODE (SUBREG_REG (to))) + if (SUBREG_WORD (x) == 0 && SUBREG_WORD (to) == 0) + return SUBREG_REG (to); + SUBST (SUBREG_REG (x), SUBREG_REG (to)); + if (SUBREG_WORD (to) != 0) + SUBST_INT (SUBREG_WORD (x), SUBREG_WORD (x) + SUBREG_WORD (to)); + } + if (SUBREG_REG (x) == to + && (GET_CODE (to) == SIGN_EXTEND || GET_CODE (to) == ZERO_EXTEND) + && subreg_lowpart_p (x)) + { + /* (subreg (sign_extend X)) is X, if it has same mode as X. */ + if (GET_MODE (x) == GET_MODE (XEXP (to, 0))) + return XEXP (to, 0); + /* (subreg (sign_extend X)), if it has a mode wider than X, + can be done with (sign_extend X). */ + if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (XEXP (to, 0)))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (GET_CODE (to), GET_MODE (x), XEXP (to, 0)); + } + /* Extend and then truncate smaller than it was to start with: + no need to extend. */ + if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (XEXP (to, 0)))) + { + SUBST (XEXP (x, 0), XEXP (to, 0)); + } + } + /* (subreg:A (mem:B X) N) becomes a modified MEM. + If we can't do that safely, then it becomes something nonsensical + so that this combination won't take place. + This avoids producing any (subreg (mem))s except in the special + paradoxical case where gen_lowpart_for_combine makes them. */ + if (SUBREG_REG (x) == to + && GET_CODE (to) == MEM) + { + int endian_offset = 0; + /* Don't combine this if mode A is wider than B. */ + if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (to))) + return gen_rtx (CLOBBER, VOIDmode, const0_rtx); + /* Don't change the mode of the MEM + if that would change the meaning of the address. */ + if (mode_dependent_address_p (XEXP (to, 0))) + return gen_rtx (CLOBBER, VOIDmode, const0_rtx); +#ifdef BYTES_BIG_ENDIAN + if (GET_MODE_SIZE (GET_MODE (x)) < UNITS_PER_WORD) + endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (x)); + if (GET_MODE_SIZE (GET_MODE (to)) < UNITS_PER_WORD) + endian_offset -= UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (to)); +#endif + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + /* Note if the plus_constant doesn't make a valid address + then this combination won't be accepted. */ + return gen_rtx (MEM, GET_MODE (x), + plus_constant (XEXP (to, 0), + (SUBREG_WORD (x) * UNITS_PER_WORD + + endian_offset))); + } + break; + + case NOT: + /* (not (minus X 1)) can become (neg X). */ + if (was_replaced[0] + && ((GET_CODE (to) == PLUS && INTVAL (XEXP (to, 1)) == -1) + || (GET_CODE (to) == MINUS && XEXP (to, 1) == const1_rtx))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (NEG, GET_MODE (to), XEXP (to, 0)); + } + /* Don't let substitution introduce double-negatives. */ + if (was_replaced[0] + && GET_CODE (to) == code) + return XEXP (to, 0); + break; + + case NEG: + /* (neg (minus X Y)) can become (minus Y X). */ + if (was_replaced[0] && GET_CODE (to) == MINUS) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (MINUS, GET_MODE (to), + XEXP (to, 1), XEXP (to, 0)); + } + /* Don't let substitution introduce double-negatives. */ + if (was_replaced[0] + && GET_CODE (to) == code) + return XEXP (to, 0); + break; + + case FLOAT_TRUNCATE: + /* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */ + if (was_replaced[0] + && GET_CODE (to) == FLOAT_EXTEND + && GET_MODE (XEXP (to, 0)) == GET_MODE (x)) + return XEXP (to, 0); + break; + +#if 0 + case COMPARE: + /* -x>0 if 0>x. */ + if (GET_CODE (XEXP (x, 0)) == NEG && XEXP (x, 1) == const0_rtx) + { + SUBST (XEXP (x, 1), XEXP (XEXP (x, 0), 0)); + SUBST (XEXP (x, 0), const0_rtx); + } + if (GET_CODE (XEXP (x, 1)) == NEG && XEXP (x, 0) == const0_rtx) + { + SUBST (XEXP (x, 0), XEXP (XEXP (x, 1), 0)); + SUBST (XEXP (x, 1), const0_rtx); + } + break; +#endif + + case PLUS: +#if 0 /* Turned off for caution: turn it on after 1.36. */ + /* Identify constant sums as such. */ + if ((was_replaced[0] || was_replaced[1]) + && CONSTANT_P (XEXP (x, 0)) + && CONSTANT_P (XEXP (x, 1))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (CONST, GET_MODE (x), x); + } +#endif + /* In (plus <foo> (ashift <bar> <n>)) + change the shift to a multiply so we can recognize + scaled indexed addresses. */ + if ((was_replaced[0] + || was_replaced[1]) + && GET_CODE (to) == ASHIFT + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (to, 1)) < HOST_BITS_PER_INT) + { + rtx temp; + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + temp = gen_rtx (MULT, GET_MODE (to), + XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + 1 << INTVAL (XEXP (to, 1)))); + if (was_replaced[0]) + SUBST (XEXP (x, 0), temp); + else + SUBST (XEXP (x, 1), temp); + } + /* (plus X (neg Y)) becomes (minus X Y). */ + if (GET_CODE (XEXP (x, 1)) == NEG) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (MINUS, GET_MODE (x), + XEXP (x, 0), XEXP (XEXP (x, 1), 0)); + } + /* (plus (neg X) Y) becomes (minus Y X). */ + if (GET_CODE (XEXP (x, 0)) == NEG) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (MINUS, GET_MODE (x), + XEXP (x, 1), XEXP (XEXP (x, 0), 0)); + } + /* (plus (plus x c1) c2) => (plus x c1+c2) */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + { + int sum = (INTVAL (XEXP (x, 1)) + + INTVAL (XEXP (XEXP (x, 0), 1))); + if (sum == 0) + return XEXP (XEXP (x, 0), 0); + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 1), gen_rtx (CONST_INT, VOIDmode, sum)); + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + } + /* If we have something (putative index) being added to a sum, + associate it so that any constant term is outermost. + That's because that's the way indexed addresses are + now supposed to appear. */ + if (((was_replaced[0] && GET_CODE (XEXP (x, 1)) == PLUS) + || (was_replaced[1] && GET_CODE (XEXP (x, 0)) == PLUS)) + || + ((was_replaced[0] || was_replaced[1]) + && GET_CODE (to) == PLUS)) + { + rtx offset = 0, base, index; + if (GET_CODE (to) != PLUS) + { + index = to; + base = was_replaced[0] ? XEXP (x, 1) : XEXP (x, 0); + } + else + { + index = was_replaced[0] ? XEXP (x, 1) : XEXP (x, 0); + base = to; + } + if (CONSTANT_ADDRESS_P (XEXP (base, 0))) + { + offset = XEXP (base, 0); + base = XEXP (base, 1); + } + else if (CONSTANT_ADDRESS_P (XEXP (base, 1))) + { + offset = XEXP (base, 1); + base = XEXP (base, 0); + } + if (offset != 0) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + if (GET_CODE (offset) == CONST_INT) + return plus_constant (gen_rtx (PLUS, GET_MODE (index), + base, index), + INTVAL (offset)); + if (GET_CODE (index) == CONST_INT) + return plus_constant (gen_rtx (PLUS, GET_MODE (offset), + base, offset), + INTVAL (index)); + return gen_rtx (PLUS, GET_MODE (index), + gen_rtx (PLUS, GET_MODE (index), + base, index), + offset); + } + } + break; + + case EQ: + case NE: + /* If comparing a subreg against zero, discard the subreg. */ + if (was_replaced[0] + && GET_CODE (to) == SUBREG + && SUBREG_WORD (to) == 0 + && XEXP (x, 1) == const0_rtx) + SUBST (XEXP (x, 0), SUBREG_REG (to)); + + /* If comparing a ZERO_EXTRACT against zero, + canonicalize to a SIGN_EXTRACT, + since the two are equivalent here. */ + if (was_replaced[0] + && GET_CODE (to) == ZERO_EXTRACT + && XEXP (x, 1) == const0_rtx) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 0), + gen_rtx (SIGN_EXTRACT, GET_MODE (to), + XEXP (to, 0), XEXP (to, 1), + XEXP (to, 2))); + } +#ifndef BITS_BIG_ENDIAN + /* If we are putting (ASHIFT 1 x) into (EQ (AND ... y) 0), + arrange to return (EQ (SIGN_EXTRACT y 1 x) 0), + which is what jump-on-bit instructions are written with. */ + else if (XEXP (x, 1) == const0_rtx + && GET_CODE (XEXP (x, 0)) == AND + && (XEXP (XEXP (x, 0), 0) == to + || XEXP (XEXP (x, 0), 1) == to) + && GET_CODE (to) == ASHIFT + && XEXP (to, 0) == const1_rtx) + { + register rtx y = XEXP (XEXP (x, 0), + XEXP (XEXP (x, 0), 0) == to); + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 0), + gen_rtx (SIGN_EXTRACT, GET_MODE (to), + y, + const1_rtx, XEXP (to, 1))); + } +#endif /* not BITS_BIG_ENDIAN */ + /* Negation is a no-op before equality test against zero. */ + if (GET_CODE (XEXP (x, 0)) == NEG && XEXP (x, 1) == const0_rtx) + { + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + } + if (GET_CODE (XEXP (x, 1)) == NEG && XEXP (x, 0) == const0_rtx) + { + SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0)); + } + break; + + case ZERO_EXTEND: + /* Nested zero-extends are equivalent to just one. */ + if (was_replaced[0] + && GET_CODE (to) == ZERO_EXTEND) + SUBST (XEXP (x, 0), XEXP (to, 0)); + /* Zero extending a constant int can be replaced + by a zero-extended constant. */ + if (was_replaced[0] + && HOST_BITS_PER_INT >= GET_MODE_BITSIZE (GET_MODE (from)) + && GET_CODE (to) == CONST_INT) + { + int intval = INTVAL (to) & GET_MODE_MASK (GET_MODE (from)); + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (CONST_INT, VOIDmode, intval); + } + /* Zero-extending the result of an and with a constant can be done + with a wider and. */ + if (was_replaced[0] + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) + /* Avoid getting wrong result if the constant has high bits set + that are irrelevant in the narrow mode where it is being used. */ + && 0 == (INTVAL (XEXP (to, 1)) + & ~ GET_MODE_MASK (GET_MODE (to)))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + XEXP (to, 1)); + } + /* Change (zero_extend:M (subreg:N (zero_extract:M ...) 0)) + to (zero_extract:M ...) if the field extracted fits in mode N. */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTRACT + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT + && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) + <= GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))) + { + return XEXP (XEXP (x, 0), 0); + } + /* Change (zero_extend:M (subreg:N (and:M ... <const>) 0)) + to (and:M ...) if the significant bits fit in mode N. */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_REG (XEXP (x, 0)) == to + && SUBREG_WORD (XEXP (x, 0)) == 0 + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) + /* Avoid getting wrong result if the constant has high bits set + that are irrelevant in the narrow mode where it is being used. */ + && 0 == (INTVAL (XEXP (to, 1)) + & ~ GET_MODE_MASK (GET_MODE (to)))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + XEXP (to, 1)); + } + /* In (zero_extend:M (subreg:N (lshiftrt:M REG))), + where REG was assigned from (zero_extend:M (any:N ...)), + remove the outer zero extension. */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_REG (XEXP (x, 0)) == to + && SUBREG_WORD (XEXP (x, 0)) == 0 + && GET_CODE (to) == LSHIFTRT) + { + rtx tmp = XEXP (to, 0); + + /* See if arg of LSHIFTRT is a register whose value we can find. */ + if (GET_CODE (tmp) == REG) + if (reg_n_sets[REGNO (tmp)] == 1 + && reg_last_set[REGNO (tmp)] != 0 + && SET_DEST (PATTERN (reg_last_set[REGNO (tmp)])) == tmp) + tmp = SET_SRC (PATTERN (reg_last_set[REGNO (tmp)])); + else + break; + + if (GET_CODE (tmp) == ZERO_EXTEND + && GET_MODE (tmp) == GET_MODE (x) + && GET_MODE (XEXP (tmp, 0)) == GET_MODE (XEXP (x, 0))) + return SUBREG_REG (XEXP (x, 0)); + } + break; + + case SIGN_EXTEND: + /* Nested sign-extends are equivalent to just one. */ + if (was_replaced[0] + && GET_CODE (to) == SIGN_EXTEND) + SUBST (XEXP (x, 0), XEXP (to, 0)); + /* Sign extending a constant int can be replaced + by a sign-extended constant. */ + if (was_replaced[0] + && HOST_BITS_PER_INT >= GET_MODE_BITSIZE (GET_MODE (from)) + && GET_CODE (to) == CONST_INT) + { + int intval = INTVAL (to); + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + if (intval > 0 + && (intval & (1 << (GET_MODE_BITSIZE (GET_MODE (from)) - 1)))) + intval |= ~ GET_MODE_MASK (GET_MODE (from)); + return gen_rtx (CONST_INT, VOIDmode, intval); + } + /* Sign-extending the result of an and with a constant can be done + with a wider and, provided the high bit of the constant is 0. */ + if (was_replaced[0] + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) + && ((INTVAL (XEXP (to, 1)) + & (-1 << (GET_MODE_BITSIZE (GET_MODE (to)) - 1))) + == 0)) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + XEXP (to, 1)); + } + /* hacks added by tiemann. */ + /* Change (sign_extend:M (subreg:N (and:M ... <const>) 0)) + to (and:M ...), provided the result fits in mode N, + and the high bit of the constant is 0 in mode N. */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_REG (XEXP (x, 0)) == to + && SUBREG_WORD (XEXP (x, 0)) == 0 + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0)) + && ((INTVAL (XEXP (to, 1)) + & (-1 << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))) + == 0)) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + XEXP (to, 1)); + } + /* In (sign_extend:M (subreg:N (ashiftrt:M REG))), + where REG was assigned from (sign_extend:M (any:N ...)), + remove the outer sign extension. */ + if (GET_CODE (XEXP (x, 0)) == SUBREG + && SUBREG_REG (XEXP (x, 0)) == to + && SUBREG_WORD (XEXP (x, 0)) == 0 + && GET_CODE (to) == ASHIFTRT) + { + rtx tmp = XEXP (to, 0); + + /* See if arg of LSHIFTRT is a register whose value we can find. */ + if (GET_CODE (tmp) == REG) + if (reg_n_sets[REGNO (tmp)] == 1 + && reg_last_set[REGNO (tmp)] != 0 + && SET_DEST (PATTERN (reg_last_set[REGNO (tmp)])) == tmp) + tmp = SET_SRC (PATTERN (reg_last_set[REGNO (tmp)])); + else + break; + + if (GET_CODE (tmp) == SIGN_EXTEND + && GET_MODE (tmp) == GET_MODE (x) + && GET_MODE (XEXP (tmp, 0)) == GET_MODE (XEXP (x, 0))) + return SUBREG_REG (XEXP (x, 0)); + } + break; + + case SET: + /* In (set (zero-extract <x> <n> <y>) (and <foo> <(2**n-1) | anything>)) + the `and' can be deleted. This can happen when storing a bit + that came from a set-flag insn followed by masking to one bit. */ + if (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && was_replaced[1] + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + && 0 == (((1 << INTVAL (XEXP (XEXP (x, 0), 1))) - 1) + & ~ INTVAL (XEXP (to, 1)))) + { + SUBST (XEXP (x, 1), XEXP (to, 0)); + } + /* In (set (zero-extract <x> <n> <y>) + (subreg (and <foo> <(2**n-1) | anything>))) + the `and' can be deleted. */ + if (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && GET_CODE (XEXP (x, 1)) == SUBREG + && SUBREG_WORD (XEXP (x, 1)) == 0 + && GET_CODE (SUBREG_REG (XEXP (x, 1))) == AND + && GET_CODE (XEXP (SUBREG_REG (XEXP (x, 1)), 1)) == CONST_INT + && 0 == (((1 << INTVAL (XEXP (XEXP (x, 0), 1))) - 1) + & ~ INTVAL (XEXP (SUBREG_REG (XEXP (x, 1)), 1)))) + { + SUBST (SUBREG_REG (XEXP (x, 1)), XEXP (SUBREG_REG (XEXP (x, 1)), 0)); + } + /* (set (zero_extract ...) (and/or/xor (zero_extract ...) const)), + if both zero_extracts have the same location, size and position, + can be changed to avoid the byte extracts. */ + if ((GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT + || GET_CODE (XEXP (x, 0)) == SIGN_EXTRACT) + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && (GET_CODE (XEXP (x, 1)) == AND + || GET_CODE (XEXP (x, 1)) == IOR + || GET_CODE (XEXP (x, 1)) == XOR) + && rtx_equal_p (XEXP (x, 0), XEXP (XEXP (x, 1), 0)) + && GET_CODE (XEXP (XEXP (x, 1), 0)) == GET_CODE (XEXP (x, 0)) + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + /* zero_extract can apply to a QImode even if the bits extracted + don't fit inside that byte. In such a case, we may not do this + optimization, since the OR or AND insn really would need + to fit in a byte. */ + && (INTVAL (XEXP (XEXP (x, 0), 1)) + INTVAL (XEXP (XEXP (x, 0), 2)) + < GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))))) + { + int shiftcount; + int newmask; +#ifdef BITS_BIG_ENDIAN + shiftcount + = GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))) + - INTVAL (XEXP (XEXP (x, 0), 1)) - INTVAL (XEXP (XEXP (x, 0), 2)); +#else + shiftcount + = INTVAL (XEXP (XEXP (x, 0), 2)); +#endif + newmask = ((INTVAL (XEXP (XEXP (x, 1), 1)) << shiftcount) + + (GET_CODE (XEXP (x, 1)) == AND + ? (1 << shiftcount) - 1 + : 0)); + if (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0))) + < HOST_BITS_PER_INT) + newmask &= (1 << GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (x, 0), 0)))) - 1; + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return + gen_rtx (SET, VOIDmode, + XEXP (XEXP (x, 0), 0), + gen_rtx (GET_CODE (XEXP (x, 1)), + GET_MODE (XEXP (XEXP (x, 0), 0)), + XEXP (XEXP (XEXP (x, 1), 0), 0), + gen_rtx (CONST_INT, VOIDmode, newmask))); + } + /* Can simplify (set (cc0) (compare (zero/sign_extend FOO) CONST)) + to (set (cc0) (compare FOO CONST)) if CONST fits in FOO's mode + and we are only testing equality. + In fact, this is valid for zero_extend if what follows is an + unsigned comparison, and for sign_extend with a signed comparison. */ + if (SET_DEST (x) == cc0_rtx + && GET_CODE (SET_SRC (x)) == COMPARE + && (GET_CODE (XEXP (SET_SRC (x), 0)) == ZERO_EXTEND + || GET_CODE (XEXP (SET_SRC (x), 0)) == SIGN_EXTEND) + && next_insn_tests_no_inequality (subst_insn) + && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT + /* This is overly cautious by one bit, but saves worrying about + whether it is zero-extension or sign extension. */ + && ((unsigned) INTVAL (XEXP (SET_SRC (x), 1)) + < (1 << (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0))) - 1)))) + SUBST (XEXP (SET_SRC (x), 0), XEXP (XEXP (SET_SRC (x), 0), 0)); + break; + + case AND: + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + rtx tem = simplify_and_const_int (x, to); + if (tem) + return tem; + } + break; + + case IOR: + case XOR: + /* (ior (ior x c1) c2) => (ior x c1|c2); likewise for xor. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (x, 0)) == code + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + { + int c0 = INTVAL (XEXP (x, 1)); + int c1 = INTVAL (XEXP (XEXP (x, 0), 1)); + int combined = (code == IOR ? c0 | c1 : c0 ^ c1); + + if (combined == 0) + return XEXP (XEXP (x, 0), 0); + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 1), gen_rtx (CONST_INT, VOIDmode, combined)); + SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0)); + break; + } + + case FLOAT: + /* (float (sign_extend <X>)) = (float <X>). */ + if (was_replaced[0] + && GET_CODE (to) == SIGN_EXTEND) + SUBST (XEXP (x, 0), XEXP (to, 0)); + break; + + case ZERO_EXTRACT: + /* (ZERO_EXTRACT (TRUNCATE x)...) + can become (ZERO_EXTRACT x ...). */ + if (was_replaced[0] + && GET_CODE (to) == TRUNCATE) + { +#ifdef BITS_BIG_ENDIAN + if (GET_CODE (XEXP (x, 2)) == CONST_INT) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + /* On a big-endian machine, must increment the bit-number + since sign bit is farther away in the pre-truncated value. */ + return gen_rtx (ZERO_EXTRACT, GET_MODE (x), + XEXP (to, 0), + XEXP (x, 1), + gen_rtx (CONST_INT, VOIDmode, + (INTVAL (XEXP (x, 2)) + + GET_MODE_BITSIZE (GET_MODE (XEXP (to, 0))) + - GET_MODE_BITSIZE (GET_MODE (to))))); + } +#else + SUBST (XEXP (x, 0), XEXP (to, 0)); +#endif + } + /* Extracting a single bit from the result of a shift: + see which bit it was before the shift and extract that directly. */ + if (was_replaced[0] + && (GET_CODE (to) == ASHIFTRT || GET_CODE (to) == LSHIFTRT + || GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) + && GET_CODE (XEXP (to, 1)) == CONST_INT + && XEXP (x, 1) == const1_rtx + && GET_CODE (XEXP (x, 2)) == CONST_INT) + { + int shift = INTVAL (XEXP (to, 1)); + int newpos; + if (GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) + shift = - shift; +#ifdef BITS_BIG_ENDIAN + shift = - shift; +#endif + newpos = INTVAL (XEXP (x, 2)) + shift; + if (newpos >= 0 && + newpos < GET_MODE_BITSIZE (GET_MODE (to))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (ZERO_EXTRACT, GET_MODE (x), + XEXP (to, 0), const1_rtx, + gen_rtx (CONST_INT, VOIDmode, newpos)); + } + } + break; + + case LSHIFTRT: + case ASHIFTRT: + case ROTATE: + case ROTATERT: +#ifdef SHIFT_COUNT_TRUNCATED + /* (lshift <X> (sign_extend <Y>)) = (lshift <X> <Y>) (most machines). + True for all kinds of shifts and also for zero_extend. */ + if (was_replaced[1] + && (GET_CODE (to) == SIGN_EXTEND + || GET_CODE (to) == ZERO_EXTEND) + && FAKE_EXTEND_SAFE_P (GET_MODE (to), XEXP (to, 0))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 1), + /* This is a perverse SUBREG, wider than its base. */ + gen_lowpart_for_combine (GET_MODE (to), XEXP (to, 0))); + } +#endif + /* Two shifts in a row of same kind + in same direction with constant counts + may be combined. */ + if (was_replaced[0] + && GET_CODE (to) == GET_CODE (x) + && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (to, 1)) > 0 + && INTVAL (XEXP (x, 1)) > 0 + && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (to, 1)) + < GET_MODE_BITSIZE (GET_MODE (x)))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (GET_CODE (x), GET_MODE (x), + XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + INTVAL (XEXP (x, 1)) + + INTVAL (XEXP (to, 1)))); + } + break; + + case LSHIFT: + case ASHIFT: +#ifdef SHIFT_COUNT_TRUNCATED + /* (lshift <X> (sign_extend <Y>)) = (lshift <X> <Y>) (most machines). + True for all kinds of shifts and also for zero_extend. */ + if (was_replaced[1] + && (GET_CODE (to) == SIGN_EXTEND + || GET_CODE (to) == ZERO_EXTEND) + && FAKE_EXTEND_SAFE_P (GET_MODE (to), XEXP (to, 0))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (x, 1), + /* This is a perverse SUBREG, wider than its base. */ + gen_lowpart_for_combine (GET_MODE (to), XEXP (to, 0))); + } +#endif + /* (lshift (and (lshiftrt <foo> <X>) <Y>) <X>) + happens copying between bit fields in similar structures. + It can be replaced by one and instruction. + It does not matter whether the shifts are logical or arithmetic. */ + if (GET_CODE (XEXP (x, 0)) == AND + && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) > 0 + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && XEXP (XEXP (x, 0), 0) == to + && (GET_CODE (to) == LSHIFTRT + || GET_CODE (to) == ASHIFTRT) +#if 0 +/* I now believe this restriction is unnecessary. + The outer shift will discard those bits in any case, right? */ + + /* If inner shift is arithmetic, either it shifts left or + the bits it shifts the sign into are zeroed by the and. */ + && (INTVAL (XEXP (x, 1)) < 0 + || ((unsigned) INTVAL (XEXP (XEXP (x, 0), 1)) + < 1 << (GET_MODE_BITSIZE (GET_MODE (x)) + - INTVAL (XEXP (x, 0))))) +#endif + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == INTVAL (XEXP (to, 1))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + /* The constant in the new `and' is <Y> << <X> + but clear out all bits that don't belong in our mode. */ + return gen_rtx (AND, GET_MODE (x), XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + (GET_MODE_MASK (GET_MODE (x)) + & ((GET_MODE_MASK (GET_MODE (x)) + & INTVAL (XEXP (XEXP (x, 0), 1))) + << INTVAL (XEXP (x, 1)))))); + } + /* Two shifts in a row in same direction with constant counts + may be combined. */ + if (was_replaced[0] + && (GET_CODE (to) == ASHIFT || GET_CODE (to) == LSHIFT) + && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (to, 1)) > 0 + && INTVAL (XEXP (x, 1)) > 0 + && (INTVAL (XEXP (x, 1)) + INTVAL (XEXP (to, 1)) + < GET_MODE_BITSIZE (GET_MODE (x)))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (GET_CODE (x), GET_MODE (x), + XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + INTVAL (XEXP (x, 1)) + + INTVAL (XEXP (to, 1)))); + } + /* (ashift (ashiftrt <foo> <X>) <X>) + (or, on some machines, (ashift (ashift <foo> <-X>) <X>) instead) + happens if you divide by 2**N and then multiply by 2**N. + It can be replaced by one `and' instruction. + It does not matter whether the shifts are logical or arithmetic. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) > 0 + && was_replaced[0] + && (((GET_CODE (to) == LSHIFTRT || GET_CODE (to) == ASHIFTRT) + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == INTVAL (XEXP (to, 1))) + || + ((GET_CODE (to) == LSHIFT || GET_CODE (to) == ASHIFT) + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (to, 1))))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + /* The constant in the new `and' is -1 << <X> + but clear out all bits that don't belong in our mode. */ + return gen_rtx (AND, GET_MODE (x), XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + (GET_MODE_MASK (GET_MODE (x)) + & (GET_MODE_MASK (GET_MODE (x)) + << INTVAL (XEXP (x, 1)))))); + } + + } + + return x; +} + +/* This is the AND case of the function subst. */ + +static rtx +simplify_and_const_int (x, to) + rtx x, to; +{ + register rtx varop = XEXP (x, 0); + register int constop = INTVAL (XEXP (x, 1)); + + /* (and (subreg (and <foo> <constant>) 0) <constant>) + results from an andsi followed by an andqi, + which happens frequently when storing bit-fields + on something whose result comes from an andsi. */ + if (GET_CODE (varop) == SUBREG + && XEXP (varop, 0) == to + && subreg_lowpart_p (varop) + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT + /* Verify that the result of the outer `and' + is not affected by any bits not defined in the inner `and'. + True if the outer mode is narrower, or if the outer constant + masks to zero all the bits that the inner mode doesn't have. */ + && (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (to)) + || (constop & ~ GET_MODE_MASK (GET_MODE (to))) == 0)) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + gen_rtx (CONST_INT, VOIDmode, + constop + /* Remember that the bits outside that mode + are not being changed, so the effect + is as if they were all 1. */ + & INTVAL (XEXP (to, 1)))); + } + /* (and:SI (zero_extract:SI ...) <constant>) + results from an andsi following a byte-fetch on risc machines. + When the constant includes all bits extracted, eliminate the `and'. */ + if (GET_CODE (varop) == ZERO_EXTRACT + && GET_CODE (XEXP (varop, 1)) == CONST_INT + /* The `and' must not clear any bits that the extract can give. */ + && (~ constop & ((1 << INTVAL (XEXP (varop, 1))) - 1)) == 0) + return varop; + /* (and (zero_extend <foo>) <constant>) + often results from storing in a bit-field something + that was calculated as a short. Replace with a single `and' + in whose constant all bits not in <foo>'s mode are zero. */ + if (varop == to + && GET_CODE (to) == ZERO_EXTEND + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + /* This is a perverse SUBREG, wider than its base. */ + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + gen_rtx (CONST_INT, VOIDmode, + constop & GET_MODE_MASK (GET_MODE (XEXP (to, 0))))); + } + /* (and (sign_extend <foo>) <constant>) + can be replaced with (and (subreg <foo>) <constant>) + if <constant> is narrower than <foo>'s mode, + or with (zero_extend <foo>) if <constant> is a mask for that mode. */ + if (varop == to + && GET_CODE (to) == SIGN_EXTEND + && ((unsigned) constop <= GET_MODE_MASK (GET_MODE (XEXP (to, 0)))) + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + if (constop == GET_MODE_MASK (GET_MODE (XEXP (to, 0)))) + return gen_rtx (ZERO_EXTEND, GET_MODE (x), XEXP (to, 0)); + return gen_rtx (AND, GET_MODE (x), + /* This is a perverse SUBREG, wider than its base. */ + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0)), + XEXP (x, 1)); + } + /* (and (and <foo> <constant>) <constant>) + comes from two and instructions in a row. */ + if (varop == to + && GET_CODE (to) == AND + && GET_CODE (XEXP (to, 1)) == CONST_INT) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (AND, GET_MODE (x), + XEXP (to, 0), + gen_rtx (CONST_INT, VOIDmode, + constop + & INTVAL (XEXP (to, 1)))); + } + /* (and (ashiftrt (ashift FOO N) N) CONST) + may be simplified to (and FOO CONST) if CONST masks off the bits + changed by the two shifts. */ + if (GET_CODE (varop) == ASHIFTRT + && GET_CODE (XEXP (varop, 1)) == CONST_INT + && XEXP (varop, 0) == to + && GET_CODE (to) == ASHIFT + && GET_CODE (XEXP (to, 1)) == CONST_INT + && INTVAL (XEXP (varop, 1)) == INTVAL (XEXP (to, 1)) + && ((unsigned) constop >> INTVAL (XEXP (varop, 1))) == 0) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + /* If CONST is a mask for the low byte, + change this into a zero-extend instruction + from just the low byte of FOO. */ + if (constop == GET_MODE_MASK (QImode)) + { + rtx temp = gen_lowpart_for_combine (QImode, XEXP (to, 0)); + if (GET_CODE (temp) != CLOBBER) + return gen_rtx (ZERO_EXTEND, GET_MODE (x), temp); + } + return gen_rtx (AND, GET_MODE (x), + XEXP (to, 0), XEXP (x, 1)); + } + /* (and (ashiftrt (zero_extend FOO) N) CONST) + may be simplified to (and (ashiftrt (subreg FOO) N) CONST) + if CONST masks off the bits changed by extension. */ + if ((GET_CODE (varop) == ASHIFTRT || GET_CODE (varop) == LSHIFTRT) + && GET_CODE (XEXP (varop, 1)) == CONST_INT + && XEXP (varop, 0) == to + && (GET_CODE (to) == ZERO_EXTEND || GET_CODE (to) == SIGN_EXTEND) + /* Verify the and discards all the extended bits. */ + && (((unsigned) constop << INTVAL (XEXP (varop, 1))) + >> GET_MODE_BITSIZE (GET_MODE (XEXP (to, 0)))) == 0 + && FAKE_EXTEND_SAFE_P (GET_MODE (x), XEXP (to, 0))) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + SUBST (XEXP (varop, 0), + gen_lowpart_for_combine (GET_MODE (x), XEXP (to, 0))); + return x; + } + /* (and x const) may be converted to (zero_extend (subreg x 0)). */ + if (constop == GET_MODE_MASK (QImode) + && GET_CODE (varop) == REG) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (ZERO_EXTEND, GET_MODE (x), + gen_rtx (SUBREG, QImode, varop, 0)); + } + if (constop == GET_MODE_MASK (HImode) + && GET_CODE (varop) == REG) + { + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + return gen_rtx (ZERO_EXTEND, GET_MODE (x), + gen_rtx (SUBREG, HImode, varop, 0)); + } + /* No simplification applies. */ + return 0; +} + +/* Like gen_lowpart but for use by combine. In combine it is not possible + to create any new pseudoregs. However, it is safe to create + invalid memory addresses, because combine will try to recognize + them and all they will do is make the combine attempt fail. + + If for some reason this cannot do its job, an rtx + (clobber (const_int 0)) is returned. + An insn containing that will not be recognized. */ + +#undef gen_lowpart + +static rtx +gen_lowpart_for_combine (mode, x) + enum machine_mode mode; + register rtx x; +{ + if (GET_CODE (x) == SUBREG || GET_CODE (x) == REG) + return gen_lowpart (mode, x); + if (GET_MODE (x) == mode) + return gen_rtx (CLOBBER, VOIDmode, const0_rtx); + if (GET_CODE (x) == MEM) + { + register int offset = 0; + + /* Refuse to work on a volatile memory ref. */ + if (MEM_VOLATILE_P (x)) + return gen_rtx (CLOBBER, VOIDmode, const0_rtx); + + /* If we want to refer to something bigger than the original memref, + generate a perverse subreg instead. That will force a reload + of the original memref X. */ + if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)) + return gen_rtx (SUBREG, mode, x, 0); + +#ifdef WORDS_BIG_ENDIAN + offset = (max (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) + - max (GET_MODE_SIZE (mode), UNITS_PER_WORD)); +#endif +#ifdef BYTES_BIG_ENDIAN + /* Adjust the address so that the address-after-the-data + is unchanged. */ + offset -= (min (UNITS_PER_WORD, GET_MODE_SIZE (mode)) + - min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); +#endif + return gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), + offset)); + } + else + return gen_rtx (CLOBBER, VOIDmode, const0_rtx); +} + +/* After substitution, if the resulting pattern looks like + (set (cc0) (and ...)) or (set (cc0) (lshiftrt ...)), + this function is called to simplify the + pattern into a bit-field operation if possible. */ + +static void +simplify_set_cc0_and (insn) + rtx insn; +{ + register rtx value = XEXP (PATTERN (insn), 1); + register rtx op0 = XEXP (value, 0); + register rtx op1 = XEXP (value, 1); + int offset = 0; + rtx var = 0; + rtx bitnum = 0; + int temp; + int unit; + rtx newpat; + + if (GET_CODE (value) == AND) + { + op0 = XEXP (value, 0); + op1 = XEXP (value, 1); + } + else if (GET_CODE (value) == LSHIFTRT) + { + /* If there is no AND, but there is a shift that discards + all but the sign bit, we can pretend that the shift result + is ANDed with 1. Otherwise we cannot handle just a shift. */ + if (GET_CODE (XEXP (value, 1)) == CONST_INT + && (INTVAL (XEXP (value, 1)) + == GET_MODE_BITSIZE (GET_MODE (value)) - 1)) + { + op0 = value; + op1 = const1_rtx; + } + else + return; + } + else + abort (); + + /* Look for a constant power of 2 or a shifted 1 + on either side of the AND. Set VAR to the other side. + Set BITNUM to the shift count of the 1 (as an rtx). + Or, if bit number is constant, set OFFSET to the bit number. */ + + switch (GET_CODE (op0)) + { + case CONST_INT: + temp = exact_log2 (INTVAL (op0)); + if (temp < 0) + return; + offset = temp; + var = op1; + break; + + case ASHIFT: + case LSHIFT: + if (XEXP (op0, 0) == const1_rtx) + { + bitnum = XEXP (op0, 1); + var = op1; + } + } + if (var == 0) + switch (GET_CODE (op1)) + { + case CONST_INT: + temp = exact_log2 (INTVAL (op1)); + if (temp < 0) + return; + offset = temp; + var = op0; + break; + + case ASHIFT: + case LSHIFT: + if (XEXP (op1, 0) == const1_rtx) + { + bitnum = XEXP (op1, 1); + var = op0; + } + } + + /* If VAR is 0, we didn't find something recognizable. */ + if (var == 0) + return; + + if (!undobuf.storage) + undobuf.storage = (char *) oballoc (0); + + /* If the bit position is currently exactly 0, + extract a right-shift from the variable portion. */ + if (offset == 0 + && (GET_CODE (var) == ASHIFTRT || GET_CODE (var) == LSHIFTRT)) + { + bitnum = XEXP (var, 1); + var = XEXP (var, 0); + } + + if (GET_CODE (var) == SUBREG && SUBREG_WORD (var) == 0) + var = SUBREG_REG (var); + + /* Note that BITNUM and OFFSET are always little-endian thru here + even on a big-endian machine. */ + +#ifdef BITS_BIG_ENDIAN + unit = GET_MODE_BITSIZE (GET_MODE (var)) - 1; + + if (bitnum != 0) + bitnum = gen_rtx (MINUS, SImode, + gen_rtx (CONST_INT, VOIDmode, unit), bitnum); + else + offset = unit - offset; +#endif + + if (bitnum == 0) + bitnum = gen_rtx (CONST_INT, VOIDmode, offset); + + newpat = gen_rtx (SET, VOIDmode, cc0_rtx, + gen_rtx (ZERO_EXTRACT, VOIDmode, var, const1_rtx, bitnum)); + if (recog (newpat, insn) >= 0) + { + if (undobuf.num_undo < MAX_UNDO) + { + undobuf.undo[undobuf.num_undo].where = &XEXP (PATTERN (insn), 1); + undobuf.undo[undobuf.num_undo].old_contents = value; + XEXP (PATTERN (insn), 1) = XEXP (newpat, 1); + } + undobuf.num_undo++; + } +} + +/* Update the records of when each REG was most recently set or killed + for the things done by INSN. This is the last thing done in processing + INSN in the combiner loop. + + We update reg_last_set, reg_last_death, and also the similar information + mem_last_set (which insn most recently modified memory) + and last_call_cuid (which insn was the most recent subroutine call). */ + +static void +record_dead_and_set_regs (insn) + rtx insn; +{ + register rtx link; + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + { + if (REG_NOTE_KIND (link) == REG_DEAD) + reg_last_death[REGNO (XEXP (link, 0))] = insn; + else if (REG_NOTE_KIND (link) == REG_INC) + reg_last_set[REGNO (XEXP (link, 0))] = insn; + } + + if (GET_CODE (insn) == CALL_INSN) + last_call_cuid = mem_last_set = INSN_CUID (insn); + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + register int i; + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + { + register rtx elt = XVECEXP (PATTERN (insn), 0, i); + register enum rtx_code code = GET_CODE (elt); + if (code == SET || code == CLOBBER) + { + rtx dest = XEXP (elt, 0); + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG) + reg_last_set[REGNO (dest)] = insn; + else if (GET_CODE (dest) == MEM) + mem_last_set = INSN_CUID (insn); + } + } + } + else if (GET_CODE (PATTERN (insn)) == SET + || GET_CODE (PATTERN (insn)) == CLOBBER) + { + register rtx dest = XEXP (PATTERN (insn), 0); + + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) + dest = XEXP (dest, 0); + + if (GET_CODE (dest) == REG) + reg_last_set[REGNO (dest)] = insn; + else if (GET_CODE (dest) == MEM) + mem_last_set = INSN_CUID (insn); + } +} + +/* Return nonzero if expression X refers to a REG or to memory + that is set in an instruction more recent than FROM_CUID. */ + +static int +use_crosses_set_p (x, from_cuid) + register rtx x; + int from_cuid; +{ + register char *fmt; + register int i; + register enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + register int regno = REGNO (x); +#ifdef PUSH_ROUNDING + /* Don't allow uses of the stack pointer to be moved, + because we don't know whether the move crosses a push insn. */ + if (regno == STACK_POINTER_REGNUM) + return 1; +#endif + return (reg_last_set[regno] + && INSN_CUID (reg_last_set[regno]) > from_cuid); + } + + if (code == MEM && mem_last_set > from_cuid) + return 1; + + fmt = GET_RTX_FORMAT (code); + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid)) + return 1; + } + else if (fmt[i] == 'e' + && use_crosses_set_p (XEXP (x, i), from_cuid)) + return 1; + } + return 0; +} + +/* Return nonzero if reg REGNO is marked as dying in INSN. */ + +int +regno_dead_p (regno, insn) + int regno; + rtx insn; +{ + register rtx link; + + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if ((REG_NOTE_KIND (link) == REG_DEAD + || REG_NOTE_KIND (link) == REG_INC) + && REGNO (XEXP (link, 0)) == regno) + return 1; + + return 0; +} + +/* Return nonzero if J is the first insn following I, + not counting labels, line numbers, etc. + We assume that J follows I. */ + +static int +adjacent_insns_p (i, j) + rtx i, j; +{ + register rtx insn; + for (insn = NEXT_INSN (i); insn != j; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN + || GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + return 0; + return 1; +} + +/* Check that X is an insn-body for an `asm' with operands + and that the operands mentioned in it are legitimate. */ + +static int +check_asm_operands (x) + rtx x; +{ + int noperands = asm_noperands (x); + rtx *operands; + int i; + + if (noperands < 0) + return 0; + if (noperands == 0) + return 1; + + operands = (rtx *) alloca (noperands * sizeof (rtx)); + decode_asm_operands (x, operands, 0, 0, 0); + + for (i = 0; i < noperands; i++) + if (!general_operand (operands[i], VOIDmode)) + return 0; + + return 1; +} + +/* Concatenate the list of logical links of OINSN + into INSN's list of logical links. + Modifies OINSN destructively. + + If ALL_LINKS is nonzero, move all the links that OINSN has. + Otherwise, move only those that point to insns that set regs + that die in the insn OINSN. + Other links are clobbered so that they are no longer effective. */ + +static void +add_links (insn, oinsn, all_links) + rtx insn, oinsn; + int all_links; +{ + register rtx links = LOG_LINKS (oinsn); + if (! all_links) + { + rtx tail; + for (tail = links; tail; tail = XEXP (tail, 1)) + { + rtx target = XEXP (tail, 0); + if (GET_CODE (target) != INSN + || GET_CODE (PATTERN (target)) != SET + || GET_CODE (SET_DEST (PATTERN (target))) != REG + || ! dead_or_set_p (oinsn, SET_DEST (PATTERN (target)))) + /* OINSN is going to become a NOTE + so a link pointing there will have no effect. */ + XEXP (tail, 0) = oinsn; + } + } + if (LOG_LINKS (insn) == 0) + LOG_LINKS (insn) = links; + else + { + register rtx next, prev = LOG_LINKS (insn); + while (next = XEXP (prev, 1)) + prev = next; + XEXP (prev, 1) = links; + } +} + +/* Delete any LOG_LINKS of INSN which point at OINSN. */ + +static void +remove_links (insn, oinsn) + rtx insn, oinsn; +{ + register rtx next = LOG_LINKS (insn), prev = 0; + while (next) + { + if (XEXP (next, 0) == oinsn) + { + if (prev) + XEXP (prev, 1) = XEXP (next, 1); + else + LOG_LINKS (insn) = XEXP (next, 1); + } + else + prev = next; + next = XEXP (next, 1); + } +} + +/* Concatenate the any elements of the list of reg-notes INCS + which are of type REG_INC + into INSN's list of reg-notes. */ + +static void +add_incs (insn, incs) + rtx insn, incs; +{ + register rtx tail; + + for (tail = incs; tail; tail = XEXP (tail, 1)) + if (REG_NOTE_KIND (tail) == REG_INC) + REG_NOTES (insn) + = gen_rtx (EXPR_LIST, REG_INC, XEXP (tail, 0), REG_NOTES (insn)); +} + +/* Remove register number REGNO from the dead registers list of INSN. */ + +void +remove_death (regno, insn) + int regno; + rtx insn; +{ + register rtx link, next; + while ((link = REG_NOTES (insn)) + && REG_NOTE_KIND (link) == REG_DEAD + && REGNO (XEXP (link, 0)) == regno) + REG_NOTES (insn) = XEXP (link, 1); + + if (link) + while (next = XEXP (link, 1)) + { + if (REG_NOTE_KIND (next) == REG_DEAD + && REGNO (XEXP (next, 0)) == regno) + XEXP (link, 1) = XEXP (next, 1); + else + link = next; + } +} + +/* For each register (hardware or pseudo) used within expression X, + if its death is in an instruction with cuid + between FROM_CUID (inclusive) and TO_INSN (exclusive), + mark it as dead in TO_INSN instead. + + This is done when X is being merged by combination into TO_INSN. */ + +static void +move_deaths (x, from_cuid, to_insn) + rtx x; + int from_cuid; + rtx to_insn; +{ + register char *fmt; + register int len, i; + register enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + register rtx where_dead = reg_last_death[REGNO (x)]; + + if (where_dead && INSN_CUID (where_dead) >= from_cuid + && INSN_CUID (where_dead) < INSN_CUID (to_insn)) + { + remove_death (REGNO (x), reg_last_death[REGNO (x)]); + if (! dead_or_set_p (to_insn, x)) + REG_NOTES (to_insn) + = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); + } + return; + } + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + move_deaths (XVECEXP (x, i, j), from_cuid, to_insn); + } + else if (fmt[i] == 'e') + move_deaths (XEXP (x, i), from_cuid, to_insn); + } +} + +/* Like move_deaths, but deaths are moving both forward + (from FROM_CUID to TO_INSN), and backwards + (from FROM_INSN to TO_INSN). This is what happens + when an insn is removed after applying the distributive law. */ + +static void +move_deaths_2 (x, from_cuid, from_insn, to_insn) + rtx x; + int from_cuid; + rtx from_insn, to_insn; +{ + register char *fmt; + register int len, i; + register enum rtx_code code = GET_CODE (x); + + if (code == REG) + { + register rtx where_dead = reg_last_death[REGNO (x)]; + + if (where_dead && INSN_CUID (where_dead) >= from_cuid + && INSN_CUID (where_dead) < INSN_CUID (to_insn)) + { + remove_death (REGNO (x), reg_last_death[REGNO (x)]); + if (! dead_or_set_p (to_insn, x)) + REG_NOTES (to_insn) + = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); + } + /* Can't use where_dead for from_insn because it has + not been computed yet. */ + else if (dead_or_set_p (from_insn, x)) + { + remove_death (REGNO (x), from_insn); + if (! dead_or_set_p (to_insn, x)) + REG_NOTES (to_insn) + = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (to_insn)); + } + return; + } + + len = GET_RTX_LENGTH (code); + fmt = GET_RTX_FORMAT (code); + + for (i = 0; i < len; i++) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + move_deaths_2 (XVECEXP (x, i, j), from_cuid, from_insn, to_insn); + } + else if (fmt[i] == 'e') + move_deaths_2 (XEXP (x, i), from_cuid, from_insn, to_insn); + } +} + +/* The distrib combiner rewrites groups of insns so that optimizations + can be more easily recognized. The front-end does not know how to + group certain kinds of operations for efficient execution, and the + resulting code can be quite poor. For example, on a machine without + bitfield instructions, bitfield references look like + + (and (lshiftrt ... n) m) + + When combining two bitfield operations, such as with ||, this can + yield code like + + (set z + (or (and (lshiftrt x n) 1) + (and (lshiftrt y n) 1))) + + which can be more efficiently executed as + + (set z + (lshiftrt (and (or x y) + (1 << m)) n)) + + From there, the combiner attempts to rewrite the insns, + keeping flow information accurate for later passes, + and reducing the total number of insns executed. + + This function returns the point at which we should try + looking for more simplifications. This will be before + INSN if the call succeeds. We do not need to fear + infinite loops, since this function is guaranteed to + eliminate at least one (non-note) instruction if it returns + successfully. */ + +static rtx +try_distrib (insn, xprev1, xprev2) + rtx insn, xprev1, xprev2; +{ + rtx pat = PATTERN (insn); + rtx prev1, prev2, pat1, pat2, src1, src2; + rtx to_prev, to_insn; + enum rtx_code code; + int insn_code_number, prev_code_number, regno; + rtx new_insn_pat, new_prev_pat; + + distrib_attempts++; + + /* ??? Need to implement a test that PREV2 and PREV1 + are completely independent. Right now their + recognition ability is sufficiently limited that + it should not be necessary, but better safe than sorry. */ + + /* Let PREV1 be the later of the two insns, and PREV2 the earlier. */ + if (INSN_CUID (xprev1) > INSN_CUID (xprev2)) + { + prev1 = xprev1; + prev2 = xprev2; + } + else + { + prev1 = xprev2; + prev2 = xprev1; + } + + pat1 = PATTERN (prev1); + pat2 = PATTERN (prev2); + + /* First, see if INSN, PREV1, and PREV2 have patterns we can expect + to simplify. */ + + if (GET_CODE (pat) != SET + || GET_CODE (pat1) != SET + || GET_CODE (pat2) != SET) + return 0; + + code = GET_CODE (SET_SRC (pat)); + src1 = SET_SRC (pat1); + src2 = SET_SRC (pat2); + + if (GET_CODE (SET_DEST (pat1)) != REG + || GET_CODE (SET_DEST (pat2)) != REG) + return 0; + + switch (code) + { + default: + return 0; + + case IOR: + case AND: + case XOR: + case PLUS: + ; + } + + /* Insns PREV1 and PREV2 must provide the two operands of the arithmetic + that is done in INSN. */ + if (! ((XEXP (SET_SRC (pat), 0) == SET_DEST (pat1) + && XEXP (SET_SRC (pat), 1) == SET_DEST (pat2)) + || + (XEXP (SET_SRC (pat), 0) == SET_DEST (pat2) + && XEXP (SET_SRC (pat), 1) == SET_DEST (pat1)))) + return 0; + + /* They must not be used in any other way in INSN. + In particular, they must not be used in a result memory address. */ + if (reg_mentioned_p (SET_DEST (pat1), SET_DEST (pat)) + || reg_mentioned_p (SET_DEST (pat2), SET_DEST (pat))) + return 0; + + /* Give up if the two operands' modes don't match. */ + if (GET_MODE (src1) != GET_MODE (src2)) + return 0; + + /* PREV1 and PREV2 must compute the same operation. + Actually, there are other cases that could be handled, + but are not implemented. For example: + + (set (reg:SI 94) + (and:SI (reg:SI 73) + (const_int 223))) + + (set (reg:SI 95) + (zero_extend:SI (subreg:QI (reg:SI 91) 0))) + + (set (reg:SI 96) + (ior:SI (reg:SI 94) + (reg:SI 95))) + + In this case, we know that because (reg:SI 94) has + been anded with 223, there is no need to zero_extend + (reg:SI 91), and we could eliminate (reg:SI 95). */ + + if (GET_CODE (src1) != GET_CODE (src2)) + return 0; + + /* The SETs in PREV1 and PREV2 do not need to be kept around. */ + + undobuf.num_undo = 0; + undobuf.storage = 0; + + /* Substitute in the latest insn for the regs set by the earlier ones. */ + subst_insn = insn; + n_occurrences = 0; /* `subst' counts here */ + + switch (GET_CODE (src1)) + { + /* case XOR: Does not distribute through anything! */ + case LSHIFTRT: + case ASHIFTRT: + /* Right-shift can't distribute through addition + since the round-off would happen differently. */ + case AND: + case IOR: + /* Boolean ops don't distribute through addition. */ + if (code == PLUS) + return 0; + goto do_distrib; + + case LSHIFT: + case ASHIFT: + /* Left shifts are multiplication; they distribute through + addition. Also, since they work bitwise, they + distribute through boolean operations. */ +#ifdef NEGATIVE_SHIFT_COUNTS + /* Negative count is really a right-shift. */ + if (NEGATIVE_SHIFT_COUNTS + && code == PLUS + && !(GET_CODE (XEXP (src1, 1)) + == CONST_INT && INTVAL (XEXP (src1, 1)) >= 0)) + return 0; +#endif + goto do_distrib; + + case MULT: + /* Multiplication distributes through addition only. */ + if (code != PLUS) + return 0; + + do_distrib: + if (GET_CODE (XEXP (src1, 1)) != CONST_INT + || GET_CODE (XEXP (src2, 1)) != CONST_INT + || INTVAL (XEXP (src1, 1)) != INTVAL (XEXP (src2, 1))) + return 0; + + /* Give up if we would move a use of a reg across an alteration. + Note this is unnecessarily conservative, since a problem really + happens only if this reg is set *between* PREV2 and PREV1 + But this test is easier. */ + if (use_crosses_set_p (XEXP (src2, 0), INSN_CUID (prev2))) + return 0; + + /* Try changing (+ (* x c) (* y c)) to (* (+ x y) c). */ + to_prev = gen_rtx (code, GET_MODE (src1), + XEXP (src1, 0), XEXP (src2, 0)); + to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1), SET_DEST (pat1), XEXP (src1, 1)); + break; + + case ZERO_EXTEND: + case SIGN_EXTEND: + /* Extension can't distribute through addition; + the carries could be changed. */ + if (code == PLUS) + return 0; + { + rtx inner1 = XEXP (src1, 0), inner2 = XEXP (src2, 0); + int subreg_needed = 0; + + /* Try changing (& (extend x) (extend y)) to (extend (& x y)). */ + /* But keep extend insns together with their subregs. */ + if (GET_CODE (inner1) == SUBREG) + { + if (SUBREG_WORD (inner1) != 0) + return 0; + else + { + subreg_needed = 1; + inner1 = SUBREG_REG (inner1); + } + } + + if (GET_CODE (inner2) == SUBREG) + { + if (SUBREG_WORD (inner2) != 0) + return 0; + else + { + subreg_needed = 1; + inner2 = SUBREG_REG (inner2); + } + } + + /* Give up if we would move a use of a reg across an alteration. + Note this is unnecessarily conservative, since a problem really + happens only if this reg is set *between* PREV2 and PREV1 + But this test is easier. */ + if (use_crosses_set_p (inner2, INSN_CUID (prev2))) + return 0; + + to_prev = gen_rtx (code, GET_MODE (src1), inner1, inner2); + to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1), + subreg_needed + ? gen_rtx (SUBREG, GET_MODE (XEXP (src1, 0)), + SET_DEST (pat1), 0) + : SET_DEST (pat1)); + } + break; + + default: + return 0; + } + + /* Are the results of this "substitution" a valid instruction? */ + + new_insn_pat = subst (PATTERN (insn), SET_SRC (PATTERN (insn)), to_insn); + distrib_merges_1++; + + insn_code_number = recog (new_insn_pat, insn); + if (insn_code_number < 0) + { + undo_all (); + return 0; + } + + subst_insn = prev1; + new_prev_pat = subst (pat1, src1, to_prev); + distrib_merges_2++; + + prev_code_number = recog (new_prev_pat, prev1); + if (prev_code_number < 0) + { + undo_all (); + return 0; + } + + /* Everything worked; install the new patterns. */ + INSN_CODE (insn) = insn_code_number; + PATTERN (insn) = new_insn_pat; + + INSN_CODE (prev1) = prev_code_number; + PATTERN (prev1) = new_prev_pat; + + /* Need to change LOG_LINKS around...PREV1 now gets + whatever flowed into PREV2. PREV2 is going to + become a NOTE, so we clear out its LOG_LINKS. */ + remove_links (insn, prev2); + add_links (prev1, prev2, adjacent_insns_p (prev2, prev1)); + + /* Registers which died in PREV2 now die in PREV1. + Also, registers born in PREV2 dying in INSN now die in PREV1. */ + move_deaths_2 (src2, INSN_CUID (prev2), insn, prev1); + + regno = REGNO (SET_DEST (pat2)); + + reg_n_sets[regno]--; + if (reg_n_sets[regno] == 0 + && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT] + & (1 << (regno % HOST_BITS_PER_INT)))) + reg_n_refs[regno] = 0; + remove_death (regno, insn); + + PUT_CODE (prev2, NOTE); + NOTE_LINE_NUMBER (prev2) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (prev2) = 0; + + distrib_successes++; + return prev1; +} + +void +dump_combine_stats (file) + FILE *file; +{ + fprintf + (file, + ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n", + combine_attempts, combine_merges, combine_extras, combine_successes); + fprintf + (file, + ";; Distributer statistics: %d attempts, %d:%d substitutions,\n;; %d successes.\n\n", + distrib_attempts, distrib_merges_1, + distrib_merges_2, distrib_successes); +} + +void +dump_combine_total_stats (file) + FILE *file; +{ + fprintf + (file, + "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n", + total_attempts, total_merges, total_extras, total_successes); + fprintf + (file, + "\n;; Distributer totals: %d attempts, %d:%d substitutions,\n;; %d successes.\n", + total_distrib_attempts, total_distrib_merges_1, + total_distrib_merges_2, total_distrib_successes); +} |
