diff options
| author | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:34:05 -0400 |
|---|---|---|
| committer | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:34:05 -0400 |
| commit | 60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f (patch) | |
| tree | fdc0be85a1ca35e34c3ae2c805fe9b718e3c1091 /gcc-1.40/cse.c | |
| parent | dd8dfab51b832a654365ed00c06bf802ff628bfa (diff) | |
| download | linux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.tar.gz linux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.tar.bz2 linux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.zip | |
Diffstat (limited to 'gcc-1.40/cse.c')
| -rw-r--r-- | gcc-1.40/cse.c | 3936 |
1 files changed, 3936 insertions, 0 deletions
diff --git a/gcc-1.40/cse.c b/gcc-1.40/cse.c new file mode 100644 index 0000000..0807d2b --- /dev/null +++ b/gcc-1.40/cse.c @@ -0,0 +1,3936 @@ +/* Common subexpression elimination for GNU compiler. + Copyright (C) 1987, 1988, 1989 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. */ + + +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "real.h" + +#include <setjmp.h> + +/* The basic idea of common subexpression elimination is to go + through the code, keeping a record of expressions that would + have the same value at the current scan point, and replacing + expressions encountered with the cheapest equivalent expression. + + It is too complicated to keep track of the different possibilities + when control paths merge; so, at each label, we forget all that is + known and start fresh. This can be described as processing each + basic block separately. Note, however, that these are not quite + the same as the basic blocks found by a later pass and used for + data flow analysis and register packing. We do not need to start fresh + after a conditional jump instruction if there is no label there. + + We use two data structures to record the equivalent expressions: + a hash table for most expressions, and several vectors together + with "quantity numbers" to record equivalent (pseudo) registers. + + The use of the special data structure for registers is desirable + because it is faster. It is possible because registers references + contain a fairly small number, the register number, taken from + a contiguously allocated series, and two register references are + identical if they have the same number. General expressions + do not have any such thing, so the only way to retrieve the + information recorded on an expression other than a register + is to keep it in a hash table. + +Registers and "quantity numbers": + + At the start of each basic block, all of the (hardware and pseudo) + registers used in the function are given distinct quantity + numbers to indicate their contents. During scan, when the code + copies one register into another, we copy the quantity number. + When a register is loaded in any other way, we allocate a new + quantity number to describe the value generated by this operation. + `reg_qty' records what quantity a register is currently thought + of as containing. + + We also maintain a bidirectional chain of registers for each + quantity number. `qty_first_reg', `qty_last_reg', + `reg_next_eqv' and `reg_prev_eqv' hold these chains. + + The first register in a chain is the one whose lifespan is least local. + Among equals, it is the one that was seen first. + We replace any equivalent register with that one. + +Constants and quantity numbers + + When a quantity has a known constant value, that value is stored + in the appropriate element of qty_const. This is in addition to + putting the constant in the hash table as is usual for non-regs. + + Regs are preferred to constants as they are to everything else, + but expressions containing constants can be simplified, by fold_rtx. + + When a quantity has a known nearly constant value (such as an address + of a stack slot), that value is stored in the appropriate element + of qty_const. + + Integer constants don't have a machine mode. However, cse + determines the intended machine mode from the destination + of the instruction that moves the constant. The machine mode + is recorded in the hash table along with the actual RTL + constant expression so that different modes are kept separate. + +Other expressions: + + To record known equivalences among expressions in general + we use a hash table called `table'. It has a fixed number of buckets + that contain chains of `struct table_elt' elements for expressions. + These chains connect the elements whose expressions have the same + hash codes. + + Other chains through the same elements connect the elements which + currently have equivalent values. + + Register references in an expression are canonicalized before hashing + the expression. This is done using `reg_qty' and `qty_first_reg'. + The hash code of a register reference is computed using the quantity + number, not the register number. + + When the value of an expression changes, it is necessary to remove from the + hash table not just that expression but all expressions whose values + could be different as a result. + + 1. If the value changing is in memory, except in special cases + ANYTHING referring to memory could be changed. That is because + nobody knows where a pointer does not point. + The function `invalidate_memory' removes what is necessary. + + The special cases are when the address is constant or is + a constant plus a fixed register such as the frame pointer + or a static chain pointer. When such addresses are stored in, + we can tell exactly which other such addresses must be invalidated + due to overlap. `invalidate' does this. + All expressions that refer to non-constant + memory addresses are also invalidated. `invalidate_memory' does this. + + 2. If the value changing is a register, all expressions + containing references to that register, and only those, + must be removed. + + Because searching the entire hash table for expressions that contain + a register is very slow, we try to figure out when it isn't necessary. + Precisely, this is necessary only when expressions have been + entered in the hash table using this register, and then the value has + changed, and then another expression wants to be added to refer to + the register's new value. This sequence of circumstances is rare + within any one basic block. + + The vectors `reg_tick' and `reg_in_table' are used to detect this case. + reg_tick[i] is incremented whenever a value is stored in register i. + reg_in_table[i] holds -1 if no references to register i have been + entered in the table; otherwise, it contains the value reg_tick[i] had + when the references were entered. If we want to enter a reference + and reg_in_table[i] != reg_tick[i], we must scan and remove old references. + Until we want to enter a new entry, the mere fact that the two vectors + don't match makes the entries be ignored if anyone tries to match them. + + Registers themselves are entered in the hash table as well as in + the equivalent-register chains. However, the vectors `reg_tick' + and `reg_in_table' do not apply to expressions which are simple + register references. These expressions are removed from the table + immediately when they become invalid, and this can be done even if + we do not immediately search for all the expressions that refer to + the register. + + A CLOBBER rtx in an instruction invalidates its operand for further + reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK + invalidates everything that resides in memory. + +Related expressions: + + Constant expressions that differ only by an additive integer + are called related. When a constant expression is put in + the table, the related expression with no constant term + is also entered. These are made to point at each other + so that it is possible to find out if there exists any + register equivalent to an expression related to a given expression. */ + +/* One plus largest register number used in this function. */ + +static int max_reg; + +/* Length of vectors indexed by quantity number. + We know in advance we will not need a quantity number this big. */ + +static int max_qty; + +/* Next quantity number to be allocated. + This is 1 + the largest number needed so far. */ + +static int next_qty; + +/* Indexed by quantity number, gives the first (or last) (pseudo) register + in the chain of registers that currently contain this quantity. */ + +static int *qty_first_reg; +static int *qty_last_reg; + +/* Indexed by quantity number, gives the rtx of the constant value of the + quantity, or zero if it does not have a known value. + A sum of the frame pointer (or arg pointer) plus a constant + can also be entered here. */ + +static rtx *qty_const; + +/* Indexed by qty number, gives the insn that stored the constant value + recorded in `qty_const'. */ + +static rtx *qty_const_insn; + +/* Value stored in CC0 by previous insn: + 0 if previous insn didn't store in CC0. + else 0100 + (M&7)<<3 + (N&7) + where M is 1, 0 or -1 if result was >, == or < as signed number + and N is 1, 0 or -1 if result was >, == or < as unsigned number. + 0200 bit may also be set, meaning that only == and != comparisons + have known results. */ + +static int prev_insn_cc0; + +/* For machines where CC0 is one bit, we may see CC0 assigned a + constant value (after fold_rtx). + Record here the value stored in the previous insn (0 if none). */ + +static rtx prev_insn_explicit_cc0; + +/* Previous actual insn. 0 if at first insn of basic block. */ + +static rtx prev_insn; + +/* Insn being scanned. */ + +static rtx this_insn; + +/* Index by (pseudo) register number, gives the quantity number + of the register's current contents. */ + +static int *reg_qty; + +/* Index by (pseudo) register number, gives the number of the next + (pseudo) register in the chain of registers sharing the same value. + Or -1 if this register is at the end of the chain. */ + +static int *reg_next_eqv; + +/* Index by (pseudo) register number, gives the number of the previous + (pseudo) register in the chain of registers sharing the same value. + Or -1 if this register is at the beginning of the chain. */ + +static int *reg_prev_eqv; + +/* Index by (pseudo) register number, gives the latest rtx + to use to insert a ref to that register. */ + +static rtx *reg_rtx; + +/* Index by (pseudo) register number, gives the number of times + that register has been altered in the current basic block. */ + +static int *reg_tick; + +/* Index by (pseudo) register number, gives the reg_tick value at which + rtx's containing this register are valid in the hash table. + If this does not equal the current reg_tick value, such expressions + existing in the hash table are invalid. + If this is -1, no expressions containing this register have been + entered in the table. */ + +static int *reg_in_table; + +/* Two vectors of max_reg ints: + one containing all -1's; in the other, element i contains i. + These are used to initialize various other vectors fast. */ + +static int *all_minus_one; +static int *consec_ints; + +/* Set nonzero in cse_insn to tell cse_basic_block to skip immediately + to the next basic block and treat it as a continuation of this one. */ + +static int cse_skip_to_next_block; + +/* CUID of insn that starts the basic block currently being cse-processed. */ + +static int cse_basic_block_start; + +/* CUID of insn that ends the basic block currently being cse-processed. */ + +static int cse_basic_block_end; + +/* Vector mapping INSN_UIDs to cuids. + The cuids are like uids but increase monononically always. + We use them to see whether a reg is used outside a given basic block. */ + +static short *uid_cuid; + +/* Get the cuid of an insn. */ + +#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) + +/* Nonzero if cse has altered conditional jump insns + in such a way that jump optimization should be redone. */ + +static int cse_jumps_altered; + +/* canon_hash stores 1 in do_not_record + if it notices a reference to CC0, CC1 or PC. */ + +static int do_not_record; + +/* canon_hash stores 1 in hash_arg_in_memory + if it notices a reference to memory within the expression being hashed. */ + +static int hash_arg_in_memory; + +/* canon_hash stores 1 in hash_arg_in_struct + if it notices a reference to memory that's part of a structure. */ + +static int hash_arg_in_struct; + +/* The hash table contains buckets which are chains of `struct table_elt's, + each recording one expression's information. + That expression is in the `exp' field. + + Those elements with the same hash code are chained in both directions + through the `next_same_hash' and `prev_same_hash' fields. + + Each set of expressions with equivalent values + are on a two-way chain through the `next_same_value' + and `prev_same_value' fields, and all point with + the `first_same_value' field at the first element in + that chain. The chain is in order of increasing cost. + Each element's cost value is in its `cost' field. + + The `in_memory' field is nonzero for elements that + involve any reference to memory. These elements are removed + whenever a write is done to an unidentified location in memory. + To be safe, we assume that a memory address is unidentified unless + the address is either a symbol constant or a constant plus + the frame pointer or argument pointer. + + The `in_struct' field is nonzero for elements that + involve any reference to memory inside a structure or array. + + The `equivalence_only' field means that this expression came from a + REG_EQUIV or REG_EQUAL note; it is not valid for substitution into an insn. + + The `related_value' field is used to connect related expressions + (that differ by adding an integer). + The related expressions are chained in a circular fashion. + `related_value' is zero for expressions for which this + chain is not useful. + + The `mode' field is usually the same as GET_MODE (`exp'), but + if `exp' is a CONST_INT and has no machine mode then the `mode' + field is the mode it was being used as. Each constant is + recorded separately for each mode it is used with. */ + + +struct table_elt +{ + rtx exp; + struct table_elt *next_same_hash; + struct table_elt *prev_same_hash; + struct table_elt *next_same_value; + struct table_elt *prev_same_value; + struct table_elt *first_same_value; + struct table_elt *related_value; + int cost; + enum machine_mode mode; + char in_memory; + char in_struct; + char equivalence_only; +}; + +#define HASH(x, m) (canon_hash (x, m) % NBUCKETS) +/* We don't want a lot of buckets, because we rarely have very many + things stored in the hash table, and a lot of buckets slows + down a lot of loops that happen frequently. */ +#define NBUCKETS 31 + +static struct table_elt *table[NBUCKETS]; + +/* Chain of `struct table_elt's made so far for this function + but currently removed from the table. */ + +static struct table_elt *free_element_chain; + +/* Number of `struct table_elt' structures made so far for this function. */ + +static int n_elements_made; + +/* Maximum value `n_elements_made' has had so far in this compilation + for functions previously processed. */ + +static int max_elements_made; + +/* Bits describing what kind of values in memory must be invalidated + for a particular instruction. If all three bits are zero, + no memory refs need to be invalidated. Each bit is more powerful + than the preceding ones, and if a bit is set then the preceding + bits are also set. + + Here is how the bits are set. + Writing at a fixed address invalidates only variable addresses, + writing in a structure element at variable address + invalidates all but scalar variables, + and writing in anything else at variable address invalidates everything. */ + +struct write_data +{ + int var : 1; /* Invalidate variable addresses. */ + int nonscalar : 1; /* Invalidate all but scalar variables. */ + int all : 1; /* Invalidate all memory refs. */ +}; + +/* Nonzero if X has the form (PLUS frame-pointer integer). */ + +#define FIXED_BASE_PLUS_P(X) \ + (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (XEXP (X, 0) == frame_pointer_rtx || XEXP (X, 0) == arg_pointer_rtx)) + +static struct table_elt *lookup (); +static void free_element (); + +static void remove_invalid_refs (); +static int exp_equiv_p (); +int refers_to_p (); +int refers_to_mem_p (); +static void invalidate_from_clobbers (); +static int safe_hash (); +static int canon_hash (); +static rtx equiv_constant (); +static int get_integer_term (); +static rtx get_related_value (); +static void note_mem_written (); +static int cse_rtx_addr_varies_p (); +static int fold_cc0 (); + +/* Return an estimate of the cost of computing rtx X. + The only use of this is to compare the costs of two expressions + to decide whether to replace one with the other. */ + +static int +rtx_cost (x) + rtx x; +{ + register int i, j; + register enum rtx_code code; + register char *fmt; + register int total; + + if (x == 0) + return 0; + + code = GET_CODE (x); + switch (code) + { + case REG: + return 1; + case SUBREG: + return 2; + CONST_COSTS (x, code); + } + + total = 2; + if (code == MEM) + total = 2 * GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD; + + /* Sum the costs of the sub-rtx's, plus 2 just put in. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + total += rtx_cost (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + total += rtx_cost (XVECEXP (x, i, j)); + + return total; +} + +/* Clear the hash table and initialize each register with its own quantity, + for a new basic block. */ + +static void +new_basic_block () +{ + register int i; + register int vecsize = max_reg * sizeof (rtx); + next_qty = max_reg; + + bzero (reg_rtx, vecsize); + bzero (reg_tick, vecsize); + + bcopy (all_minus_one, reg_in_table, vecsize); + bcopy (all_minus_one, reg_next_eqv, vecsize); + bcopy (all_minus_one, reg_prev_eqv, vecsize); + bcopy (consec_ints, reg_qty, vecsize); + + for (i = 0; i < max_qty; i++) + { + qty_first_reg[i] = i; + qty_last_reg[i] = i; + qty_const[i] = 0; + qty_const_insn[i] = 0; + } + + for (i = 0; i < NBUCKETS; i++) + { + register struct table_elt *this, *next; + for (this = table[i]; this; this = next) + { + next = this->next_same_hash; + free_element (this); + } + } + + bzero (table, sizeof table); + + prev_insn_cc0 = 0; + prev_insn_explicit_cc0 = 0; + prev_insn = 0; +} + +/* Say that register REG contains a quantity not in any register before. */ + +static void +make_new_qty (reg) + register int reg; +{ + register int q; + + q = reg_qty[reg] = next_qty++; + qty_first_reg[q] = reg; + qty_last_reg[q] = reg; +} + +/* Make reg NEW equivalent to reg OLD. + OLD is not changing; NEW is. */ + +static void +make_regs_eqv (new, old) + register int new, old; +{ + register int lastr, firstr; + register int q = reg_qty[old]; + + /* Nothing should become eqv until it has a "non-invalid" qty number. */ + if (q == old) + abort (); + + reg_qty[new] = q; + firstr = qty_first_reg[q]; + lastr = qty_last_reg[q]; + + /* Prefer pseudo regs to hard regs with the same value. + Among pseudos, if NEW will live longer than any other reg of the same qty, + and that is beyond the current basic block, + make it the new canonical replacement for this qty. */ + if (new >= FIRST_PSEUDO_REGISTER + && (firstr < FIRST_PSEUDO_REGISTER + || ((uid_cuid[regno_last_uid[new]] > cse_basic_block_end + || uid_cuid[regno_first_uid[new]] < cse_basic_block_start) + && (uid_cuid[regno_last_uid[new]] + > uid_cuid[regno_last_uid[firstr]])))) + { + reg_prev_eqv[firstr] = new; + reg_next_eqv[new] = firstr; + reg_prev_eqv[new] = -1; + qty_first_reg[q] = new; + } + else + { + /* If NEW is a hard reg, insert at end. + Otherwise, insert before any hard regs that are at the end. */ + while (lastr < FIRST_PSEUDO_REGISTER && new >= FIRST_PSEUDO_REGISTER) + lastr = reg_prev_eqv[lastr]; + reg_next_eqv[new] = reg_next_eqv[lastr]; + if (reg_next_eqv[lastr] >= 0) + reg_prev_eqv[reg_next_eqv[lastr]] = new; + else + qty_last_reg[q] = new; + reg_next_eqv[lastr] = new; + reg_prev_eqv[new] = lastr; + } +} + +/* Discard the records of what is in register REG. */ + +static void +reg_invalidate (reg) + register int reg; +{ + register int n = reg_next_eqv[reg]; + register int p = reg_prev_eqv[reg]; + register int q = reg_qty[reg]; + + reg_tick[reg]++; + + if (q == reg) + { + /* Save time if already invalid */ + /* It shouldn't be linked to anything if it's invalid. */ + if (reg_prev_eqv[q] != -1) + abort (); + if (reg_next_eqv[q] != -1) + abort (); + return; + } + + if (n != -1) + reg_prev_eqv[n] = p; + else + qty_last_reg[q] = p; + if (p != -1) + reg_next_eqv[p] = n; + else + qty_first_reg[q] = n; + + reg_qty[reg] = reg; + qty_first_reg[reg] = reg; + qty_last_reg[reg] = reg; + reg_next_eqv[reg] = -1; + reg_prev_eqv[reg] = -1; +} + +/* Remove any invalid expressions from the hash table + that refer to any of the registers contained in expression X. + + Make sure that newly inserted references to those registers + as subexpressions will be considered valid. + + mention_regs is not called when a register itself + is being stored in the table. */ + +static void +mention_regs (x) + rtx x; +{ + register enum rtx_code code; + register int i, j; + register char *fmt; + + if (x == 0) + return; + + code = GET_CODE (x); + if (code == REG) + { + register int regno = REGNO (x); + reg_rtx[regno] = x; + + if (reg_in_table[regno] >= 0 && reg_in_table[regno] != reg_tick[regno]) + remove_invalid_refs (regno); + + reg_in_table[regno] = reg_tick[regno]; + + return; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + mention_regs (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + mention_regs (XVECEXP (x, i, j)); +} + +/* Update the register quantities for inserting X into the hash table + with a value equivalent to CLASSP. + (If CLASSP is not a REG or a SUBREG, it is irrelevant.) + If MODIFIED is nonzero, X is a destination; it is being modified. + Note that reg_invalidate should be called on a register + before insert_regs is done on that register with MODIFIED != 0. + + Nonzero value means that elements of reg_qty have changed + so X's hash code may be different. */ + +static int +insert_regs (x, classp, modified) + rtx x; + struct table_elt *classp; + int modified; +{ + if (GET_CODE (x) == REG) + { + register int regno = REGNO (x); + reg_rtx[regno] = x; + if (modified || reg_qty[regno] == regno) + { + if (classp && GET_CODE (classp->exp) == REG) + { + make_regs_eqv (regno, REGNO (classp->exp)); + /* Make sure reg_rtx is set up even for regs + not explicitly set (such as function value). */ + reg_rtx[REGNO (classp->exp)] = classp->exp; + } + else + make_new_qty (regno); + return 1; + } + } + /* Copying a subreg into a subreg makes the regs equivalent, + but only if the entire regs' mode is within one word. + Copying one reg of a DImode into one reg of another DImode + does not make them equivalent. */ + else if (GET_CODE (x) == SUBREG + && GET_CODE (SUBREG_REG (x)) == REG + && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) <= UNITS_PER_WORD + && (modified + || reg_qty[REGNO (SUBREG_REG (x))] == REGNO (SUBREG_REG (x)))) + { + if (classp && GET_CODE (classp->exp) == SUBREG + && GET_CODE (SUBREG_REG (classp->exp)) == REG + && GET_MODE (SUBREG_REG (classp->exp)) == GET_MODE (SUBREG_REG (x))) + { + int oregno = REGNO (SUBREG_REG (classp->exp)); + make_regs_eqv (REGNO (SUBREG_REG (x)), oregno); + /* Make sure reg_rtx is set up even for regs + not explicitly set (such as function value). */ + reg_rtx[oregno] = SUBREG_REG (classp->exp); + } + else + make_new_qty (REGNO (SUBREG_REG (x))); + return 1; + } + else + mention_regs (x); + return 0; +} + +/* Look in or update the hash table. */ + +/* Put the element ELT on the list of free elements. */ + +static void +free_element (elt) + struct table_elt *elt; +{ + elt->next_same_hash = free_element_chain; + free_element_chain = elt; +} + +/* Return an element that is free for use. */ + +static struct table_elt * +get_element () +{ + struct table_elt *elt = free_element_chain; + if (elt) + { + free_element_chain = elt->next_same_hash; + return elt; + } + n_elements_made++; + return (struct table_elt *) oballoc (sizeof (struct table_elt)); +} + +/* Remove table element ELT from use in the table. + HASH is its hash code, made using the HASH macro. + It's an argument because often that is known in advance + and we save much time not recomputing it. */ + +static void +remove (elt, hash) + register struct table_elt *elt; + int hash; +{ + if (elt == 0) + return; + + /* Mark this element as removed. See cse_insn. */ + elt->first_same_value = 0; + + /* Remove the table element from its equivalence class. */ + + { + register struct table_elt *prev = elt->prev_same_value; + register struct table_elt *next = elt->next_same_value; + + if (next) next->prev_same_value = prev; + + if (prev) + prev->next_same_value = next; + else + { + register struct table_elt *newfirst = next; + while (next) + { + next->first_same_value = newfirst; + next = next->next_same_value; + } + } + } + + /* Remove the table element from its hash bucket. */ + + { + register struct table_elt *prev = elt->prev_same_hash; + register struct table_elt *next = elt->next_same_hash; + + if (next) next->prev_same_hash = prev; + + if (prev) + prev->next_same_hash = next; + else + table[hash] = next; + } + + /* Remove the table element from its related-value circular chain. */ + + if (elt->related_value != 0 && elt->related_value != elt) + { + register struct table_elt *p = elt->related_value; + while (p->related_value != elt) + p = p->related_value; + p->related_value = elt->related_value; + if (p->related_value == p) + p->related_value = 0; + } + + free_element (elt); +} + +/* Look up X in the hash table and return its table element, + or 0 if X is not in the table. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + Here we are satisfied to find an expression whose tree structure + looks like X. */ + +static struct table_elt * +lookup (x, hash, mode) + rtx x; + int hash; + enum machine_mode mode; +{ + register struct table_elt *p; + + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 1))) + return p; + + return 0; +} + +/* Like `lookup' but don't care whether the table element uses invalid regs. + Also ignore discrepancies in the machine mode of a register. */ + +static struct table_elt * +lookup_for_remove (x, hash, mode) + rtx x; + int hash; + enum machine_mode mode; +{ + register struct table_elt *p; + + if (GET_CODE (x) == REG) + { + int regno = REGNO (x); + /* Don't check the machine mode when comparing registers; + invalidating (REG:SI 0) also invalidates (REG:DF 0). */ + for (p = table[hash]; p; p = p->next_same_hash) + if (GET_CODE (p->exp) == REG + && REGNO (p->exp) == regno) + return p; + } + else + { + for (p = table[hash]; p; p = p->next_same_hash) + if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0))) + return p; + } + + return 0; +} + +/* Look for an expression equivalent to X and with code CODE. + If one is found, return that expression. */ + +static rtx +lookup_as_function (x, code) + rtx x; + enum rtx_code code; +{ + register struct table_elt *p = lookup (x, safe_hash (x, 0) % NBUCKETS, + GET_MODE (x)); + if (p == 0) + return 0; + + for (p = p->first_same_value; p; p = p->next_same_value) + { + if (GET_CODE (p->exp) == code + /* Make sure this is a valid entry in the table. */ + && (exp_equiv_p (XEXP (p->exp, 0), XEXP (p->exp, 0), 1))) + return p->exp; + } + + return 0; +} + +/* Insert X in the hash table, assuming HASH is its hash code + and CLASSP is the current first element of the class it should go in + (or 0 if a new class should be made). + It is inserted at the proper position to keep the class in + the order cheapest first. + + MODE is the machine-mode of X, or if X is an integer constant + with VOIDmode then MODE is the mode with which X will be used. + + For elements of equal cheapness, the most recent one + goes in front, except that the first element in the list + remains first unless a cheaper element is added. + + The in_memory field in the hash table element is set to 0. + The caller must set it nonzero if appropriate. + + You should call insert_regs (X, CLASSP, MODIFY) before calling here, + and if insert_regs returns a nonzero value + you must then recompute its hash code before calling here. + + If necessary, update table showing constant values of quantities. */ + +#define CHEAPER(X,Y) \ + (((X)->cost < (Y)->cost) || \ + ((X)->cost == (Y)->cost \ + && GET_CODE ((X)->exp) == REG && GET_CODE ((Y)->exp) == REG \ + && (uid_cuid[regno_last_uid[REGNO ((X)->exp)]] > cse_basic_block_end \ + || uid_cuid[regno_first_uid[REGNO ((X)->exp)]] < cse_basic_block_start) \ + && (uid_cuid[regno_last_uid[REGNO ((X)->exp)]] \ + > uid_cuid[regno_last_uid[REGNO ((Y)->exp)]]))) + +static struct table_elt * +insert (x, classp, hash, mode) + register rtx x; + register struct table_elt *classp; + int hash; + enum machine_mode mode; +{ + register struct table_elt *elt; + + /* Put an element for X into the right hash bucket. */ + + elt = get_element (); + elt->exp = x; + elt->cost = rtx_cost (x) * 2; + /* Make pseudo regs a little cheaper than hard regs. */ + if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) + elt->cost -= 1; + elt->next_same_value = 0; + elt->prev_same_value = 0; + elt->next_same_hash = table[hash]; + elt->prev_same_hash = 0; + elt->related_value = 0; + elt->in_memory = 0; + elt->equivalence_only = 0; + elt->mode = mode; + if (table[hash]) + table[hash]->prev_same_hash = elt; + table[hash] = elt; + + /* Put it into the proper value-class. */ + if (classp) + { + if (CHEAPER (elt, classp)) + /** Insert at the head of the class */ + { + register struct table_elt *p; + elt->next_same_value = classp; + classp->prev_same_value = elt; + elt->first_same_value = elt; + + for (p = classp; p; p = p->next_same_value) + p->first_same_value = elt; + } + else + { + /* Insert not at head of the class. */ + /* Put it after the last element cheaper than X. */ + register struct table_elt *p, *next; + for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt); + p = next); + /* Put it after P and before NEXT. */ + elt->next_same_value = next; + if (next) + next->prev_same_value = elt; + elt->prev_same_value = p; + p->next_same_value = elt; + elt->first_same_value = classp; + } + } + else + elt->first_same_value = elt; + + if ((CONSTANT_P (x) || GET_CODE (x) == CONST_DOUBLE || FIXED_BASE_PLUS_P (x)) + && GET_CODE (elt->first_same_value->exp) == REG) + { + qty_const[reg_qty[REGNO (elt->first_same_value->exp)]] = x; + qty_const_insn[reg_qty[REGNO (elt->first_same_value->exp)]] = this_insn; + } + + if (GET_CODE (x) == REG) + { + if (elt->next_same_value != 0 + && (CONSTANT_P (elt->next_same_value->exp) + || GET_CODE (elt->next_same_value->exp) == CONST_DOUBLE + || FIXED_BASE_PLUS_P (elt->next_same_value->exp))) + { + qty_const[reg_qty[REGNO (x)]] = elt->next_same_value->exp; + qty_const_insn[reg_qty[REGNO (x)]] = this_insn; + } + if (CONSTANT_P (elt->first_same_value->exp) + || GET_CODE (elt->first_same_value->exp) == CONST_DOUBLE + || FIXED_BASE_PLUS_P (elt->first_same_value->exp)) + { + qty_const[reg_qty[REGNO (x)]] = elt->first_same_value->exp; + qty_const_insn[reg_qty[REGNO (x)]] = this_insn; + } + } + + /* If this is a constant with symbolic value, + and it has a term with an explicit integer value, + link it up with related expressions. */ + if (GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + int subhash; + struct table_elt *subelt, *subelt_prev; + + if (subexp != 0) + { + /* Get the integer-free subexpression in the hash table. */ + subhash = safe_hash (subexp, mode) % NBUCKETS; + subelt = lookup (subexp, subhash, mode); + if (subelt == 0) + subelt = insert (subexp, 0, subhash, mode); + /* Initialize SUBELT's circular chain if it has none. */ + if (subelt->related_value == 0) + subelt->related_value = subelt; + /* Find the element in the circular chain that precedes SUBELT. */ + subelt_prev = subelt; + while (subelt_prev->related_value != subelt) + subelt_prev = subelt_prev->related_value; + /* Put new ELT into SUBELT's circular chain just before SUBELT. + This way the element that follows SUBELT is the oldest one. */ + elt->related_value = subelt_prev->related_value; + subelt_prev->related_value = elt; + } + } + + return elt; +} + +/* Remove from the hash table, or mark as invalid, + all expressions whose values could be altered by storing in X. + X is a register, a subreg, or a memory reference with nonvarying address + (because, when a memory reference with a varying address is stored in, + all memory references are removed by invalidate_memory + so specific invalidation is superfluous). + + A nonvarying address may be just a register or just + a symbol reference, or it may be either of those plus + a numeric offset. */ + +static void +invalidate (x) + rtx x; +{ + register int i; + register struct table_elt *p; + register rtx base; + register int start, end; + + /* If X is a register, dependencies on its contents + are recorded through the qty number mechanism. + Just change the qty number of the register, + mark it as invalid for expressions that refer to it, + and remove it itself. */ + + if (GET_CODE (x) == REG) + { + register int hash = HASH (x, 0); + reg_invalidate (REGNO (x)); + remove (lookup_for_remove (x, hash, GET_MODE (x)), hash); + return; + } + + if (GET_CODE (x) == SUBREG) + { + if (GET_CODE (SUBREG_REG (x)) != REG) + abort (); + invalidate (SUBREG_REG (x)); + return; + } + + /* X is not a register; it must be a memory reference with + a nonvarying address. Remove all hash table elements + that refer to overlapping pieces of memory. */ + + if (GET_CODE (x) != MEM) + abort (); + base = XEXP (x, 0); + start = 0; + + /* Registers with nonvarying addresses usually have constant equivalents; + but the frame pointer register is also possible. */ + if (GET_CODE (base) == REG + && qty_const[reg_qty[REGNO (base)]] != 0) + base = qty_const[reg_qty[REGNO (base)]]; + + if (GET_CODE (base) == CONST) + base = XEXP (base, 0); + if (GET_CODE (base) == PLUS + && GET_CODE (XEXP (base, 1)) == CONST_INT) + { + start = INTVAL (XEXP (base, 1)); + base = XEXP (base, 0); + } + + end = start + GET_MODE_SIZE (GET_MODE (x)); + for (i = 0; i < NBUCKETS; i++) + { + register struct table_elt *next; + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (refers_to_mem_p (p->exp, base, start, end)) + remove (p, i); + } + } +} + +/* Remove all expressions that refer to register REGNO, + since they are already invalid, and we are about to + mark that register valid again and don't want the old + expressions to reappear as valid. */ + +static void +remove_invalid_refs (regno) + int regno; +{ + register int i; + register struct table_elt *p, *next; + register rtx x = reg_rtx[regno]; + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (GET_CODE (p->exp) != REG && refers_to_p (p->exp, x)) + remove (p, i); + } +} + +/* Remove from the hash table all expressions that reference memory, + or some of them as specified by *WRITES. */ + +static void +invalidate_memory (writes) + struct write_data *writes; +{ + register int i; + register struct table_elt *p, *next; + int all = writes->all; + int nonscalar = writes->nonscalar; + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + next = p->next_same_hash; + if (p->in_memory + && (all + || (nonscalar && p->in_struct) + || cse_rtx_addr_varies_p (p->exp))) + remove (p, i); + } +} + +/* Return the value of the integer term in X, if one is apparent; + otherwise return 0. + We do not check extremely carefully for the presence of integer terms + but rather consider only the cases that `insert' notices + for the `related_value' field. */ + +static int +get_integer_term (x) + rtx x; +{ + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + if (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 1)) == CONST_INT) + return - INTVAL (XEXP (x, 1)); + if (GET_CODE (x) != PLUS) + return 0; + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + return INTVAL (XEXP (x, 0)); + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + return INTVAL (XEXP (x, 1)); + return 0; +} + +static rtx +get_related_value (x) + rtx x; +{ + if (GET_CODE (x) != CONST) + return 0; + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS) + { + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + return XEXP (x, 1); + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + return XEXP (x, 0); + } + else if (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 1)) == CONST_INT) + return XEXP (x, 0); + return 0; +} + +/* Given an expression X of type CONST, + and ELT which is its table entry (or 0 if it + is not in the hash table), + return an alternate expression for X as a register plus integer. + If none can be found or it would not be a valid address, return 0. */ + +static rtx +use_related_value (x, elt) + rtx x; + struct table_elt *elt; +{ + register struct table_elt *relt = 0; + register struct table_elt *p; + int offset; + rtx addr; + + /* First, is there anything related known? + If we have a table element, we can tell from that. + Otherwise, must look it up. */ + + if (elt != 0 && elt->related_value != 0) + relt = elt; + else if (elt == 0 && GET_CODE (x) == CONST) + { + rtx subexp = get_related_value (x); + if (subexp != 0) + relt = lookup (subexp, + safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS, + GET_MODE (subexp)); + } + + if (relt == 0) + return 0; + + /* Search all related table entries for one that has an + equivalent register. */ + + p = relt; + while (1) + { + if (p->first_same_value != 0 + && GET_CODE (p->first_same_value->exp) == REG) + break; + p = p->related_value; + + /* We went all the way around, so there is nothing to be found. + Return failure. */ + if (p == relt) + return 0; + /* Perhaps RELT was in the table for some other reason and + it has no related values recorded. */ + if (p == 0) + return 0; + } + + /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */ + offset = (get_integer_term (x) - get_integer_term (p->exp)); + addr = plus_constant (p->first_same_value->exp, offset); + if (memory_address_p (QImode, addr)) + return addr; + return 0; +} + +/* Hash an rtx. We are careful to make sure the value is never negative. + Equivalent registers hash identically. + MODE is used in hashing for CONST_INTs only; + otherwise the mode of X is used. + + Store 1 in do_not_record if any subexpression is volatile. + + Store 1 in hash_arg_in_memory if X contains a MEM rtx + which does not have the RTX_UNCHANGING_P bit set. + In this case, also store 1 in hash_arg_in_struct + if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set. + + Note that cse_insn knows that the hash code of a MEM expression + is just (int) MEM plus the hash code of the address. + It also knows it can use HASHREG to get the hash code of (REG n). */ + +#define HASHBITS 16 + +#define HASHREG(RTX) \ + ((((int) REG << 7) + reg_qty[REGNO (RTX)]) % NBUCKETS) + +static int +canon_hash (x, mode) + rtx x; + enum machine_mode mode; +{ + register int i, j; + register int hash = 0; + register enum rtx_code code; + register char *fmt; + + /* repeat is used to turn tail-recursion into iteration. */ + repeat: + if (x == 0) + return hash; + + code = GET_CODE (x); + switch (code) + { + case REG: + { + /* We do not invalidate anything on pushing or popping + because they cannot change anything but the stack pointer; + but that means we must consider the stack pointer volatile + since it can be changed "mysteriously". */ + + register int regno = REGNO (x); + if (regno == STACK_POINTER_REGNUM + || (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) + { + do_not_record = 1; + return 0; + } +#ifdef SMALL_REGISTER_CLASSES + if (regno < FIRST_PSEUDO_REGISTER) + { + do_not_record = 1; + return 0; + } +#endif + return hash + ((int) REG << 7) + reg_qty[regno]; + } + + case CONST_INT: + hash += ((int) mode + ((int) CONST_INT << 7) + + INTVAL (x) + (INTVAL (x) >> HASHBITS)); + return ((1 << HASHBITS) - 1) & hash; + + case CONST_DOUBLE: + /* This is like the general case, except that it only counts + the first two elements. */ + hash += (int) code + (int) GET_MODE (x); + { + int i; + for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++) + { + int tem = XINT (x, i); + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + } + return hash; + + /* Assume there is only one rtx object for any given label. */ + case LABEL_REF: + /* Use `and' to ensure a positive number. */ + return (hash + ((int) LABEL_REF << 7) + + ((int) XEXP (x, 0) & ((1 << HASHBITS) - 1))); + + case SYMBOL_REF: + return (hash + ((int) SYMBOL_REF << 7) + + ((int) XEXP (x, 0) & ((1 << HASHBITS) - 1))); + + case MEM: + if (MEM_VOLATILE_P (x)) + { + do_not_record = 1; + return 0; + } + if (! RTX_UNCHANGING_P (x)) + { + hash_arg_in_memory = 1; + if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1; + } + /* Now that we have already found this special case, + might as well speed it up as much as possible. */ + hash += (int) MEM; + x = XEXP (x, 0); + goto repeat; + + case PRE_DEC: + case PRE_INC: + case POST_DEC: + case POST_INC: + case PC: + case CC0: + case CALL: + do_not_record = 1; + return 0; + + case ASM_OPERANDS: + if (MEM_VOLATILE_P (x)) + { + do_not_record = 1; + return 0; + } + } + + i = GET_RTX_LENGTH (code) - 1; + hash += (int) code + (int) GET_MODE (x); + fmt = GET_RTX_FORMAT (code); + for (; i >= 0; i--) + { + if (fmt[i] == 'e') + { + /* If we are about to do the last recursive call + needed at this level, change it into iteration. + This function is called enough to be worth it. */ + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + hash += canon_hash (XEXP (x, i), 0); + } + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + hash += canon_hash (XVECEXP (x, i, j), 0); + else if (fmt[i] == 's') + { + register char *p = XSTR (x, i); + if (p) + while (*p) + { + register int tem = *p++; + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + } + else + { + register int tem = XINT (x, i); + hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS)); + } + } + return hash; +} + +/* Like canon_hash but with no side effects. */ + +static int +safe_hash (x, mode) + rtx x; + enum machine_mode mode; +{ + int save_do_not_record = do_not_record; + int save_hash_arg_in_memory = hash_arg_in_memory; + int save_hash_arg_in_struct = hash_arg_in_struct; + int hash = canon_hash (x, mode); + hash_arg_in_memory = save_hash_arg_in_memory; + hash_arg_in_struct = save_hash_arg_in_struct; + do_not_record = save_do_not_record; + return hash; +} + +/* Return 1 iff X and Y would canonicalize into the same thing, + without actually constructing the canonicalization of either one. + If VALIDATE is nonzero, + we assume X is an expression being processed from the rtl + and Y was found in the hash table. We check register refs + in Y for being marked as valid. */ + +static int +exp_equiv_p (x, y, validate) + rtx x, y; + int validate; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + /* Note: it is incorrect to assume an expression is equivalent to itself + if VALIDATE is nonzero. */ + if (x == y && !validate) + return 1; + if (x == 0 || y == 0) + return x == y; + code = GET_CODE (x); + if (code != GET_CODE (y)) + return 0; + + switch (code) + { + case PC: + case CC0: + return x == y; + + case CONST_INT: + return XINT (x, 0) == XINT (y, 0); + + case LABEL_REF: + case SYMBOL_REF: + return XEXP (x, 0) == XEXP (y, 0); + + case REG: + return (reg_qty[REGNO (x)] == reg_qty[REGNO (y)] + && (!validate + || reg_in_table[REGNO (y)] == reg_tick[REGNO (y)])); + } + + /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ + + if (GET_MODE (x) != GET_MODE (y)) + return 0; + + /* Compare the elements. If any pair of corresponding elements + fail to match, return 0 for the whole things. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate)) + return 0; + } + else if (fmt[i] == 'E') + { + int j; + if (XVECLEN (x, i) != XVECLEN (y, i)) + return 0; + for (j = 0; j < XVECLEN (x, i); j++) + if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j), validate)) + return 0; + } + else if (fmt[i] == 's') + { + if (strcmp (XSTR (x, i), XSTR (y, i))) + return 0; + } + else + { + if (XINT (x, i) != XINT (y, i)) + return 0; + } + } + return 1; +} + +/* Return 1 iff any subexpression of X matches Y. + Here we do not require that X or Y be valid (for registers referred to) + for being in the hash table. */ + +int +refers_to_p (x, y) + rtx x, y; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + repeat: + if (x == y) + return 1; + if (x == 0 || y == 0) + return 0; + + code = GET_CODE (x); + /* If X as a whole has the same code as Y, they may match. + If so, return 1. */ + if (code == GET_CODE (y)) + { + if (exp_equiv_p (x, y, 0)) + return 1; + } + + /* X does not match, so try its subexpressions. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + else + if (refers_to_p (XEXP (x, i), y)) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (refers_to_p (XVECEXP (x, i, j), y)) + return 1; + } + + return 0; +} + +/* Return 1 iff any subexpression of X refers to memory + at an address of REG plus some offset + such that any of the bytes' offsets fall between START (inclusive) + and END (exclusive). + + The value is undefined if X is a varying address. + This function is not used in such cases. + + When used in the cse pass, `qty_const' is nonzero, and it is used + to treat an address that is a register with a known constant value + as if it were that constant value. + In the loop pass, `qty_const' is zero, so this is not done. */ + +int +refers_to_mem_p (x, reg, start, end) + rtx x, reg; + int start, end; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + if (GET_CODE (reg) == CONST_INT) + { + start += INTVAL (reg); + end += INTVAL (reg); + reg = const0_rtx; + } + + repeat: + if (x == 0) + return 0; + + code = GET_CODE (x); + if (code == MEM) + { + register rtx addr = XEXP (x, 0); /* Get the address. */ + int myend; + if (GET_CODE (addr) == REG + /* qty_const is 0 when outside the cse pass; + at such times, this info is not available. */ + && qty_const != 0 + && qty_const[reg_qty[REGNO (addr)]] != 0) + addr = qty_const[reg_qty[REGNO (addr)]]; + if (GET_CODE (addr) == CONST) + addr = XEXP (addr, 0); + + /* If ADDR is BASE, or BASE plus an integer, put + the integer in I. */ + if (addr == reg) + i = 0; + else if (GET_CODE (addr) == PLUS + && XEXP (addr, 0) == reg + && GET_CODE (XEXP (addr, 1)) == CONST_INT) + i = INTVAL (XEXP (addr, 1)); + else if (GET_CODE (addr) == CONST_INT && reg == const0_rtx) + i = INTVAL (addr); + else + return 0; + + myend = i + GET_MODE_SIZE (GET_MODE (x)); + return myend > start && i < end; + } + + /* X does not match, so try its subexpressions. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + if (i == 0) + { + x = XEXP (x, 0); + goto repeat; + } + else + if (refers_to_mem_p (XEXP (x, i), reg, start, end)) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (refers_to_mem_p (XVECEXP (x, i, j), reg, start, end)) + return 1; + } + + return 0; +} + +/* Nonzero if X refers to memory at a varying address; + except that a register which has at the moment a known constant value + isn't considered variable. */ + +static int +cse_rtx_addr_varies_p (x) + rtx x; +{ + if (GET_CODE (x) == MEM + && GET_CODE (XEXP (x, 0)) == REG + && qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0) + return 0; + return rtx_addr_varies_p (x); +} + +/* Canonicalize an expression: + replace each register reference inside it + with the "oldest" equivalent register. */ + +static rtx +canon_reg (x) + rtx x; +{ + register int i; + register enum rtx_code code; + register char *fmt; + + if (x == 0) + return x; + + code = GET_CODE (x); + switch (code) + { + case PC: + case CC0: + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case ADDR_VEC: + case ADDR_DIFF_VEC: + return x; + + case REG: + { + register rtx new; + /* Never replace a hard reg, because hard regs can appear + in more than one machine mode, and we must preserve the mode + of each occurrence. Also, some hard regs appear in + MEMs that are shared and mustn't be altered. */ + if (REGNO (x) < FIRST_PSEUDO_REGISTER) + return x; + new = reg_rtx[qty_first_reg[reg_qty[REGNO (x)]]]; + return new ? new : x; + } + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + register int j; + + if (fmt[i] == 'e') + XEXP (x, i) = canon_reg (XEXP (x, i)); + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j)); + } + + return x; +} + +/* If X is a nontrivial arithmetic operation on an argument + for which a constant value can be determined, return + the result of operating on that value, as a constant. + Otherwise, return X, possibly with one or more operands + modified by recursive calls to this function. + + If X is a register whose contents are known, we do NOT + return those contents. This is because an instruction that + uses a register is usually faster than one that uses a constant. + + COPYFLAG is nonzero for memory addresses and subexpressions thereof. + If COPYFLAG is nonzero, we avoid altering X itself + by creating new structure when necessary. In this case we + can risk creating invalid structure because it will be tested. + If COPYFLAG is zero, be careful not to substitute constants + into expressions that cannot be simplified. */ + +static rtx +fold_rtx (x, copyflag) + rtx x; + int copyflag; +{ + register enum rtx_code code; + register char *fmt; + register int i, val; + rtx new = 0; + int copied = ! copyflag; + int width; + + /* Constant equivalents of first three operands of X; + 0 when no such equivalent is known. */ + rtx const_arg0; + rtx const_arg1; + rtx const_arg2; + + if (x == 0) + return x; + + width = GET_MODE_BITSIZE (GET_MODE (x)); + + code = GET_CODE (x); + switch (code) + { + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case PC: + case CC0: + case REG: + /* No use simplifying an EXPR_LIST + since they are used only for lists of args + in a function call's REG_EQUAL note. */ + case EXPR_LIST: + return x; + + /* We must be careful when folding a memory address + to avoid making it invalid. So fold nondestructively + and use the result only if it's valid. */ + case MEM: + { + rtx newaddr = fold_rtx (XEXP (x, 0), 1); + /* Save time if no change was made. */ + if (XEXP (x, 0) == newaddr) + return x; + + if (! memory_address_p (GET_MODE (x), newaddr) + && memory_address_p (GET_MODE (x), XEXP (x, 0))) + return x; + + /* Don't replace a value with a more expensive one. */ + if (rtx_cost (XEXP (x, 0)) < rtx_cost (newaddr)) + return x; + + if (copyflag) + return gen_rtx (MEM, GET_MODE (x), newaddr); + XEXP (x, 0) = newaddr; + return x; + } + } + + const_arg0 = 0; + const_arg1 = 0; + const_arg2 = 0; + + /* Try folding our operands. + Then see which ones have constant values known. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + { + register rtx tem = fold_rtx (XEXP (x, i), copyflag); + + /* If an operand has changed under folding, and we are not supposed to + alter the original structure, copy X if we haven't yet done so. */ + if (! copied && tem != XEXP (x, i)) + { + int j; + rtx new = rtx_alloc (code); + PUT_MODE (new, GET_MODE (x)); + for (j = 0; j < GET_RTX_LENGTH (code); j++) + XINT (new, j) = XINT (x, j); + x = new; + copied = 1; + } + + /* Install the possibly altered folded operand. */ + XEXP (x, i) = tem; + + /* For the first three operands, see if the operand + is constant or equivalent to a constant. */ + if (i < 3) + { + rtx const_arg = equiv_constant (tem); + + switch (i) + { + case 0: + const_arg0 = const_arg; + break; + case 1: + const_arg1 = const_arg; + break; + case 2: + const_arg2 = const_arg; + break; + } + } + } + else if (fmt[i] == 'E') + /* Don't try to fold inside of a vector of expressions. + Doing nothing is is harmless. */ + ; + + /* If a commutative operation, place a constant integer as the second + operand unless the first operand is also a constant integer. Otherwise, + place any constant second unless the first operand is also a constant. */ + + switch (code) + { + case PLUS: + case MULT: + case UMULT: + case AND: + case IOR: + case XOR: + case NE: + case EQ: + if (const_arg0 && const_arg0 == XEXP (x, 0) + && (! (const_arg1 && const_arg1 == XEXP (x, 1)) + || (GET_CODE (const_arg0) == CONST_INT + && GET_CODE (const_arg1) != CONST_INT))) + { + register rtx tem; + + if (! copied) + copied = 1, x = copy_rtx (x); + tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1); XEXP (x, 1) = tem; + tem = const_arg0; const_arg0 = const_arg1; const_arg1 = tem; + } + break; + } + + /* Now decode the kind of rtx X is + and then return X (if nothing can be done) + or return a folded rtx + or store a value in VAL and drop through + (to return a CONST_INT for the integer VAL). */ + + if (GET_RTX_LENGTH (code) == 1) + { + if (const_arg0 == 0) + return x; + + if (GET_CODE (const_arg0) == CONST_INT) + { + register int arg0 = INTVAL (const_arg0); + + switch (GET_CODE (x)) + { + case NOT: + val = ~ arg0; + break; + + case NEG: + val = - arg0; + break; + + case TRUNCATE: + val = arg0; + break; + + case ZERO_EXTEND: + { + enum machine_mode mode = GET_MODE (XEXP (x, 0)); + if (mode == VOIDmode) + return x; + if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) + val = arg0 & ~((-1) << GET_MODE_BITSIZE (mode)); + else + return x; + break; + } + + case SIGN_EXTEND: + { + enum machine_mode mode = GET_MODE (XEXP (x, 0)); + if (mode == VOIDmode) + return x; + if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT) + { + val = arg0 & ~((-1) << GET_MODE_BITSIZE (mode)); + if (val & (1 << (GET_MODE_BITSIZE (mode) - 1))) + val -= 1 << GET_MODE_BITSIZE (mode); + } + else + return x; + break; + } + + default: + return x; + } + } +#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + else if (GET_CODE (const_arg0) == CONST_DOUBLE + && GET_CODE (x) == NEG + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + union real_extract u; + register REAL_VALUE_TYPE arg0; + jmp_buf handler; + + if (setjmp (handler)) + { + warning ("floating point trap in constant folding"); + return x; + } + set_float_handler (handler); + bcopy (&CONST_DOUBLE_LOW (const_arg0), &u, sizeof u); + arg0 = u.d; + + u.d = REAL_VALUE_NEGATE (arg0); + x = immed_real_const_1 (u.d, GET_MODE (x)); + set_float_handler (0); + return x; + } +#endif + else + return x; + } + else if (GET_RTX_LENGTH (code) == 2) + { + register int arg0, arg1, arg0s, arg1s; + int arithwidth = width; + + /* If 1st arg is the condition codes, 2nd must be zero + and this must be a comparison. + Decode the info on how the previous insn set the cc0 + and use that to deduce result of comparison. */ + if (XEXP (x, 0) == cc0_rtx + || GET_CODE (XEXP (x, 0)) == COMPARE) + { + if (XEXP (x, 0) == cc0_rtx) + arg0 = prev_insn_cc0; + else + arg0 = fold_cc0 (VOIDmode, XEXP (x, 0)); + + if (arg0 == 0 + || const_arg1 != const0_rtx + /* 0200 bit in arg0 means only zeroness is known, + and sign is not known. */ + || ((arg0 & 0200) != 0 && code != EQ && code != NE)) + return x; + + /* Extract either the signed or the unsigned digit from ARG0. */ + if (code == LEU || code == LTU || code == GEU || code == GTU) + arg0 = arg0 & 7; + else + arg0 = (arg0 >> 3) & 7; + if (arg0 == 7) arg0 = -1; + + switch (code) + { + case LE: + case LEU: + return (arg0 <= 0) ? const1_rtx : const0_rtx; + case LT: + case LTU: + return (arg0 < 0) ? const1_rtx : const0_rtx; + case GE: + case GEU: + return (arg0 >= 0) ? const1_rtx : const0_rtx; + case GT: + case GTU: + return (arg0 > 0) ? const1_rtx : const0_rtx; + case NE: + return (arg0 != 0) ? const1_rtx : const0_rtx; + case EQ: + return (arg0 == 0) ? const1_rtx : const0_rtx; + default: + abort (); + } + } + + if (const_arg0 == 0 || const_arg1 == 0 + || GET_CODE (const_arg0) != CONST_INT + || GET_CODE (const_arg1) != CONST_INT) + { + /* Even if we can't compute a constant result, + there are some cases worth simplifying. */ + /* Note that we cannot rely on constant args to come last, + even for commutative operators, + because that happens only when the constant is explicit. */ + switch (code) + { + case PLUS: + if (const_arg0 == const0_rtx + || const_arg0 == fconst0_rtx + || const_arg0 == dconst0_rtx) + return XEXP (x, 1); + if (const_arg1 == const0_rtx + || const_arg1 == fconst0_rtx + || const_arg1 == dconst0_rtx) + return XEXP (x, 0); + + /* Handle both-operands-constant cases. */ + if (const_arg0 != 0 && const_arg1 != 0 + && GET_CODE (const_arg0) != CONST_DOUBLE + && GET_CODE (const_arg1) != CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) + { + if (GET_CODE (const_arg1) == CONST_INT) + new = plus_constant (const_arg0, INTVAL (const_arg1)); + else + { + new = gen_rtx (PLUS, GET_MODE (x), const0_rtx, const0_rtx); + XEXP (new, 0) = const_arg0; + if (GET_CODE (const_arg0) == CONST) + XEXP (new, 0) = XEXP (const_arg0, 0); + XEXP (new, 1) = const_arg1; + if (GET_CODE (const_arg1) == CONST) + XEXP (new, 1) = XEXP (const_arg1, 0); + new = gen_rtx (CONST, GET_MODE (new), new); + } + } + else if (const_arg1 != 0 + && GET_CODE (const_arg1) == CONST_INT + && GET_CODE (XEXP (x, 0)) == PLUS + && (CONSTANT_P (XEXP (XEXP (x, 0), 0)) + || CONSTANT_P (XEXP (XEXP (x, 0), 1)))) + /* constant + (variable + constant) + can result if an index register is made constant. + We simplify this by adding the constants. + If we did not, it would become an invalid address. */ + new = plus_constant (XEXP (x, 0), + INTVAL (const_arg1)); + break; + + case COMPARE: + if (const_arg1 == const0_rtx) + return XEXP (x, 0); + + if (XEXP (x, 0) == XEXP (x, 1) + || (const_arg0 != 0 && const_arg0 == const_arg1)) + { + /* We can't assume x-x is 0 with IEEE floating point. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) + return const0_rtx; + } + break; + + case MINUS: + if (const_arg1 == const0_rtx + || const_arg1 == fconst0_rtx + || const_arg1 == dconst0_rtx) + return XEXP (x, 0); + + if (XEXP (x, 0) == XEXP (x, 1) + || (const_arg0 != 0 && const_arg0 == const_arg1)) + { + /* We can't assume x-x is 0 with IEEE floating point. */ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT) + return const0_rtx; + } + + /* Change subtraction from zero into negation. */ + if (const_arg0 == const0_rtx) + return gen_rtx (NEG, GET_MODE (x), XEXP (x, 1)); + + /* Don't let a relocatable value get a negative coeff. */ + if (const_arg0 != 0 && const_arg1 != 0 + && GET_CODE (const_arg1) == CONST_INT) + new = plus_constant (const_arg0, - INTVAL (const_arg1)); + break; + + case MULT: + case UMULT: + if (const_arg1 && GET_CODE (const_arg1) == CONST_INT + && INTVAL (const_arg1) == -1 + /* Don't do this in the case of widening multiplication. */ + && GET_MODE (XEXP (x, 0)) == GET_MODE (x)) + return gen_rtx (NEG, GET_MODE (x), XEXP (x, 0)); + if (const_arg0 && GET_CODE (const_arg0) == CONST_INT + && INTVAL (const_arg0) == -1 + && GET_MODE (XEXP (x, 1)) == GET_MODE (x)) + return gen_rtx (NEG, GET_MODE (x), XEXP (x, 1)); + if (const_arg1 == const0_rtx || const_arg0 == const0_rtx) + new = const0_rtx; + if (const_arg1 == fconst0_rtx || const_arg0 == fconst0_rtx) + new = fconst0_rtx; + if (const_arg1 == dconst0_rtx || const_arg0 == dconst0_rtx) + new = dconst0_rtx; + if (const_arg1 == const1_rtx) + return XEXP (x, 0); + if (const_arg0 == const1_rtx) + return XEXP (x, 1); + break; + + case IOR: + if (const_arg1 == const0_rtx) + return XEXP (x, 0); + if (const_arg0 == const0_rtx) + return XEXP (x, 1); + if (const_arg1 && GET_CODE (const_arg1) == CONST_INT + && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + new = const_arg1; + if (const_arg0 && GET_CODE (const_arg0) == CONST_INT + && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + new = const_arg0; + break; + + case XOR: + if (const_arg1 == const0_rtx) + return XEXP (x, 0); + if (const_arg0 == const0_rtx) + return XEXP (x, 1); + if (const_arg1 && GET_CODE (const_arg1) == CONST_INT + && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + return gen_rtx (NOT, GET_MODE (x), XEXP (x, 0)); + if (const_arg0 && GET_CODE (const_arg0) == CONST_INT + && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + return gen_rtx (NOT, GET_MODE (x), XEXP (x, 1)); + break; + + case AND: + if (const_arg1 == const0_rtx || const_arg0 == const0_rtx) + new = const0_rtx; + if (const_arg1 && GET_CODE (const_arg1) == CONST_INT + && (INTVAL (const_arg1) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + return XEXP (x, 0); + if (const_arg0 && GET_CODE (const_arg0) == CONST_INT + && (INTVAL (const_arg0) & GET_MODE_MASK (GET_MODE (x))) + == GET_MODE_MASK (GET_MODE (x))) + return XEXP (x, 1); + break; + + case DIV: + case UDIV: + if (const_arg1 == const1_rtx) + return XEXP (x, 0); + if (const_arg0 == const0_rtx) + new = const0_rtx; + break; + + case UMOD: + case MOD: + if (const_arg0 == const0_rtx || const_arg1 == const1_rtx) + new = const0_rtx; + break; + + case LSHIFT: + case ASHIFT: + case ROTATE: + case ASHIFTRT: + case LSHIFTRT: + case ROTATERT: + if (const_arg1 == const0_rtx) + return XEXP (x, 0); + if (const_arg0 == const0_rtx) + new = const_arg0; + break; + } + + if (new != 0 && LEGITIMATE_CONSTANT_P (new)) + return new; + return x; + } + + if (arithwidth == 0) + { + if (GET_MODE (XEXP (x, 0)) != VOIDmode) + arithwidth = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))); + if (GET_MODE (XEXP (x, 1)) != VOIDmode) + arithwidth = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 1))); + } + + /* Get the integer argument values in two forms: + zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ + + arg0 = INTVAL (const_arg0); + arg1 = INTVAL (const_arg1); + + if (arithwidth < HOST_BITS_PER_INT && arithwidth > 0) + { + arg0 &= (1 << arithwidth) - 1; + arg1 &= (1 << arithwidth) - 1; + + arg0s = arg0; + if (arg0s & (1 << (arithwidth - 1))) + arg0s |= ((-1) << arithwidth); + + arg1s = arg1; + if (arg1s & (1 << (arithwidth - 1))) + arg1s |= ((-1) << arithwidth); + } + else + { + arg0s = arg0; + arg1s = arg1; + } + + /* Compute the value of the arithmetic. */ + + switch (code) + { + case PLUS: + val = arg0 + arg1; + break; + + case MINUS: + val = arg0 - arg1; + break; + + case MULT: + val = arg0s * arg1s; + break; + + case DIV: + if (arg1s == 0) + return x; + val = arg0s / arg1s; + break; + + case MOD: + if (arg1s == 0) + return x; + val = arg0s % arg1s; + break; + + case UMULT: + val = (unsigned) arg0 * arg1; + break; + + case UDIV: + if (arg1 == 0) + return x; + val = (unsigned) arg0 / arg1; + break; + + case UMOD: + if (arg1 == 0) + return x; + val = (unsigned) arg0 % arg1; + break; + + case AND: + val = arg0 & arg1; + break; + + case IOR: + val = arg0 | arg1; + break; + + case XOR: + val = arg0 ^ arg1; + break; + + case NE: + val = arg0 != arg1; + break; + + case EQ: + val = arg0 == arg1; + break; + + case LE: + val = arg0s <= arg1s; + break; + + case LT: + val = arg0s < arg1s; + break; + + case GE: + val = arg0s >= arg1s; + break; + + case GT: + val = arg0s > arg1s; + break; + + case LEU: + val = ((unsigned) arg0) <= ((unsigned) arg1); + break; + + case LTU: + val = ((unsigned) arg0) < ((unsigned) arg1); + break; + + case GEU: + val = ((unsigned) arg0) >= ((unsigned) arg1); + break; + + case GTU: + val = ((unsigned) arg0) > ((unsigned) arg1); + break; + + case LSHIFT: + /* If target machine uses negative shift counts + but host machine does not, simulate them. */ + if (arg1 < 0) + val = ((unsigned) arg0) >> -arg1; + else + val = ((unsigned) arg0) << arg1; + break; + + case ASHIFT: + if (arg1 < 0) + val = arg0s >> -arg1; + else + val = arg0s << arg1; + break; + + case ROTATERT: + arg1 = - arg1; + case ROTATE: + { + int size = GET_MODE_SIZE (GET_MODE (x)) * BITS_PER_UNIT; + if (arg1 > 0) + { + arg1 %= size; + val = ((((unsigned) arg0) << arg1) + | (((unsigned) arg0) >> (size - arg1))); + } + else if (arg1 < 0) + { + arg1 = (- arg1) % size; + val = ((((unsigned) arg0) >> arg1) + | (((unsigned) arg0) << (size - arg1))); + } + else + val = arg0; + } + break; + + case LSHIFTRT: + /* If target machine uses negative shift counts + but host machine does not, simulate them. */ + if (arg1 < 0) + val = ((unsigned) arg0) << -arg1; + else + val = ((unsigned) arg0) >> arg1; + break; + + case ASHIFTRT: + if (arg1 < 0) + val = arg0s << -arg1; + else + val = arg0s >> arg1; + break; + + default: + return x; + } + } + else if (code == IF_THEN_ELSE && const_arg0 != 0 + && GET_CODE (const_arg0) == CONST_INT) + return XEXP (x, ((INTVAL (const_arg0) != 0) ? 1 : 2)); + else if (code == IF_THEN_ELSE && XEXP (x, 0) == cc0_rtx + && prev_insn_explicit_cc0 != 0) + return XEXP (x, ((INTVAL (prev_insn_explicit_cc0) != 0) ? 1 : 2)); + else if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) + { + if (const_arg0 != 0 && const_arg1 != 0 && const_arg2 != 0 + && GET_CODE (const_arg0) == CONST_INT + && GET_CODE (const_arg1) == CONST_INT + && GET_CODE (const_arg2) == CONST_INT) + { + /* Extracting a bit-field from a constant */ + val = INTVAL (const_arg0); +#ifdef BITS_BIG_ENDIAN + val >>= (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) + - INTVAL (const_arg2) - INTVAL (const_arg1)); +#else + val >>= INTVAL (const_arg2); +#endif + if (HOST_BITS_PER_INT != INTVAL (const_arg1)) + { + /* First zero-extend. */ + val &= (1 << INTVAL (const_arg1)) - 1; + /* If desired, propagate sign bit. */ + if (code == SIGN_EXTRACT + && (val & (1 << (INTVAL (const_arg1) - 1)))) + val |= ~ (1 << INTVAL (const_arg1)); + } + } + else + return x; + } + else + return x; + + /* Clear the bits that don't belong in our mode, + unless they and our sign bit are all one. + So we get either a reasonable negative value or a reasonable + unsigned value for this mode. */ + if (width < HOST_BITS_PER_INT && width > 0) + { + if ((val & ((-1) << (width - 1))) + != ((-1) << (width - 1))) + val &= (1 << width) - 1; + } + + /* Now make the new constant. */ + { + rtx new = gen_rtx (CONST_INT, VOIDmode, val); + return LEGITIMATE_CONSTANT_P (new) ? new : x; + } +} + +/* Return a constant value currently equivalent to X. + Return 0 if we don't know one. */ + +static rtx +equiv_constant (x) + rtx x; +{ + rtx tem1; + + if (CONSTANT_P (x) || GET_CODE (x) == CONST_DOUBLE) + return x; + else if (GET_CODE (x) == REG + && (tem1 = qty_const[reg_qty[REGNO (x)]]) != 0 + /* Make sure it is really a constant */ + && GET_CODE (tem1) != REG && GET_CODE (tem1) != PLUS) + return tem1; + /* If integer truncation is being done with SUBREG, + we can compute the result. */ + else if (GET_CODE (x) == SUBREG && SUBREG_WORD (x) == 0 + && (tem1 = qty_const[reg_qty[REGNO (SUBREG_REG (x))]]) != 0 + /* Make sure it is a known integer. */ + && GET_CODE (tem1) == CONST_INT + && GET_MODE_SIZE (GET_MODE (x)) <= HOST_BITS_PER_INT + /* Make sure this SUBREG is truncation. */ + && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) + { + int value = INTVAL (tem1); + if (GET_MODE_BITSIZE (GET_MODE (x)) != HOST_BITS_PER_INT) + value &= (1 << GET_MODE_BITSIZE (GET_MODE (x))) - 1; + + if (value == INTVAL (tem1)) + return tem1; + else + return gen_rtx (CONST_INT, VOIDmode, value); + } + return 0; +} + +/* Given an expression X which is used to set CC0, + return an integer recording (in the encoding used for prev_insn_cc0) + how the condition codes would be set by that expression. + Return 0 if the value is not constant + or if there is any doubt what condition codes result from it. + + MODE is the machine mode to use to interpret X if it is a CONST_INT. */ + +static int +fold_cc0 (mode, x) + enum machine_mode mode; + rtx x; +{ + if (GET_CODE (x) == COMPARE) + { + rtx y0 = fold_rtx (XEXP (x, 0), 0); + rtx y1 = fold_rtx (XEXP (x, 1), 0); + int u0, u1, s0, s1; + enum machine_mode m; + rtx tem; + + m = GET_MODE (y0); + if (m == VOIDmode) + m = GET_MODE (y1); + if (m == VOIDmode) + return 0; + + tem = equiv_constant (y0); + if (tem != 0) + y0 = tem; + + if (y0 == 0) + return 0; + + tem = equiv_constant (y1); + if (tem != 0) + y1 = tem; + + if (y1 == 0) + return 0; + + /* Compare floats; report the result only for signed compares + since that's all there are for floats. */ + if (GET_CODE (y0) == CONST_DOUBLE + && GET_CODE (y1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (y0)) == MODE_FLOAT) + { + union real_extract u0, u1; + int value; + jmp_buf handler; + + if (setjmp (handler)) + { + warning ("floating point trap in constant folding"); + return 0; + } + set_float_handler (handler); + bcopy (&CONST_DOUBLE_LOW (y0), &u0, sizeof u0); + bcopy (&CONST_DOUBLE_LOW (y1), &u1, sizeof u1); + value = 0100 + (REAL_VALUES_LESS (u0.d, u1.d) ? 7 << 3 + : REAL_VALUES_LESS (u1.d, u0.d) ? 1 << 3 : 0); + set_float_handler (0); + return value; + } + + /* Aside from that, demand explicit integers. */ + + if (GET_CODE (y0) != CONST_INT) + return 0; + + if (GET_CODE (y1) != CONST_INT) + return 0; + + s0 = u0 = INTVAL (y0); + s1 = u1 = INTVAL (y1); + + { + int width = GET_MODE_BITSIZE (m); + if (width < HOST_BITS_PER_INT) + { + s0 = u0 &= ~ ((-1) << width); + s1 = u1 &= ~ ((-1) << width); + if (u0 & (1 << (width - 1))) + s0 |= ((-1) << width); + if (u1 & (1 << (width - 1))) + s1 |= ((-1) << width); + } + } + + return 0100 + ((s0 < s1 ? 7 : s0 > s1) << 3) + + (((unsigned) u0 < (unsigned) u1) ? 7 + : ((unsigned) u0 > (unsigned) u1)); + } + { + rtx y0; + int u0, s0; + enum machine_mode m; + + y0 = fold_rtx (x, 0); + + m = GET_MODE (y0); + if (m == VOIDmode) + m = mode; + + if (GET_CODE (y0) == REG) + y0 = qty_const[reg_qty[REGNO (y0)]]; + + /* Register had no constant equivalent? We can't do anything. */ + if (y0 == 0) + return 0; + + /* If we don't know the mode, we can't test the sign. */ + if (m == VOIDmode) + return 0; + + /* Value is frame-pointer plus a constant? Or non-explicit constant? + That isn't zero, but we don't know its sign. */ + if (FIXED_BASE_PLUS_P (y0) + || GET_CODE (y0) == SYMBOL_REF || GET_CODE (y0) == CONST + || GET_CODE (y0) == LABEL_REF) + return 0300 + (1<<3) + 1; + + /* Otherwise, only integers enable us to optimize. */ + if (GET_CODE (y0) != CONST_INT) + return 0; + + s0 = u0 = INTVAL (y0); + { + int width = GET_MODE_BITSIZE (m); + if (width < HOST_BITS_PER_INT) + { + s0 = u0 &= ~ ((-1) << GET_MODE_BITSIZE (m)); + if (u0 & (1 << (GET_MODE_BITSIZE (m) - 1))) + s0 |= ((-1) << GET_MODE_BITSIZE (m)); + } + } + return 0100 + ((s0 < 0 ? 7 : s0 > 0) << 3) + (u0 != 0); + } +} + +/* Attempt to prove that a loop will be executed >= 1 times, + or prove it will be executed 0 times. + If either can be proved, delete some of the code. */ + +static void +predecide_loop_entry (insn) + register rtx insn; +{ + register rtx jump = NEXT_INSN (insn); + register rtx p; + register rtx loop_top_label = NEXT_INSN (jump); + enum anon1 { UNK, DELETE_LOOP, DELETE_JUMP } disposition = UNK; + int count = 0; + + /* Give up if we don't find a jump that enters the loop. */ + if (! simplejump_p (jump)) + return; + + /* Find the label at the top of the loop. */ + while (GET_CODE (loop_top_label) == BARRIER + || GET_CODE (loop_top_label) == NOTE) + { + loop_top_label = NEXT_INSN (loop_top_label); + /* No label? Give up. */ + if (loop_top_label == 0) + return; + } + if (GET_CODE (loop_top_label) != CODE_LABEL) + abort (); + + /* Find the label at which the loop is entered. */ + p = XEXP (SET_SRC (PATTERN (jump)), 0); + if (GET_CODE (p) != CODE_LABEL) + abort (); + + /* Trace the flow of control through the end test, + propagating constants, to see if result is determined. */ + prev_insn_cc0 = 0; + prev_insn_explicit_cc0 = 0; + /* Avoid infinite loop if we find a cycle of jumps. */ + while (count < 10) + { + /* At end of function? Means rtl is inconsistent, + but this can happen when stmt.c gets confused + by a syntax error. */ + if (p == 0) + break; + /* Arriving at end of loop means endtest will drop out. */ + if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) + { + disposition = DELETE_LOOP; + break; + } + else if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == NOTE) + ; + /* We only know how to handle two kinds of insns: + conditional jumps, and those that set the condition codes. */ + else if (GET_CODE (p) == INSN && GET_CODE (PATTERN (p)) == SET + && SET_DEST (PATTERN (p)) == cc0_rtx) + { + prev_insn_cc0 = fold_cc0 (GET_MODE (SET_SRC (PATTERN (p))), + copy_rtx (SET_SRC (PATTERN (p)))); + if (GET_CODE (SET_SRC (PATTERN (p))) == CONST_INT) + prev_insn_explicit_cc0 = SET_SRC (PATTERN (p)); + } + else if (GET_CODE (p) == JUMP_INSN + && GET_CODE (PATTERN (p)) == SET + && SET_DEST (PATTERN (p)) == pc_rtx) + { + register rtx target + = fold_rtx (SET_SRC (PATTERN (p)), 1); + if (GET_CODE (target) == LABEL_REF) + p = XEXP (target, 0); + else if (target != pc_rtx) + /* If destination of jump is not fixed, give up. */ + break; + count++; + } + /* Any other kind of insn means we don't know + what result the test will have. */ + else + break; + + /* Arriving at top of loop means we can drop straight in. + Check here because we can arrive only via a jump insn + which would have changed P above. */ + if (p == loop_top_label) + { + disposition = DELETE_JUMP; + break; + } + /* We went past one insn; consider the next. */ + p = NEXT_INSN (p); + } + if (disposition == DELETE_JUMP) + { + /* We know the loop test will succeed the first time, + so delete the jump to the test; drop right into loop. + Note that one call to delete_insn gets the BARRIER as well. */ + delete_insn (jump); + } + if (disposition == DELETE_LOOP) + { + /* We know the endtest will fail and drop right out of the loop, + but it isn't safe to delete the loop here. + There could be jumps into it from outside. + So make the entry-jump jump around the loop. + This will cause find_basic_blocks to delete it if appropriate. */ + register rtx label = gen_label_rtx (); + emit_label_after (label, p); + redirect_jump (jump, label); + } +} + +/* CSE processing for one instruction. + First simplify sources and addresses of all assignments + in the instruction, using previously-computed equivalents values. + Then install the new sources and destinations in the table + of available values. */ + +/* Data on one SET contained in the instruction. */ + +struct set +{ + /* The SET rtx itself. */ + rtx rtl; + /* The hash-table element for the SET_SRC of the SET. */ + struct table_elt *src_elt; + /* Hash code for the SET_SRC. */ + int src_hash_code; + /* Hash code for the SET_DEST. */ + int dest_hash_code; + /* The SET_DEST, with SUBREG, etc., stripped. */ + rtx inner_dest; + /* Place where the pointer to the INNER_DEST was found. */ + rtx *inner_dest_loc; + /* Nonzero if the SET_SRC is in memory. */ + char src_in_memory; + /* Nonzero if the SET_SRC is in a structure. */ + char src_in_struct; + /* Nonzero if the SET_SRC contains something + whose value cannot be predicted and understood. */ + char src_volatile; + /* Original machine mode, in case it becomes a CONST_INT. */ + enum machine_mode mode; +}; + +static void +cse_insn (insn) + rtx insn; +{ + register rtx x = PATTERN (insn); + register int i; + register int n_sets = 0; + + /* Records what this insn does to set CC0, + using same encoding used for prev_insn_cc0. */ + int this_insn_cc0 = 0; + /* Likewise, what to store in prev_insn_explicit_cc0. */ + rtx this_insn_explicit_cc0 = 0; + struct write_data writes_memory; + static struct write_data init = {0, 0, 0}; + + rtx src_eqv = 0; + struct table_elt *src_eqv_elt = 0; + int src_eqv_in_memory; + int src_eqv_in_struct; + int src_eqv_hash_code; + + struct set *sets; + + this_insn = insn; + writes_memory = init; + + /* Find all the SETs and CLOBBERs in this instruction. + Record all the SETs in the array `set' and count them. + Also determine whether there is a CLOBBER that invalidates + all memory references, or all references at varying addresses. */ + + if (GET_CODE (x) == SET) + { + rtx tem; + n_sets = 1; + sets = (struct set *) alloca (sizeof (struct set)); + sets[0].rtl = x; + + if (REG_NOTES (insn) != 0) + { + /* Store the equivalent value (re REG_EQUAL or REG_EQUIV) in SRC_EQV. */ + tem = find_reg_note (insn, REG_EQUIV, 0); + if (tem == 0) + tem = find_reg_note (insn, REG_EQUAL, 0); + if (tem) src_eqv = XEXP (tem, 0); + + /* Ignore the REG_EQUAL or REG_EQUIV note if its contents + are the same as the source. */ + if (src_eqv && rtx_equal_p (src_eqv, SET_SRC (x))) + src_eqv = 0; + } + + /* Return now for unconditional jumps. + They never need cse processing, so this does not hurt. + The reason is not efficiency but rather + so that we can test at the end for instructions + that have been simplified to unconditional jumps + and not be misled by unchanged instructions + that were unconditional jumps to begin with. */ + if (SET_DEST (x) == pc_rtx + && GET_CODE (SET_SRC (x)) == LABEL_REF) + return; + + /* Return now for call-insns, (set (reg 0) (call ...)). + The hard function value register is used only once, to copy to + someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)! */ + if (GET_CODE (SET_SRC (x)) == CALL) + { + canon_reg (SET_SRC (x)); + return; + } + } + else if (GET_CODE (x) == PARALLEL) + { + register int lim = XVECLEN (x, 0); + + sets = (struct set *) alloca (lim * sizeof (struct set)); + + /* Find all regs explicitly clobbered in this insn, + and ensure they are not replaced with any other regs + elsewhere in this insn. + When a reg that is clobbered is also used for input, + we should presume that that is for a reason, + and we should not substitute some other register + which is not supposed to be clobbered. */ + for (i = 0; i < lim; i++) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER && GET_CODE (XEXP (y, 0)) == REG) + invalidate (XEXP (y, 0)); + } + + for (i = 0; i < lim; i++) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == SET) + sets[n_sets++].rtl = y; + else if (GET_CODE (y) == CLOBBER) + { + /* If we clobber memory, take note of that, + and canon the address. + This does nothing when a register is clobbered + because we have already invalidated the reg. */ + canon_reg (y); + note_mem_written (XEXP (y, 0), &writes_memory); + } + else if (GET_CODE (y) == USE + && ! (GET_CODE (XEXP (y, 0)) == REG + && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) + canon_reg (y); + else if (GET_CODE (y) == CALL) + canon_reg (y); + } + } + else if (GET_CODE (x) == CLOBBER) + note_mem_written (XEXP (x, 0), &writes_memory); + else if (GET_CODE (x) == CALL) + canon_reg (x); + + if (n_sets == 0) + { + invalidate_from_clobbers (&writes_memory, x); + return; + } + + /* Canonicalize sources and addresses of destinations. + set sets[i].src_elt to the class each source belongs to. + Detect assignments from or to volatile things + and set set[i] to zero so they will be ignored + in the rest of this function. + + Nothing in this loop changes the hash table or the register chains. */ + + for (i = 0; i < n_sets; i++) + { + register rtx src, dest; + register struct table_elt *elt; + enum machine_mode mode; + + dest = SET_DEST (sets[i].rtl); + src = SET_SRC (sets[i].rtl); + + /* If SRC is a constant that has no machine mode, + hash it with the destination's machine mode. + This way we can keep different modes separate. */ + + mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + sets[i].mode = mode; + + /* Replace each registers in SRC with oldest equivalent register, + but if DEST is a register do not replace it if it appears in SRC. */ + + if (GET_CODE (dest) == REG) + { + int tem = reg_qty[REGNO (dest)]; + reg_qty[REGNO (dest)] = REGNO (dest); + src = canon_reg (src); + + if (src_eqv) + src_eqv = canon_reg (src_eqv); + + reg_qty[REGNO (dest)] = tem; + } + else + { + src = canon_reg (src); + + if (src_eqv) + src_eqv = canon_reg (src_eqv); + } + + if (src_eqv) + { + enum machine_mode eqvmode = mode; + if (GET_CODE (dest) == STRICT_LOW_PART) + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + src_eqv = fold_rtx (src_eqv, 0); + src_eqv_hash_code = HASH (src_eqv, eqvmode); + + /* Replace the src_eqv with its cheapest equivalent. */ + + if (!do_not_record) + { + elt = lookup (src_eqv, src_eqv_hash_code, eqvmode); + if (elt && elt != elt->first_same_value) + { + elt = elt->first_same_value; + /* Find the cheapest one that is still valid. */ + while ((GET_CODE (elt->exp) != REG + && !exp_equiv_p (elt->exp, elt->exp, 1)) + || elt->equivalence_only) + elt = elt->next_same_value; + src_eqv = copy_rtx (elt->exp); + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + src_eqv_hash_code = HASH (src_eqv, elt->mode); + } + src_eqv_elt = elt; + } + else + src_eqv = 0; + + src_eqv_in_memory = hash_arg_in_memory; + src_eqv_in_struct = hash_arg_in_struct; + } + + /* Compute SRC's hash code, and also notice if it + should not be recorded at all. In that case, + prevent any further processing of this assignment. */ + do_not_record = 0; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + src = fold_rtx (src, 0); + /* If SRC is a subreg of a reg with a known value, + perform the truncation now. */ + if (GET_CODE (src) == SUBREG) + { + rtx temp = equiv_constant (src); + if (temp) + src = temp; + } + /* If we have (NOT Y), see if Y is known to be (NOT Z). + If so, (NOT Y) simplifies to Z. */ + if (GET_CODE (src) == NOT || GET_CODE (src) == NEG) + { + rtx y = lookup_as_function (XEXP (src, 0), GET_CODE (src)); + if (y != 0) + src = copy_rtx (XEXP (y, 0)); + } + + /* If storing a constant value in a register that + previously held the constant value 0, + record this fact with a REG_WAS_0 note on this insn. */ + if (GET_CODE (src) == CONST_INT + && GET_CODE (dest) == REG + && qty_const[reg_qty[REGNO (dest)]] == const0_rtx) + REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_WAS_0, + qty_const_insn[reg_qty[REGNO (dest)]], + REG_NOTES (insn)); + + sets[i].src_hash_code = HASH (src, mode); + + sets[i].src_volatile = do_not_record; + +#if 0 + /* This code caused multiple hash-table entries + to be created for registers. Invalidation + would only get one, leaving others that didn't belong. + I don't know what good this ever did. */ + if (GET_CODE (src) == REG) + { + sets[i].src_in_memory = 0; + sets[i].src_elt = 0; + } + else ...; +#endif + /* If source is a perverse subreg (such as QI treated as an SI), + treat it as volatile. It may do the work of an SI in one context + where the extra bits are not being used, but cannot replace an SI + in general. */ + if (GET_CODE (src) == SUBREG + && (GET_MODE_SIZE (GET_MODE (src)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))) + sets[i].src_volatile = 1; + else if (!sets[i].src_volatile) + { + /* Replace the source with its cheapest equivalent. */ + + elt = lookup (src, sets[i].src_hash_code, mode); + if (elt && elt != elt->first_same_value) + { + elt = elt->first_same_value; + /* Find the cheapest one that is still valid. */ + while ((GET_CODE (elt->exp) != REG + && !exp_equiv_p (elt->exp, elt->exp, 1)) + || elt->equivalence_only) + elt = elt->next_same_value; + /* Don't replace with things that are not likely to be valid, + such as arithmetic expressions, unless the destination is + a register. */ + if (general_operand (elt->exp, VOIDmode) + || GET_CODE (dest) == REG) + { + src = copy_rtx (elt->exp); + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + sets[i].src_hash_code = HASH (src, elt->mode); + } + } + + /* If ELT is a constant, is there a register + linearly related to it? If so, replace it + with the sum of that register plus an offset. */ + + if (GET_CODE (src) == CONST && n_sets == 1 + && SET_DEST (sets[i].rtl) != cc0_rtx) + { + rtx newsrc = use_related_value (src, elt); + if (newsrc == 0 && src_eqv != 0) + newsrc = use_related_value (src_eqv, src_eqv_elt); + if (newsrc) + { + rtx oldsrc = src; + src = newsrc; + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + sets[i].src_hash_code = HASH (src, GET_MODE (src)); + /* The new expression for the SRC has the same value + as the previous one; so if the previous one is in + the hash table, put the new one in as equivalent. */ + if (elt != 0) + elt = insert (src, elt->first_same_value, sets[i].src_hash_code, + elt->mode); + else + { + /* Maybe the new expression is in the table already. */ + elt = lookup (src, sets[i].src_hash_code, mode); + /* And maybe a register contains the same value. */ + if (elt && elt != elt->first_same_value) + { + elt = elt->first_same_value; + /* Find the cheapest one that is still valid. */ + while ((GET_CODE (elt->exp) != REG + && !exp_equiv_p (elt->exp, elt->exp, 1)) + || elt->equivalence_only) + elt = elt->next_same_value; + src = copy_rtx (elt->exp); + hash_arg_in_memory = 0; + hash_arg_in_struct = 0; + sets[i].src_hash_code = HASH (src, elt->mode); + } + } + + /* This would normally be inhibited by the REG_EQUIV + note we are about to make. */ +#if 0 + /* Deleted because the inhibition was deleted. */ + SET_SRC (sets[i].rtl) = src; +#endif + + /* Record the actual constant value + in a REG_EQUIV or REG_EQUAL note. */ + if (GET_CODE (SET_DEST (sets[i].rtl)) == REG) + { + /* A REG_EQUIV note means the dest never changes. + Don't put one on unless there is already one. */ + rtx note = find_reg_note (insn, REG_EQUIV, 0); + if (note != 0) + XEXP (note, 0) = oldsrc; + else + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, + oldsrc, REG_NOTES (insn)); + } + } + } + + sets[i].src_elt = elt; + sets[i].src_in_memory = hash_arg_in_memory; + sets[i].src_in_struct = hash_arg_in_struct; + } + + /* Either canon_reg or the copy_rtx may have changed this. */ + /* Note it is not safe to replace the sources if there + is more than one set. We could get an insn + [(set (reg) (reg)) (set (reg) (reg))], which is probably + not in the machine description. + This case we could handle by breaking into several insns. + Cases of partial substitution cannot win at all. */ + /* Also, if this insn is setting a "constant" register, + we may not replace the value that is given to it. */ + if (n_sets == 1) +#if 0 + /* Now that the REG_EQUIV contains the constant instead of the reg, + it should be ok to modify the insn's actual source. */ + if (REG_NOTES (insn) == 0 + || REG_NOTE_KIND (REG_NOTES (insn)) != REG_EQUIV) +#endif + SET_SRC (sets[0].rtl) = src; + + do_not_record = 0; + sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl); + + /* Look within any SIGN_EXTRACT or ZERO_EXTRACT + to the MEM or REG within it. */ + while (1) + { + if (GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) + { + XEXP (dest, 1) = canon_reg (XEXP (dest, 1)); + XEXP (dest, 2) = canon_reg (XEXP (dest, 2)); + sets[i].inner_dest_loc = &XEXP (dest, 0); + dest = XEXP (dest, 0); + } + else if (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == STRICT_LOW_PART) + { + sets[i].inner_dest_loc = &XEXP (dest, 0); + dest = XEXP (dest, 0); + } + else + break; + } + + sets[i].inner_dest = dest; + + /* If storing into memory, do cse on the memory address. + Also compute the hash code of the destination now, + before the effects of this instruction are recorded, + since the register values used in the address computation + are those before this instruction. */ + if (GET_CODE (dest) == MEM) + { + register rtx addr; + register int hash; + + canon_reg (dest); + dest = fold_rtx (dest, 0); + addr = XEXP (dest, 0); + + /* Pushing or popping does not invalidate anything. */ + if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) + && GET_CODE (XEXP (addr, 0)) == REG + && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) + ; + else + /* Otherwise, decide whether we invalidate + everything in memory, or just things at non-fixed places. + Writing a large aggregate must invalidate everything + because we don't know how long it is. */ + note_mem_written (dest, &writes_memory); + + /* Do not try to replace addresses of local and argument slots. + The MEM expressions for args and non-register local variables + are made only once and inserted in many instructions, + as well as being used to control symbol table output. + It is not safe to clobber them. It also doesn't do any good! */ + if ((GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 0)) == REG + && GET_CODE (XEXP (addr, 1)) == CONST_INT + && (hash = REGNO (XEXP (addr, 0)), + hash == FRAME_POINTER_REGNUM || hash == ARG_POINTER_REGNUM)) + || (GET_CODE (addr) == REG + && (hash = REGNO (addr), + hash == FRAME_POINTER_REGNUM || hash == ARG_POINTER_REGNUM))) + sets[i].dest_hash_code = ((int)MEM + canon_hash (addr, GET_MODE (dest))) % NBUCKETS; + else + { + /* Look for a simpler equivalent for the destination address. */ + hash = HASH (addr, Pmode); + if (! do_not_record) + { + elt = lookup (addr, hash, Pmode); + sets[i].dest_hash_code = ((int) MEM + hash) % NBUCKETS; + + if (elt && elt != elt->first_same_value) + { + elt = elt->first_same_value; + /* Find the cheapest one that is still valid. */ + while ((GET_CODE (elt->exp) != REG + && !exp_equiv_p (elt->exp, elt->exp, 1)) + || elt->equivalence_only) + elt = elt->next_same_value; + + addr = copy_rtx (elt->exp); + /* Create a new MEM rtx, in case the old one + is shared somewhere else. */ + dest = gen_rtx (MEM, GET_MODE (dest), addr); + MEM_VOLATILE_P (dest) + = MEM_VOLATILE_P (sets[i].inner_dest); + MEM_IN_STRUCT_P (dest) + = MEM_IN_STRUCT_P (sets[i].inner_dest); + *sets[i].inner_dest_loc = dest; + sets[i].inner_dest = dest; + } + } + } + } + + /* Don't enter a bit-field in the hash table + because the value in it after the store + may not equal what was stored, due to truncation. */ + + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT + || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT) + { + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + rtx value = equiv_constant (SET_SRC (sets[i].rtl)); + + if (value != 0 && GET_CODE (value) == CONST_INT + && GET_CODE (width) == CONST_INT + && INTVAL (width) < HOST_BITS_PER_INT + && ! (INTVAL (value) & (-1) << INTVAL (width))) + /* Exception: if the value is constant, + we can tell whether truncation would change it. */ + ; + else + sets[i].src_volatile = 1, src_eqv = 0; + } + + /* No further processing for this assignment + if destination is volatile or if the source and destination + are the same. */ + + else if (do_not_record + || (GET_CODE (dest) == REG + ? REGNO (dest) == STACK_POINTER_REGNUM + : GET_CODE (dest) != MEM) + || rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl))) + sets[i].rtl = 0; + + if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl)) + sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode); + + if (dest == cc0_rtx + && (GET_CODE (src) == COMPARE + || CONSTANT_P (src) + || GET_CODE (src) == REG)) + this_insn_cc0 = fold_cc0 (sets[i].mode, src); + + if (dest == cc0_rtx && GET_CODE (src) == CONST_INT) + this_insn_explicit_cc0 = src; + } + + /* Now enter all non-volatile source expressions in the hash table + if they are not already present. + Record in src_elt the heads of their equivalence classes. + This way we can insert the corresponding destinations into + the same classes even if the actual sources are no longer in them + (having been invalidated). */ + + if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0) + { + register struct table_elt *elt; + rtx dest = SET_DEST (sets[0].rtl); + enum machine_mode eqvmode = GET_MODE (dest); + + if (GET_CODE (dest) == STRICT_LOW_PART) + eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0))); + if (insert_regs (src_eqv, 0, 0)) + src_eqv_hash_code = HASH (src_eqv, eqvmode); + elt = insert (src_eqv, 0, src_eqv_hash_code, eqvmode); + elt->in_memory = src_eqv_in_memory; + elt->in_struct = src_eqv_in_struct; + elt->equivalence_only = 1; + src_eqv_elt = elt->first_same_value; + } + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && ! sets[i].src_volatile) + { + if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART) + { + /* REG_EQUAL in setting a STRICT_LOW_PART + gives an equivalent for the entire destination register, + not just for the subreg being stored in now. + This is a more interesting equivalent, so we arrange later + to treat the entire reg as the destination. */ + sets[i].src_elt = src_eqv_elt; + sets[i].src_hash_code = src_eqv_hash_code; + } + else if (sets[i].src_elt == 0) + { + register rtx src = SET_SRC (sets[i].rtl); + register rtx dest = SET_DEST (sets[i].rtl); + register struct table_elt *elt; + enum machine_mode mode + = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + + /* Note that these insert_regs calls cannot remove + any of the src_elt's, because they would have failed to match + if not still valid. */ + if (insert_regs (src, 0, 0)) + sets[i].src_hash_code = HASH (src, mode); + elt = insert (src, src_eqv_elt, sets[i].src_hash_code, mode); + elt->in_memory = sets[i].src_in_memory; + elt->in_struct = sets[i].src_in_struct; + sets[i].src_elt = elt->first_same_value; + } + } + + invalidate_from_clobbers (&writes_memory, x); + + /* Now invalidate everything set by this instruction. + If a SUBREG or other funny destination is being set, + sets[i].rtl is still nonzero, so here we invalidate the reg + a part of which is being set. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + register rtx dest = sets[i].inner_dest; + + /* Needed for registers to remove the register from its + previous quantity's chain. + Needed for memory if this is a nonvarying address, unless + we have just done an invalidate_memory that covers even those. */ + if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG + || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest))) + invalidate (dest); + } + + /* Make sure registers mentioned in destinations + are safe for use in an expression to be inserted. + This removes from the hash table + any invalid entry that refers to one of these registers. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG) + mention_regs (SET_DEST (sets[i].rtl)); + + /* We may have just removed some of the src_elt's from the hash table. + So replace each one with the current head of the same class. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + /* If the source is volatile, its destination goes in + a class of its own. */ + if (sets[i].src_volatile) + sets[i].src_elt = 0; + + if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0) + /* If elt was removed, find current head of same class, + or 0 if nothing remains of that class. */ + { + register struct table_elt *elt = sets[i].src_elt; + + while (elt && elt->first_same_value == 0) + elt = elt->next_same_value; + sets[i].src_elt = elt ? elt->first_same_value : 0; + } + } + + /* Now insert the destinations into their equivalence classes. */ + + for (i = 0; i < n_sets; i++) + if (sets[i].rtl) + { + register rtx dest = SET_DEST (sets[i].rtl); + register struct table_elt *elt; + + if (flag_float_store + && GET_CODE (dest) == MEM + && (GET_MODE (dest) == SFmode || GET_MODE (dest) == DFmode)) + continue; + + /* STRICT_LOW_PART isn't part of the value BEING set, + and neither is the SUBREG inside it. + Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */ + if (GET_CODE (dest) == STRICT_LOW_PART) + dest = SUBREG_REG (XEXP (dest, 0)); + + if (GET_CODE (dest) == REG) + /* Registers must also be inserted into chains for quantities. */ + if (insert_regs (dest, sets[i].src_elt, 1)) + /* If `insert_regs' changes something, the hash code must be + recalculated. */ + sets[i].dest_hash_code = HASHREG (dest); + + if (GET_CODE (dest) == SUBREG) + /* Registers must also be inserted into chains for quantities. */ + if (insert_regs (dest, sets[i].src_elt, 1)) + /* If `insert_regs' changes something, the hash code must be + recalculated. */ + sets[i].dest_hash_code + = canon_hash (dest, GET_MODE (dest)) % NBUCKETS; + + elt = insert (dest, sets[i].src_elt, sets[i].dest_hash_code, GET_MODE (dest)); + elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM; + if (elt->in_memory) + { + elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest) + || sets[i].inner_dest != SET_DEST (sets[i].rtl)); + } + } + + /* Special handling for (set REG0 REG1) + where REG0 is the "cheapest", cheaper than REG1. + After cse, REG1 will probably not be used in the sequel, + so (if easily done) change this insn to (set REG1 REG0) and + replace REG1 with REG0 in the previous insn that computed their value. + Then REG1 will become a dead store and won't cloud the situation + for later optimizations. */ + if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG + && GET_CODE (SET_SRC (sets[0].rtl)) == REG + && rtx_equal_p (canon_reg (SET_SRC (sets[0].rtl)), SET_DEST (sets[0].rtl))) + { + rtx prev = PREV_INSN (insn); + while (prev && GET_CODE (prev) == NOTE) + prev = PREV_INSN (prev); + + if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET + && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)) + { + rtx dest = SET_DEST (sets[0].rtl); + rtx note = find_reg_note (prev, REG_EQUIV, 0); + + SET_DEST (PATTERN (prev)) = dest; + SET_DEST (sets[0].rtl) = SET_SRC (sets[0].rtl); + SET_SRC (sets[0].rtl) = dest; + /* If REG1 was equivalent to a constant, REG0 is not. */ + if (note) + PUT_MODE (note, REG_EQUAL); + } + } + + /* Did this insn become an unconditional branch or become a no-op? */ + if (GET_CODE (insn) == JUMP_INSN + && GET_CODE (x) == SET + && SET_DEST (x) == pc_rtx) + { + if (SET_SRC (x) == pc_rtx) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + cse_jumps_altered = 1; + /* If previous insn just set CC0 for us, delete it too. */ + if (prev_insn_cc0 != 0 || prev_insn_explicit_cc0 != 0) + { + PUT_CODE (prev_insn, NOTE); + NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (prev_insn) = 0; + } + /* One less use of the label this insn used to jump to. */ + --LABEL_NUSES (JUMP_LABEL (insn)); + } + else if (GET_CODE (SET_SRC (x)) == LABEL_REF) + { + rtx label; + + emit_barrier_after (insn); + cse_jumps_altered = 1; + /* If previous insn just set CC0 for us, delete it too. */ + if (prev_insn_cc0 != 0 || prev_insn_explicit_cc0 != 0) + { + PUT_CODE (prev_insn, NOTE); + NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (prev_insn) = 0; + } + /* If jump target is the following label, and this is only use of it, + skip direct to that label and continue optimizing there. */ + label = insn; + while (label != 0 && GET_CODE (label) != CODE_LABEL) + label = NEXT_INSN (label); + if (label == XEXP (SET_SRC (x), 0) + && LABEL_NUSES (label) == 1) + cse_skip_to_next_block = 1; + } + } + + /* If this insn used to store a value based on CC0 but now value is constant, + and the previous insn just set CC0 for us, delete previous insn. + Here we use the fact that nothing expects CC0 to be valid over an insn, + which is true until the final pass. */ + if (GET_CODE (x) == SET && prev_insn_cc0 + && CONSTANT_P (SET_SRC (x))) + { + PUT_CODE (prev_insn, NOTE); + NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (prev_insn) = 0; + } + + prev_insn_explicit_cc0 = this_insn_explicit_cc0; + prev_insn_cc0 = this_insn_cc0; + prev_insn = insn; +} + +/* Store 1 in *WRITES_PTR for those categories of memory ref + that must be invalidated when the expression WRITTEN is stored in. + If WRITTEN is null, say everything must be invalidated. */ + +static void +note_mem_written (written, writes_ptr) + rtx written; + struct write_data *writes_ptr; +{ + static struct write_data everything = {1, 1, 1}; + + if (written == 0) + *writes_ptr = everything; + else if (GET_CODE (written) == MEM) + { + /* Pushing or popping the stack invalidates nothing. */ + rtx addr = XEXP (written, 0); + if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC) + && GET_CODE (XEXP (addr, 0)) == REG + && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM) + return; + if (GET_MODE (written) == BLKmode) + *writes_ptr = everything; + else if (cse_rtx_addr_varies_p (written)) + { + /* A varying address that is a sum indicates an array element, + and that's just as good as a structure element + in implying that we need not invalidate scalar variables. */ + if (!(MEM_IN_STRUCT_P (written) + || GET_CODE (XEXP (written, 0)) == PLUS)) + writes_ptr->all = 1; + writes_ptr->nonscalar = 1; + } + writes_ptr->var = 1; + } +} + +/* Perform invalidation on the basis of everything about an insn + except for invalidating the actual places that are SET in it. + This includes the places CLOBBERed, and anything that might + alias with something that is SET or CLOBBERed. + + W points to the writes_memory for this insn, a struct write_data + saying which kinds of memory references must be invalidated. + X is the pattern of the insn. */ + +static void +invalidate_from_clobbers (w, x) + struct write_data *w; + rtx x; +{ + /* If W->var is not set, W specifies no action. + If W->all is set, this step gets all memory refs + so they can be ignored in the rest of this function. */ + if (w->var) + invalidate_memory (w); + + if (GET_CODE (x) == CLOBBER) + { + rtx ref = XEXP (x, 0); + if (ref + && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG + || (GET_CODE (ref) == MEM && ! w->all))) + invalidate (ref); + } + else if (GET_CODE (x) == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + register rtx y = XVECEXP (x, 0, i); + if (GET_CODE (y) == CLOBBER) + { + rtx ref = XEXP (y, 0); + if (ref + &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG + || (GET_CODE (ref) == MEM && !w->all))) + invalidate (ref); + } + } + } +} + +/* Find the end of INSN's basic block, and return the cuid of its last insn + and the total number of SETs in all the insns of the block. */ + +struct cse_basic_block_data { int cuid, nsets; rtx last; }; + +static struct cse_basic_block_data +cse_end_of_basic_block (insn) + rtx insn; +{ + rtx p = insn; + struct cse_basic_block_data val; + int nsets = 0; + int last_uid = 0; + + /* Scan to end of this basic block. */ + while (p && GET_CODE (p) != CODE_LABEL) + { + /* Don't cse out the end of a loop. This makes a difference + only for the unusual loops that always execute at least once; + all other loops have labels there so we will stop in any case. + Cse'ing out the end of the loop is dangerous because it + might cause an invariant expression inside the loop + to be reused after the end of the loop. This would make it + hard to move the expression out of the loop in loop.c, + especially if it is one of several equivalent expressions + and loop.c would like to eliminate it. + The occasional optimizations lost by this will all come back + if loop and cse are made to work alternatingly. */ + if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END) + break; + + /* Don't cse over a call to setjmp; on some machines (eg vax) + the regs restored by the longjmp come from + a later time than the setjmp. */ + if (GET_CODE (p) == NOTE + && NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP) + break; + + /* A PARALLEL can have lots of SETs in it, + especially if it is really an ASM_OPERANDS. */ + if (GET_CODE (p) == INSN && GET_CODE (PATTERN (p)) == PARALLEL) + nsets += XVECLEN (PATTERN (p), 0); + else + nsets += 1; + + last_uid = INSN_UID (p); + p = NEXT_INSN (p); + } + val.cuid = uid_cuid[last_uid]; + val.nsets = nsets; + val.last = p; + + return val; +} + +static rtx cse_basic_block (); + +/* Perform cse on the instructions of a function. + F is the first instruction. + NREGS is one plus the highest pseudo-reg number used in the instruction. + + Returns 1 if jump_optimize should be redone due to simplifications + in conditional jump instructions. */ + +int +cse_main (f, nregs) + /* f is the first instruction of a chain of insns for one function */ + rtx f; + /* nregs is the total number of registers used in it */ + int nregs; +{ + register rtx insn = f; + register int i; + + cse_jumps_altered = 0; + + init_recog (); + + max_reg = nregs; + + all_minus_one = (int *) alloca (nregs * sizeof (int)); + consec_ints = (int *) alloca (nregs * sizeof (int)); + for (i = 0; i < nregs; i++) + { + all_minus_one[i] = -1; + consec_ints[i] = i; + } + + reg_next_eqv = (int *) alloca (nregs * sizeof (int)); + reg_prev_eqv = (int *) alloca (nregs * sizeof (int)); + reg_qty = (int *) alloca (nregs * sizeof (int)); + reg_rtx = (rtx *) alloca (nregs * sizeof (rtx)); + reg_in_table = (int *) alloca (nregs * sizeof (int)); + reg_tick = (int *) alloca (nregs * sizeof (int)); + + /* Discard all the free elements of the previous function + since they are allocated in the temporarily obstack. */ + bzero (table, sizeof table); + free_element_chain = 0; + n_elements_made = 0; + + /* Find the largest uid. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) > i) + i = INSN_UID (insn); + + uid_cuid = (short *) alloca ((i + 1) * sizeof (short)); + bzero (uid_cuid, (i + 1) * sizeof (short)); + + /* Compute the mapping from uids to cuids. + CUIDs are numbers assigned to insns, like uids, + except that cuids increase monotonically through the code. + Don't assign cuids to line-number NOTEs, so that the distance in cuids + between two insns is not affected by -g. */ + + for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) != NOTE + || NOTE_LINE_NUMBER (insn) < 0) + INSN_CUID (insn) = ++i; + else + /* Give a line number note the same cuid as preceding insn. */ + INSN_CUID (insn) = i; + } + + /* Loop over basic blocks. + Compute the maximum number of qty's needed for each basic block + (which is 2 for each SET). */ + insn = f; + while (insn) + { + struct cse_basic_block_data val; + + val = cse_end_of_basic_block (insn); + + cse_basic_block_end = val.cuid; + cse_basic_block_start = INSN_CUID (insn); + max_qty = val.nsets * 2; + + /* Make MAX_QTY bigger to give us room to optimize + past the end of this basic block, if that should prove useful. */ + if (max_qty < 500) + max_qty = 500; + + max_qty += max_reg; + + insn = cse_basic_block (insn, val.last); +#ifdef USE_C_ALLOCA + alloca (0); +#endif + } + + /* Tell refers_to_mem_p that qty_const info is not available. */ + qty_const = 0; + + if (max_elements_made < n_elements_made) + max_elements_made = n_elements_made; + + return cse_jumps_altered; +} + +static rtx +cse_basic_block (from, to) + register rtx from, to; +{ + register rtx insn; + int *qv1 = (int *) alloca (max_qty * sizeof (int)); + int *qv2 = (int *) alloca (max_qty * sizeof (int)); + rtx *qv3 = (rtx *) alloca (max_qty * sizeof (rtx)); + + qty_first_reg = qv1; + qty_last_reg = qv2; + qty_const = qv3; + qty_const_insn = (rtx *) alloca (max_qty * sizeof (rtx)); + + new_basic_block (); + + cse_skip_to_next_block = 0; + + for (insn = from; insn != to; insn = NEXT_INSN (insn)) + { + register enum rtx_code code; + + code = GET_CODE (insn); + + if (code == INSN || code == JUMP_INSN || code == CALL_INSN) + cse_insn (insn); + /* Memory, and some registers, are invalidate by subroutine calls. */ + if (code == CALL_INSN) + { + register int i; + static struct write_data everything = {1, 1, 1}; + invalidate_memory (&everything); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (call_used_regs[i] && reg_rtx[i] + && i != FRAME_POINTER_REGNUM + && i != ARG_POINTER_REGNUM) + invalidate (reg_rtx[i]); + } + /* Loop beginnings are often followed by jumps + (that enter the loop above the endtest). + See if we can prove the loop will be executed at least once; + if so, delete the jump. Also perhaps we can prove loop + will never be executed and delete the entire thing. */ + if (code == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG + && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) + { + predecide_loop_entry (insn); + /* Whether that jump was deleted or not, + it certainly is the end of the basic block. + Since the jump is unconditional, + it requires no further processing here. */ + break; + } + + /* See if it is ok to keep on going past the label + which used to end our basic block. */ + if (cse_skip_to_next_block + || (to != 0 && NEXT_INSN (insn) == to && LABEL_NUSES (to) == 0)) + { + struct cse_basic_block_data val; + + /* Skip the remaining insns in this block. */ + cse_skip_to_next_block = 0; + insn = to; + if (insn == 0) + break; + + /* Find the end of the following block. */ + val = cse_end_of_basic_block (NEXT_INSN (insn)); + + /* If the tables we allocated have enough space left + to handle all the SETs in the next basic block, + continue through it. Otherwise, return, + and that block will be scanned individually. */ + if (val.nsets * 2 + next_qty > max_qty) + break; + + cse_basic_block_end = val.cuid; + to = val.last; + } + } + + if (next_qty > max_qty) + abort (); + + return to ? NEXT_INSN (to) : 0; +} |
