This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] fwprop merge (stage2 project), 2/2: CSE chainsaw
- From: Paolo Bonzini <paolo dot bonzini at lu dot unisi dot ch>
- To: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Fri, 17 Feb 2006 14:49:40 +0100
- Subject: [PATCH] fwprop merge (stage2 project), 2/2: CSE chainsaw
Well, this time the metaphor is not as good as with Zack's
genattrtab/genautomata chainsaw pass. Still, this patch cuts about 500
lines of CSE. These include:
1) killing find_best_addr: we found that, about always, simple forward
propagation guarded by address_cost and rtx_cost tests have the same
effect as find_best_addr, both on RISC and CISC processors. As a
result, fold_rtx_mem can now be replaced by avoid_constant_pool_reference.
2) killing fold_rtx_subreg. Parts are subsumed by simplify-rtx.c (e.g
simplification of a subreg of a zero_extend), parts are specially
accounted for in fwprop (e.g. elimination of unneeded paradoxical subregs).
3) killing recursive calls to fold_rtx. With the changes above, we can
move the recursion from fold_rtx to equiv_constant. This allows to
simplify fold_rtx's loop on the operands.
More chainsawing will come later, with the complete removal of CSE path
following. But we should let the water cool down a bit first.
Bootstrapped/regtested together with the other patch on
i386-pc-linux-gnu. Ok for mainline?
Paolo
2006-02-17 Paolo Bonzini <bonzini@gnu.org>
* cse.c (fold_rtx_subreg, fold_rtx_mem, find_best_addr,
canon_for_address): Remove.
(fold_rtx): Process SUBREGs and MEMs with equiv_constant, make
simplification loop more straightforward by not calling fold_rtx
recursively.
(equiv_constant): Move here a small part of fold_rtx_subreg,
do not call fold_rtx. Call avoid_constant_pool_reference
to process MEMs.
* recog.c (canonicalize_change_group): New.
* recog.h (canonicalize_change_group): New.
Index: cse.c
===================================================================
*** cse.c (revision 111180)
--- cse.c (working copy)
***************
*** 600,606 ****
static unsigned hash_rtx_string (const char *);
static rtx canon_reg (rtx, rtx);
- static void find_best_addr (rtx, rtx *, enum machine_mode);
static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *,
enum machine_mode *,
enum machine_mode *);
--- 600,605 ----
***************
*** 731,787 ****
return cost;
}
- /* Returns a canonical version of X for the address, from the point of view,
- that all multiplications are represented as MULT instead of the multiply
- by a power of 2 being represented as ASHIFT. */
-
- static rtx
- canon_for_address (rtx x)
- {
- enum rtx_code code;
- enum machine_mode mode;
- rtx new = 0;
- int i;
- const char *fmt;
-
- if (!x)
- return x;
-
- code = GET_CODE (x);
- mode = GET_MODE (x);
-
- switch (code)
- {
- case ASHIFT:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode)
- && INTVAL (XEXP (x, 1)) >= 0)
- {
- new = canon_for_address (XEXP (x, 0));
- new = gen_rtx_MULT (mode, new,
- gen_int_mode ((HOST_WIDE_INT) 1
- << INTVAL (XEXP (x, 1)),
- mode));
- }
- break;
- default:
- break;
-
- }
- if (new)
- return new;
-
- /* Now recursively process each operand of this operation. */
- fmt = GET_RTX_FORMAT (code);
- for (i = 0; i < GET_RTX_LENGTH (code); i++)
- if (fmt[i] == 'e')
- {
- new = canon_for_address (XEXP (x, i));
- XEXP (x, i) = new;
- }
- return x;
- }
-
/* Return a negative value if an rtx A, whose costs are given by COST_A
and REGCOST_A, is more desirable than an rtx B.
Return a positive value if A is less desirable, or 0 if the two are
--- 730,735 ----
***************
*** 2822,3052 ****
return x;
}
- /* LOC is a location within INSN that is an operand address (the contents of
- a MEM). Find the best equivalent address to use that is valid for this
- insn.
-
- On most CISC machines, complicated address modes are costly, and rtx_cost
- is a good approximation for that cost. However, most RISC machines have
- only a few (usually only one) memory reference formats. If an address is
- valid at all, it is often just as cheap as any other address. Hence, for
- RISC machines, we use `address_cost' to compare the costs of various
- addresses. For two addresses of equal cost, choose the one with the
- highest `rtx_cost' value as that has the potential of eliminating the
- most insns. For equal costs, we choose the first in the equivalence
- class. Note that we ignore the fact that pseudo registers are cheaper than
- hard registers here because we would also prefer the pseudo registers. */
-
- static void
- find_best_addr (rtx insn, rtx *loc, enum machine_mode mode)
- {
- struct table_elt *elt;
- rtx addr = *loc;
- struct table_elt *p;
- int found_better = 1;
- int save_do_not_record = do_not_record;
- int save_hash_arg_in_memory = hash_arg_in_memory;
- int addr_volatile;
- int regno;
- unsigned hash;
-
- /* Do not try to replace constant addresses or addresses of local and
- argument slots. These MEM expressions 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.
-
- There are some uncommon cases where the address is already in a register
- for some reason, but we cannot take advantage of that because we have
- no easy way to unshare the MEM. In addition, looking up all stack
- addresses is costly. */
- if ((GET_CODE (addr) == PLUS
- && REG_P (XEXP (addr, 0))
- && GET_CODE (XEXP (addr, 1)) == CONST_INT
- && (regno = REGNO (XEXP (addr, 0)),
- regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
- || regno == ARG_POINTER_REGNUM))
- || (REG_P (addr)
- && (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
- || regno == HARD_FRAME_POINTER_REGNUM
- || regno == ARG_POINTER_REGNUM))
- || CONSTANT_ADDRESS_P (addr))
- return;
-
- /* If this address is not simply a register, try to fold it. This will
- sometimes simplify the expression. Many simplifications
- will not be valid, but some, usually applying the associative rule, will
- be valid and produce better code. */
- if (!REG_P (addr))
- {
- rtx folded = canon_for_address (fold_rtx (addr, NULL_RTX));
-
- if (folded != addr)
- {
- int addr_folded_cost = address_cost (folded, mode);
- int addr_cost = address_cost (addr, mode);
-
- if ((addr_folded_cost < addr_cost
- || (addr_folded_cost == addr_cost
- /* ??? The rtx_cost comparison is left over from an older
- version of this code. It is probably no longer helpful.*/
- && (rtx_cost (folded, MEM) > rtx_cost (addr, MEM)
- || approx_reg_cost (folded) < approx_reg_cost (addr))))
- && validate_change (insn, loc, folded, 0))
- addr = folded;
- }
- }
-
- /* If this address is not in the hash table, we can't look for equivalences
- of the whole address. Also, ignore if volatile. */
-
- do_not_record = 0;
- hash = HASH (addr, Pmode);
- addr_volatile = do_not_record;
- do_not_record = save_do_not_record;
- hash_arg_in_memory = save_hash_arg_in_memory;
-
- if (addr_volatile)
- return;
-
- elt = lookup (addr, hash, Pmode);
-
- if (elt)
- {
- /* We need to find the best (under the criteria documented above) entry
- in the class that is valid. We use the `flag' field to indicate
- choices that were invalid and iterate until we can't find a better
- one that hasn't already been tried. */
-
- for (p = elt->first_same_value; p; p = p->next_same_value)
- p->flag = 0;
-
- while (found_better)
- {
- int best_addr_cost = address_cost (*loc, mode);
- int best_rtx_cost = (elt->cost + 1) >> 1;
- int exp_cost;
- struct table_elt *best_elt = elt;
-
- found_better = 0;
- for (p = elt->first_same_value; p; p = p->next_same_value)
- if (! p->flag)
- {
- if ((REG_P (p->exp)
- || exp_equiv_p (p->exp, p->exp, 1, false))
- && ((exp_cost = address_cost (p->exp, mode)) < best_addr_cost
- || (exp_cost == best_addr_cost
- && ((p->cost + 1) >> 1) > best_rtx_cost)))
- {
- found_better = 1;
- best_addr_cost = exp_cost;
- best_rtx_cost = (p->cost + 1) >> 1;
- best_elt = p;
- }
- }
-
- if (found_better)
- {
- if (validate_change (insn, loc,
- canon_reg (copy_rtx (best_elt->exp),
- NULL_RTX), 0))
- return;
- else
- best_elt->flag = 1;
- }
- }
- }
-
- /* If the address is a binary operation with the first operand a register
- and the second a constant, do the same as above, but looking for
- equivalences of the register. Then try to simplify before checking for
- the best address to use. This catches a few cases: First is when we
- have REG+const and the register is another REG+const. We can often merge
- the constants and eliminate one insn and one register. It may also be
- that a machine has a cheap REG+REG+const. Finally, this improves the
- code on the Alpha for unaligned byte stores. */
-
- if (flag_expensive_optimizations
- && ARITHMETIC_P (*loc)
- && REG_P (XEXP (*loc, 0)))
- {
- rtx op1 = XEXP (*loc, 1);
-
- do_not_record = 0;
- hash = HASH (XEXP (*loc, 0), Pmode);
- do_not_record = save_do_not_record;
- hash_arg_in_memory = save_hash_arg_in_memory;
-
- elt = lookup (XEXP (*loc, 0), hash, Pmode);
- if (elt == 0)
- return;
-
- /* We need to find the best (under the criteria documented above) entry
- in the class that is valid. We use the `flag' field to indicate
- choices that were invalid and iterate until we can't find a better
- one that hasn't already been tried. */
-
- for (p = elt->first_same_value; p; p = p->next_same_value)
- p->flag = 0;
-
- while (found_better)
- {
- int best_addr_cost = address_cost (*loc, mode);
- int best_rtx_cost = (COST (*loc) + 1) >> 1;
- struct table_elt *best_elt = elt;
- rtx best_rtx = *loc;
- int count;
-
- /* This is at worst case an O(n^2) algorithm, so limit our search
- to the first 32 elements on the list. This avoids trouble
- compiling code with very long basic blocks that can easily
- call simplify_gen_binary so many times that we run out of
- memory. */
-
- found_better = 0;
- for (p = elt->first_same_value, count = 0;
- p && count < 32;
- p = p->next_same_value, count++)
- if (! p->flag
- && (REG_P (p->exp)
- || (GET_CODE (p->exp) != EXPR_LIST
- && exp_equiv_p (p->exp, p->exp, 1, false))))
-
- {
- rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
- p->exp, op1);
- int new_cost;
-
- /* Get the canonical version of the address so we can accept
- more. */
- new = canon_for_address (new);
-
- new_cost = address_cost (new, mode);
-
- if (new_cost < best_addr_cost
- || (new_cost == best_addr_cost
- && (COST (new) + 1) >> 1 > best_rtx_cost))
- {
- found_better = 1;
- best_addr_cost = new_cost;
- best_rtx_cost = (COST (new) + 1) >> 1;
- best_elt = p;
- best_rtx = new;
- }
- }
-
- if (found_better)
- {
- if (validate_change (insn, loc,
- canon_reg (copy_rtx (best_rtx),
- NULL_RTX), 0))
- return;
- else
- best_elt->flag = 1;
- }
- }
- }
- }
-
/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison
operation (EQ, NE, GT, etc.), follow it back through the hash table and
what values are being compared.
--- 2770,2775 ----
***************
*** 3241,3620 ****
return code;
}
- /* Fold SUBREG. */
-
- static rtx
- fold_rtx_subreg (rtx x, rtx insn)
- {
- enum machine_mode mode = GET_MODE (x);
- rtx folded_arg0;
- rtx const_arg0;
- rtx new;
-
- /* See if we previously assigned a constant value to this SUBREG. */
- if ((new = lookup_as_function (x, CONST_INT)) != 0
- || (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
- return new;
-
- /* If this is a paradoxical SUBREG, we have no idea what value the
- extra bits would have. However, if the operand is equivalent to
- a SUBREG whose operand is the same as our mode, and all the modes
- are within a word, we can just use the inner operand because
- these SUBREGs just say how to treat the register.
-
- Similarly if we find an integer constant. */
-
- if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- {
- enum machine_mode imode = GET_MODE (SUBREG_REG (x));
- struct table_elt *elt;
-
- if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
- && GET_MODE_SIZE (imode) <= UNITS_PER_WORD
- && (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode),
- imode)) != 0)
- for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
- {
- if (CONSTANT_P (elt->exp)
- && GET_MODE (elt->exp) == VOIDmode)
- return elt->exp;
-
- if (GET_CODE (elt->exp) == SUBREG
- && GET_MODE (SUBREG_REG (elt->exp)) == mode
- && exp_equiv_p (elt->exp, elt->exp, 1, false))
- return copy_rtx (SUBREG_REG (elt->exp));
- }
-
- return x;
- }
-
- /* Fold SUBREG_REG. If it changed, see if we can simplify the
- SUBREG. We might be able to if the SUBREG is extracting a single
- word in an integral mode or extracting the low part. */
! folded_arg0 = fold_rtx (SUBREG_REG (x), insn);
! const_arg0 = equiv_constant (folded_arg0);
! if (const_arg0)
! folded_arg0 = const_arg0;
!
! if (folded_arg0 != SUBREG_REG (x))
! {
! new = simplify_subreg (mode, folded_arg0,
! GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
! if (new)
! return new;
! }
!
! if (REG_P (folded_arg0)
! && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0)))
! {
! struct table_elt *elt;
!
! elt = lookup (folded_arg0,
! HASH (folded_arg0, GET_MODE (folded_arg0)),
! GET_MODE (folded_arg0));
!
! if (elt)
! elt = elt->first_same_value;
!
! if (subreg_lowpart_p (x))
! /* If this is a narrowing SUBREG and our operand is a REG, see
! if we can find an equivalence for REG that is an arithmetic
! operation in a wider mode where both operands are
! paradoxical SUBREGs from objects of our result mode. In
! that case, we couldn-t report an equivalent value for that
! operation, since we don't know what the extra bits will be.
! But we can find an equivalence for this SUBREG by folding
! that operation in the narrow mode. This allows us to fold
! arithmetic in narrow modes when the machine only supports
! word-sized arithmetic.
!
! Also look for a case where we have a SUBREG whose operand
! is the same as our result. If both modes are smaller than
! a word, we are simply interpreting a register in different
! modes and we can use the inner value. */
!
! for (; elt; elt = elt->next_same_value)
! {
! enum rtx_code eltcode = GET_CODE (elt->exp);
!
! /* Just check for unary and binary operations. */
! if (UNARY_P (elt->exp)
! && eltcode != SIGN_EXTEND
! && eltcode != ZERO_EXTEND
! && GET_CODE (XEXP (elt->exp, 0)) == SUBREG
! && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode
! && (GET_MODE_CLASS (mode)
! == GET_MODE_CLASS (GET_MODE (XEXP (elt->exp, 0)))))
! {
! rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
!
! if (!REG_P (op0) && ! CONSTANT_P (op0))
! op0 = fold_rtx (op0, NULL_RTX);
!
! op0 = equiv_constant (op0);
! if (op0)
! new = simplify_unary_operation (GET_CODE (elt->exp), mode,
! op0, mode);
! }
! else if (ARITHMETIC_P (elt->exp)
! && eltcode != DIV && eltcode != MOD
! && eltcode != UDIV && eltcode != UMOD
! && eltcode != ASHIFTRT && eltcode != LSHIFTRT
! && eltcode != ROTATE && eltcode != ROTATERT
! && ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG
! && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0)))
! == mode))
! || CONSTANT_P (XEXP (elt->exp, 0)))
! && ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG
! && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1)))
! == mode))
! || CONSTANT_P (XEXP (elt->exp, 1))))
! {
! rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
! rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
!
! if (op0 && !REG_P (op0) && ! CONSTANT_P (op0))
! op0 = fold_rtx (op0, NULL_RTX);
!
! if (op0)
! op0 = equiv_constant (op0);
!
! if (op1 && !REG_P (op1) && ! CONSTANT_P (op1))
! op1 = fold_rtx (op1, NULL_RTX);
!
! if (op1)
! op1 = equiv_constant (op1);
!
! /* If we are looking for the low SImode part of
! (ashift:DI c (const_int 32)), it doesn't work to
! compute that in SImode, because a 32-bit shift in
! SImode is unpredictable. We know the value is
! 0. */
! if (op0 && op1
! && GET_CODE (elt->exp) == ASHIFT
! && GET_CODE (op1) == CONST_INT
! && INTVAL (op1) >= GET_MODE_BITSIZE (mode))
! {
! if (INTVAL (op1)
! < GET_MODE_BITSIZE (GET_MODE (elt->exp)))
! /* If the count fits in the inner mode's width,
! but exceeds the outer mode's width, the value
! will get truncated to 0 by the subreg. */
! new = CONST0_RTX (mode);
! else
! /* If the count exceeds even the inner mode's width,
! don't fold this expression. */
! new = 0;
! }
! else if (op0 && op1)
! new = simplify_binary_operation (GET_CODE (elt->exp),
! mode, op0, op1);
! }
!
! else if (GET_CODE (elt->exp) == SUBREG
! && GET_MODE (SUBREG_REG (elt->exp)) == mode
! && (GET_MODE_SIZE (GET_MODE (folded_arg0))
! <= UNITS_PER_WORD)
! && exp_equiv_p (elt->exp, elt->exp, 1, false))
! new = copy_rtx (SUBREG_REG (elt->exp));
!
! if (new)
! return new;
! }
! else
! /* A SUBREG resulting from a zero extension may fold to zero
! if it extracts higher bits than the ZERO_EXTEND's source
! bits. FIXME: if combine tried to, er, combine these
! instructions, this transformation may be moved to
! simplify_subreg. */
! for (; elt; elt = elt->next_same_value)
! {
! if (GET_CODE (elt->exp) == ZERO_EXTEND
! && subreg_lsb (x)
! >= GET_MODE_BITSIZE (GET_MODE (XEXP (elt->exp, 0))))
! return CONST0_RTX (mode);
! }
! }
!
! return x;
! }
!
! /* Fold MEM. */
!
! static rtx
! fold_rtx_mem (rtx x, rtx insn)
! {
! enum machine_mode mode = GET_MODE (x);
! rtx new;
!
! /* If we are not actually processing an insn, don't try to find the
! best address. Not only don't we care, but we could modify the
! MEM in an invalid way since we have no insn to validate
! against. */
! if (insn != 0)
! find_best_addr (insn, &XEXP (x, 0), mode);
!
! {
! /* Even if we don't fold in the insn itself, we can safely do so
! here, in hopes of getting a constant. */
! rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
! rtx base = 0;
! HOST_WIDE_INT offset = 0;
!
! if (REG_P (addr)
! && REGNO_QTY_VALID_P (REGNO (addr)))
! {
! int addr_q = REG_QTY (REGNO (addr));
! struct qty_table_elem *addr_ent = &qty_table[addr_q];
!
! if (GET_MODE (addr) == addr_ent->mode
! && addr_ent->const_rtx != NULL_RTX)
! addr = addr_ent->const_rtx;
! }
!
! /* Call target hook to avoid the effects of -fpic etc.... */
! addr = targetm.delegitimize_address (addr);
!
! /* If address is constant, split it into a base and integer
! offset. */
! if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
! base = addr;
! else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
! && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
! {
! base = XEXP (XEXP (addr, 0), 0);
! offset = INTVAL (XEXP (XEXP (addr, 0), 1));
! }
! else if (GET_CODE (addr) == LO_SUM
! && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
! base = XEXP (addr, 1);
!
! /* If this is a constant pool reference, we can fold it into its
! constant to allow better value tracking. */
! if (base && GET_CODE (base) == SYMBOL_REF
! && CONSTANT_POOL_ADDRESS_P (base))
! {
! rtx constant = get_pool_constant (base);
! enum machine_mode const_mode = get_pool_mode (base);
! rtx new;
!
! if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
! {
! constant_pool_entries_cost = COST (constant);
! constant_pool_entries_regcost = approx_reg_cost (constant);
! }
!
! /* If we are loading the full constant, we have an
! equivalence. */
! if (offset == 0 && mode == const_mode)
! return constant;
!
! /* If this actually isn't a constant (weird!), we can't do
! anything. Otherwise, handle the two most common cases:
! extracting a word from a multi-word constant, and
! extracting the low-order bits. Other cases don't seem
! common enough to worry about. */
! if (! CONSTANT_P (constant))
! return x;
!
! if (GET_MODE_CLASS (mode) == MODE_INT
! && GET_MODE_SIZE (mode) == UNITS_PER_WORD
! && offset % UNITS_PER_WORD == 0
! && (new = operand_subword (constant,
! offset / UNITS_PER_WORD,
! 0, const_mode)) != 0)
! return new;
!
! if (((BYTES_BIG_ENDIAN
! && offset == GET_MODE_SIZE (GET_MODE (constant)) - 1)
! || (! BYTES_BIG_ENDIAN && offset == 0))
! && (new = gen_lowpart (mode, constant)) != 0)
! return new;
! }
!
! /* If this is a reference to a label at a known position in a jump
! table, we also know its value. */
! if (base && GET_CODE (base) == LABEL_REF)
! {
! rtx label = XEXP (base, 0);
! rtx table_insn = NEXT_INSN (label);
!
! if (table_insn && JUMP_P (table_insn)
! && GET_CODE (PATTERN (table_insn)) == ADDR_VEC)
! {
! rtx table = PATTERN (table_insn);
!
! if (offset >= 0
! && (offset / GET_MODE_SIZE (GET_MODE (table))
! < XVECLEN (table, 0)))
! {
! rtx label = XVECEXP
! (table, 0, offset / GET_MODE_SIZE (GET_MODE (table)));
! rtx set;
!
! /* If we have an insn that loads the label from the
! jumptable into a reg, we don't want to set the reg
! to the label, because this may cause a reference to
! the label to remain after the label is removed in
! some very obscure cases (PR middle-end/18628). */
! if (!insn)
! return label;
!
! set = single_set (insn);
!
! if (! set || SET_SRC (set) != x)
! return x;
!
! /* If it's a jump, it's safe to reference the label. */
! if (SET_DEST (set) == pc_rtx)
! return label;
!
! return x;
! }
! }
! if (table_insn && JUMP_P (table_insn)
! && GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC)
! {
! rtx table = PATTERN (table_insn);
!
! if (offset >= 0
! && (offset / GET_MODE_SIZE (GET_MODE (table))
! < XVECLEN (table, 1)))
! {
! offset /= GET_MODE_SIZE (GET_MODE (table));
! new = gen_rtx_MINUS (Pmode, XVECEXP (table, 1, offset),
! XEXP (table, 0));
!
! if (GET_MODE (table) != Pmode)
! new = gen_rtx_TRUNCATE (GET_MODE (table), new);
!
! /* Indicate this is a constant. This isn't a valid
! form of CONST, but it will only be used to fold the
! next insns and then discarded, so it should be
! safe.
!
! Note this expression must be explicitly discarded,
! by cse_insn, else it may end up in a REG_EQUAL note
! and "escape" to cause problems elsewhere. */
! return gen_rtx_CONST (GET_MODE (new), new);
! }
! }
! }
!
! 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 here. equiv_constant is called to
! perform that task.
INSN is the insn that we may be modifying. If it is 0, make a copy
of X before modifying it. */
--- 2964,2978 ----
return code;
}
! /* If X is a nontrivial arithmetic operation on an argument for which
! a constant value can be determined, return the result of operating
! on that value, as a constant. Otherwise, return X, possibly with
! one or more operands changed to a forward-propagated constant.
!
! If X is a register whose contents are known, we do NOT return
! those contents here; equiv_constant is called to perform that task.
! For SUBREGs and MEMs, we do that both here and in equiv_constant.
INSN is the insn that we may be modifying. If it is 0, make a copy
of X before modifying it. */
***************
*** 3627,3636 ****
const char *fmt;
int i;
rtx new = 0;
! int copied = 0;
! int must_swap = 0;
! /* Folded equivalents of first two operands of X. */
rtx folded_arg0;
rtx folded_arg1;
--- 2985,2993 ----
const char *fmt;
int i;
rtx new = 0;
! int changed = 0;
! /* Operands of X. */
rtx folded_arg0;
rtx folded_arg1;
***************
*** 3647,3656 ****
if (x == 0)
return x;
! mode = GET_MODE (x);
code = GET_CODE (x);
switch (code)
{
case CONST:
case CONST_INT:
case CONST_DOUBLE:
--- 3004,3019 ----
if (x == 0)
return x;
! /* Try to perform some initial simplifications on X. */
code = GET_CODE (x);
switch (code)
{
+ case MEM:
+ case SUBREG:
+ if ((new = equiv_constant (x)) != NULL_RTX)
+ return new;
+ return x;
+
case CONST:
case CONST_INT:
case CONST_DOUBLE:
***************
*** 3670,3697 ****
return prev_insn_cc0;
#endif
- case SUBREG:
- return fold_rtx_subreg (x, insn);
-
- case NOT:
- case NEG:
- /* If we have (NOT Y), see if Y is known to be (NOT Z).
- If so, (NOT Y) simplifies to Z. Similarly for NEG. */
- new = lookup_as_function (XEXP (x, 0), code);
- if (new)
- return fold_rtx (copy_rtx (XEXP (new, 0)), insn);
- break;
-
- case MEM:
- return fold_rtx_mem (x, insn);
-
- #ifdef NO_FUNCTION_CSE
- case CALL:
- if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
- return x;
- break;
- #endif
-
case ASM_OPERANDS:
if (insn)
{
--- 3033,3038 ----
***************
*** 3699,3710 ****
--- 3040,3060 ----
validate_change (insn, &ASM_OPERANDS_INPUT (x, i),
fold_rtx (ASM_OPERANDS_INPUT (x, i), insn), 0);
}
+ return x;
+
+ #ifdef NO_FUNCTION_CSE
+ case CALL:
+ if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
+ return x;
break;
+ #endif
+ /* Anything else goes through the loop below. */
default:
break;
}
+ mode = GET_MODE (x);
const_arg0 = 0;
const_arg1 = 0;
const_arg2 = 0;
***************
*** 3717,3771 ****
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
! rtx arg = XEXP (x, i);
! rtx folded_arg = arg, const_arg = 0;
! enum machine_mode mode_arg = GET_MODE (arg);
! rtx cheap_arg, expensive_arg;
! rtx replacements[2];
! int j;
! int old_cost = COST_IN (XEXP (x, i), code);
!
! /* Most arguments are cheap, so handle them specially. */
! switch (GET_CODE (arg))
! {
! case REG:
! /* This is the same as calling equiv_constant; it is duplicated
! here for speed. */
! if (REGNO_QTY_VALID_P (REGNO (arg)))
! {
! int arg_q = REG_QTY (REGNO (arg));
! struct qty_table_elem *arg_ent = &qty_table[arg_q];
!
! if (arg_ent->const_rtx != NULL_RTX
! && !REG_P (arg_ent->const_rtx)
! && GET_CODE (arg_ent->const_rtx) != PLUS)
! const_arg
! = gen_lowpart (GET_MODE (arg),
! arg_ent->const_rtx);
! }
! break;
!
! case CONST:
! case CONST_INT:
! case SYMBOL_REF:
! case LABEL_REF:
! case CONST_DOUBLE:
! case CONST_VECTOR:
! const_arg = arg;
! break;
!
#ifdef HAVE_cc0
! case CC0:
! folded_arg = prev_insn_cc0;
! mode_arg = prev_insn_cc0_mode;
! const_arg = equiv_constant (folded_arg);
! break;
#endif
!
! default:
! folded_arg = fold_rtx (arg, insn);
! const_arg = equiv_constant (folded_arg);
! }
/* For the first three operands, see if the operand
is constant or equivalent to a constant. */
--- 3067,3079 ----
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
! rtx folded_arg = XEXP (x, i), const_arg;
! enum machine_mode mode_arg = GET_MODE (folded_arg);
#ifdef HAVE_cc0
! if (CC0_P (folded_arg))
! folded_arg = prev_insn_cc0, mode_arg = prev_insn_cc0_mode;
#endif
! const_arg = equiv_constant (folded_arg);
/* For the first three operands, see if the operand
is constant or equivalent to a constant. */
***************
*** 3785,3904 ****
break;
}
! /* Pick the least expensive of the folded argument and an
! equivalent constant argument. */
! if (const_arg == 0 || const_arg == folded_arg
! || COST_IN (const_arg, code) > COST_IN (folded_arg, code))
! cheap_arg = folded_arg, expensive_arg = const_arg;
! else
! cheap_arg = const_arg, expensive_arg = folded_arg;
!
! /* Try to replace the operand with the cheapest of the two
! possibilities. If it doesn't work and this is either of the first
! two operands of a commutative operation, try swapping them.
! If THAT fails, try the more expensive, provided it is cheaper
! than what is already there. */
!
! if (cheap_arg == XEXP (x, i))
! continue;
!
! if (insn == 0 && ! copied)
! {
! x = copy_rtx (x);
! copied = 1;
! }
!
! /* Order the replacements from cheapest to most expensive. */
! replacements[0] = cheap_arg;
! replacements[1] = expensive_arg;
!
! for (j = 0; j < 2 && replacements[j]; j++)
! {
! int new_cost = COST_IN (replacements[j], code);
!
! /* Stop if what existed before was cheaper. Prefer constants
! in the case of a tie. */
! if (new_cost > old_cost
! || (new_cost == old_cost && CONSTANT_P (XEXP (x, i))))
! break;
/* It's not safe to substitute the operand of a conversion
operator with a constant, as the conversion's identity
depends upon the mode of its operand. This optimization
is handled by the call to simplify_unary_operation. */
! if (GET_RTX_CLASS (code) == RTX_UNARY
! && GET_MODE (replacements[j]) != mode_arg0
! && (code == ZERO_EXTEND
! || code == SIGN_EXTEND
! || code == TRUNCATE
! || code == FLOAT_TRUNCATE
! || code == FLOAT_EXTEND
! || code == FLOAT
! || code == FIX
! || code == UNSIGNED_FLOAT
! || code == UNSIGNED_FIX))
! continue;
!
! if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
! break;
! if (GET_RTX_CLASS (code) == RTX_COMM_COMPARE
! || GET_RTX_CLASS (code) == RTX_COMM_ARITH)
! {
! validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1);
! validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1);
!
! if (apply_change_group ())
! {
! /* Swap them back to be invalid so that this loop can
! continue and flag them to be swapped back later. */
! rtx tem;
!
! tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1);
! XEXP (x, 1) = tem;
! must_swap = 1;
! break;
! }
! }
! }
! }
! else
! {
! if (fmt[i] == 'E')
! /* Don't try to fold inside of a vector of expressions.
! Doing nothing 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. */
!
! if (COMMUTATIVE_P (x))
{
! if (must_swap
! || swap_commutative_operands_p (const_arg0 ? const_arg0
! : XEXP (x, 0),
! const_arg1 ? const_arg1
! : XEXP (x, 1)))
! {
! rtx tem = XEXP (x, 0);
!
! if (insn == 0 && ! copied)
! {
! x = copy_rtx (x);
! copied = 1;
! }
!
! validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
! validate_change (insn, &XEXP (x, 1), tem, 1);
! if (apply_change_group ())
! {
! tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
! tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
! }
}
}
/* If X is an arithmetic operation, see if we can simplify it. */
--- 3093,3142 ----
break;
}
! /* Pick the least expensive of the argument and an equivalent constant
! argument. */
! if (const_arg != 0
! && const_arg != folded_arg
! && COST_IN (const_arg, code) <= COST_IN (folded_arg, code)
/* It's not safe to substitute the operand of a conversion
operator with a constant, as the conversion's identity
depends upon the mode of its operand. This optimization
is handled by the call to simplify_unary_operation. */
! && (GET_RTX_CLASS (code) != RTX_UNARY
! || GET_MODE (const_arg) == mode_arg0
! || (code != ZERO_EXTEND
! && code != SIGN_EXTEND
! && code != TRUNCATE
! && code != FLOAT_TRUNCATE
! && code != FLOAT_EXTEND
! && code != FLOAT
! && code != FIX
! && code != UNSIGNED_FLOAT
! && code != UNSIGNED_FIX)))
! folded_arg = const_arg;
! if (folded_arg == XEXP (x, i))
! continue;
! if (insn == NULL_RTX && !changed)
! x = copy_rtx (x);
! changed = 1;
! validate_change (insn, &XEXP (x, i), folded_arg, 1);
}
! if (changed)
{
! /* Canonicalize X if necessary, and keep const_argN and folded_argN
! consistent with the order in X. */
! if (canonicalize_change_group (insn, x))
! {
! rtx tem;
! tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
! tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
}
+
+ apply_change_group ();
}
/* If X is an arithmetic operation, see if we can simplify it. */
***************
*** 4403,4418 ****
if (x == 0 || CONSTANT_P (x))
return x;
! /* If X is a MEM, try to fold it outside the context of any insn to see if
! it might be equivalent to a constant. That handles the case where it
! is a constant-pool reference. Then try to look it up in the hash table
! in case it is something whose value we have seen before. */
if (MEM_P (x))
{
struct table_elt *elt;
! x = fold_rtx (x, NULL_RTX);
if (CONSTANT_P (x))
return x;
--- 3641,3671 ----
if (x == 0 || CONSTANT_P (x))
return x;
! if (GET_CODE (x) == SUBREG)
! {
! rtx new;
!
! /* See if we previously assigned a constant value to this SUBREG. */
! if ((new = lookup_as_function (x, CONST_INT)) != 0
! || (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
! return new;
!
! if (REG_P (SUBREG_REG (x))
! && (new = equiv_constant (SUBREG_REG (x))) != 0)
! return simplify_subreg (GET_MODE (x), SUBREG_REG (x),
! GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
!
! return 0;
! }
!
! /* If X is a MEM, see if it is a constant-pool reference, or look it up in
! the hash table in case its value was seen before. */
if (MEM_P (x))
{
struct table_elt *elt;
! x = avoid_constant_pool_reference (x);
if (CONSTANT_P (x))
return x;
Index: recog.c
===================================================================
*** recog.c (revision 111180)
--- recog.c (working copy)
***************
*** 237,242 ****
--- 237,264 ----
return apply_change_group ();
}
+ /* Keep X canonicalized if some changes have made it non-canonical; only
+ modifies the operands of X, not (for example) its code. Simplifications
+ are not the job of this routine.
+
+ Return true if anything was changed. */
+ bool
+ canonicalize_change_group (rtx insn, rtx x)
+ {
+ if (COMMUTATIVE_P (x)
+ && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
+ {
+ /* Oops, the caller has made X no longer canonical.
+ Let's redo the changes in the correct order. */
+ rtx tem = XEXP (x, 0);
+ validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+ validate_change (insn, &XEXP (x, 1), tem, 1);
+ return true;
+ }
+ else
+ return false;
+ }
+
/* Function to be passed to for_each_rtx to test whether a piece of
RTL contains any mem/v. */
Index: recog.h
===================================================================
*** recog.h (revision 111180)
--- recog.h (working copy)
***************
*** 75,80 ****
--- 75,81 ----
extern int asm_operand_ok (rtx, const char *);
extern int validate_change (rtx, rtx *, rtx, int);
extern int validate_change_maybe_volatile (rtx, rtx *, rtx);
+ extern bool canonicalize_change_group (rtx insn, rtx x);
extern int insn_invalid_p (rtx);
extern int verify_changes (int);
extern void confirm_change_group (void);