diff options
Diffstat (limited to 'gcc/combine.c')
-rw-r--r-- | gcc/combine.c | 137 |
1 files changed, 108 insertions, 29 deletions
diff --git a/gcc/combine.c b/gcc/combine.c index e860b4fc4d3..f6ae431ee70 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -90,6 +90,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "real.h" #include "toplev.h" #include "target.h" +#include "params.h" #ifndef SHIFT_COUNT_TRUNCATED #define SHIFT_COUNT_TRUNCATED 0 @@ -3417,10 +3418,10 @@ subst (rtx x, rtx from, rtx to, int in_dest, int unique_copy) /* If this is a register being set, ignore it. */ new = XEXP (x, i); if (in_dest - && (code == SUBREG || code == STRICT_LOW_PART - || code == ZERO_EXTRACT) && i == 0 - && GET_CODE (new) == REG) + && (((code == SUBREG || code == ZERO_EXTRACT) + && GET_CODE (new) == REG) + || code == STRICT_LOW_PART)) ; else if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from)) @@ -10688,34 +10689,61 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1) break; case SUBREG: - /* Check for the case where we are comparing A - C1 with C2, - both constants are smaller than 1/2 the maximum positive - value in MODE, and the comparison is equality or unsigned. - In that case, if A is either zero-extended to MODE or has - sufficient sign bits so that the high-order bit in MODE - is a copy of the sign in the inner mode, we can prove that it is - safe to do the operation in the wider mode. This simplifies - many range checks. */ + /* Check for the case where we are comparing A - C1 with C2, that is + + (subreg:MODE (plus (A) (-C1))) op (C2) + + with C1 a constant, and try to lift the SUBREG, i.e. to do the + comparison in the wider mode. One of the following two conditions + must be true in order for this to be valid: + + 1. The mode extension results in the same bit pattern being added + on both sides and the comparison is equality or unsigned. As + C2 has been truncated to fit in MODE, the pattern can only be + all 0s or all 1s. + + 2. The mode extension results in the sign bit being copied on + each side. + + The difficulty here is that we have predicates for A but not for + (A - C1) so we need to check that C1 is within proper bounds so + as to perturbate A as little as possible. */ if (mode_width <= HOST_BITS_PER_WIDE_INT && subreg_lowpart_p (op0) + && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) > mode_width && GET_CODE (SUBREG_REG (op0)) == PLUS - && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT - && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0 - && (-INTVAL (XEXP (SUBREG_REG (op0), 1)) - < (HOST_WIDE_INT) (GET_MODE_MASK (mode) / 2)) - && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2 - && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0), - GET_MODE (SUBREG_REG (op0))) - & ~GET_MODE_MASK (mode)) - || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0), - GET_MODE (SUBREG_REG (op0))) - > (unsigned int) - (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) - - GET_MODE_BITSIZE (mode))))) - { - op0 = SUBREG_REG (op0); - continue; + && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT) + { + enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0)); + rtx a = XEXP (SUBREG_REG (op0), 0); + HOST_WIDE_INT c1 = -INTVAL (XEXP (SUBREG_REG (op0), 1)); + + if ((c1 > 0 + && (unsigned HOST_WIDE_INT) c1 + < (unsigned HOST_WIDE_INT) 1 << (mode_width - 1) + && (equality_comparison_p || unsigned_comparison_p) + /* (A - C1) zero-extends if it is positive and sign-extends + if it is negative, C2 both zero- and sign-extends. */ + && ((0 == (nonzero_bits (a, inner_mode) + & ~GET_MODE_MASK (mode)) + && const_op >= 0) + /* (A - C1) sign-extends if it is positive and 1-extends + if it is negative, C2 both sign- and 1-extends. */ + || (num_sign_bit_copies (a, inner_mode) + > (unsigned int) (GET_MODE_BITSIZE (inner_mode) + - mode_width) + && const_op < 0))) + || ((unsigned HOST_WIDE_INT) c1 + < (unsigned HOST_WIDE_INT) 1 << (mode_width - 2) + /* (A - C1) always sign-extends, like C2. */ + && num_sign_bit_copies (a, inner_mode) + > (unsigned int) (GET_MODE_BITSIZE (inner_mode) + - mode_width - 1))) + { + op0 = SUBREG_REG (op0); + continue; + } } /* If the inner mode is narrower and we are extracting the low part, @@ -11353,6 +11381,47 @@ reversed_comparison (rtx exp, enum machine_mode mode, rtx op0, rtx op1) return gen_binary (reversed_code, mode, op0, op1); } +/* Utility function for record_value_for_reg. Count number of + rtxs in X. */ +static int +count_rtxs (rtx x) +{ + enum rtx_code code = GET_CODE (x); + const char *fmt; + int i, ret = 1; + + if (GET_RTX_CLASS (code) == '2' + || GET_RTX_CLASS (code) == 'c') + { + rtx x0 = XEXP (x, 0); + rtx x1 = XEXP (x, 1); + + if (x0 == x1) + return 1 + 2 * count_rtxs (x0); + + if ((GET_RTX_CLASS (GET_CODE (x1)) == '2' + || GET_RTX_CLASS (GET_CODE (x1)) == 'c') + && (x0 == XEXP (x1, 0) || x0 == XEXP (x1, 1))) + return 2 + 2 * count_rtxs (x0) + + count_rtxs (x == XEXP (x1, 0) + ? XEXP (x1, 1) : XEXP (x1, 0)); + + if ((GET_RTX_CLASS (GET_CODE (x0)) == '2' + || GET_RTX_CLASS (GET_CODE (x0)) == 'c') + && (x1 == XEXP (x0, 0) || x1 == XEXP (x0, 1))) + return 2 + 2 * count_rtxs (x1) + + count_rtxs (x == XEXP (x0, 0) + ? XEXP (x0, 1) : XEXP (x0, 0)); + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + ret += count_rtxs (XEXP (x, i)); + + return ret; +} + /* Utility function for following routine. Called when X is part of a value being stored into reg_last_set_value. Sets reg_last_set_table_tick for each register mentioned. Similar to mention_regs in cse.c */ @@ -11459,6 +11528,13 @@ record_value_for_reg (rtx reg, rtx insn, rtx value) && GET_CODE (XEXP (tem, 0)) == CLOBBER && GET_CODE (XEXP (tem, 1)) == CLOBBER) tem = XEXP (tem, 0); + else if (count_occurrences (value, reg, 1) >= 2) + { + /* If there are two or more occurrences of REG in VALUE, + prevent the value from growing too much. */ + if (count_rtxs (tem) > MAX_LAST_VALUE_RTL) + tem = gen_rtx_CLOBBER (GET_MODE (tem), const0_rtx); + } value = replace_rtx (copy_rtx (value), reg, tem); } @@ -12502,6 +12578,7 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2) seem worth the trouble. */ if (from_insn == i3 + && PATTERN(i3) != pc_rtx && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0)))) place = i3; break; @@ -12576,7 +12653,7 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2) /* If the insn previously containing this note still exists, put it back where it was. Otherwise move it to the previous insn. Adjust the corresponding REG_LIBCALL note. */ - if (GET_CODE (from_insn) != NOTE) + if (GET_CODE (from_insn) != NOTE && PATTERN (from_insn) != pc_rtx) place = from_insn; else { @@ -12596,7 +12673,7 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2) case REG_LIBCALL: /* This is handled similarly to REG_RETVAL. */ - if (GET_CODE (from_insn) != NOTE) + if (GET_CODE (from_insn) != NOTE && PATTERN (from_insn) != pc_rtx) place = from_insn; else { @@ -12702,6 +12779,8 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2) distribute_notes (old_notes, tem, tem, NULL_RTX); distribute_links (LOG_LINKS (tem)); + if (REG_NOTES (tem)) + abort (); PUT_CODE (tem, NOTE); NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED; |