X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fsimplify-rtx.c;h=2f7ae25c57e6158e0e1b81def139ef891428d2a6;hp=10a9047d739418bcf6f9389d13b0f45e3658c1dd;hb=bce471493dbd17218e3acbc35ee809986ba855c0;hpb=d632b59af06c9aa953ba889b1ecfe6c40fe6a2c1 diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index 10a9047d739..2f7ae25c57e 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -1,6 +1,6 @@ /* RTL simplification functions for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GCC. @@ -50,12 +50,19 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0)) static rtx neg_const_int (enum machine_mode, rtx); +static bool plus_minus_operand_p (rtx); static int simplify_plus_minus_op_data_cmp (const void *, const void *); static rtx simplify_plus_minus (enum rtx_code, enum machine_mode, rtx, rtx, int); -static bool associative_constant_p (rtx); +static rtx simplify_immed_subreg (enum machine_mode, rtx, enum machine_mode, + unsigned int); static rtx simplify_associative_operation (enum rtx_code, enum machine_mode, rtx, rtx); +static rtx simplify_relational_operation_1 (enum rtx_code, enum machine_mode, + enum machine_mode, rtx, rtx); +static rtx simplify_unary_operation_1 (enum rtx_code, enum machine_mode, rtx); +static rtx simplify_binary_operation_1 (enum rtx_code, enum machine_mode, + rtx, rtx, rtx, rtx); /* Negate a CONST_INT rtx, truncating (because a conversion from a maximally negative number can overflow). */ @@ -65,6 +72,39 @@ neg_const_int (enum machine_mode mode, rtx i) return gen_int_mode (- INTVAL (i), mode); } +/* Test whether expression, X, is an immediate constant that represents + the most significant bit of machine mode MODE. */ + +bool +mode_signbit_p (enum machine_mode mode, rtx x) +{ + unsigned HOST_WIDE_INT val; + unsigned int width; + + if (GET_MODE_CLASS (mode) != MODE_INT) + return false; + + width = GET_MODE_BITSIZE (mode); + if (width == 0) + return false; + + if (width <= HOST_BITS_PER_WIDE_INT + && GET_CODE (x) == CONST_INT) + val = INTVAL (x); + else if (width <= 2 * HOST_BITS_PER_WIDE_INT + && GET_CODE (x) == CONST_DOUBLE + && CONST_DOUBLE_LOW (x) == 0) + { + val = CONST_DOUBLE_HIGH (x); + width -= HOST_BITS_PER_WIDE_INT; + } + else + return false; + + if (width < HOST_BITS_PER_WIDE_INT) + val &= ((unsigned HOST_WIDE_INT) 1 << width) - 1; + return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1)); +} /* Make a binary operation by properly ordering the operands and seeing if the expression folds. */ @@ -76,7 +116,7 @@ simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0, rtx tem; /* Put complex operands first and constants second if commutative. */ - if (GET_RTX_CLASS (code) == 'c' + if (GET_RTX_CLASS (code) == RTX_COMM_ARITH && swap_commutative_operands_p (op0, op1)) tem = op0, op0 = op1, op1 = tem; @@ -131,7 +171,7 @@ avoid_constant_pool_reference (rtx x) addr = XEXP (x, 0); /* Call target hook to avoid the effects of -fpic etc.... */ - addr = (*targetm.delegitimize_address) (addr); + addr = targetm.delegitimize_address (addr); if (GET_CODE (addr) == LO_SUM) addr = XEXP (addr, 1); @@ -186,10 +226,9 @@ simplify_gen_ternary (enum rtx_code code, enum machine_mode mode, return gen_rtx_fmt_eee (code, mode, op0, op1, op2); } - + /* Likewise, for relational operations. - CMP_MODE specifies mode comparison is done in. - */ + CMP_MODE specifies mode comparison is done in. */ rtx simplify_gen_relational (enum rtx_code code, enum machine_mode mode, @@ -197,129 +236,78 @@ simplify_gen_relational (enum rtx_code code, enum machine_mode mode, { rtx tem; - if (cmp_mode == VOIDmode) - cmp_mode = GET_MODE (op0); - if (cmp_mode == VOIDmode) - cmp_mode = GET_MODE (op1); - - if (cmp_mode != VOIDmode) - { - tem = simplify_relational_operation (code, cmp_mode, op0, op1); - - if (tem) - { -#ifdef FLOAT_STORE_FLAG_VALUE - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - { - REAL_VALUE_TYPE val; - if (tem == const0_rtx) - return CONST0_RTX (mode); - if (tem != const_true_rtx) - abort (); - val = FLOAT_STORE_FLAG_VALUE (mode); - return CONST_DOUBLE_FROM_REAL_VALUE (val, mode); - } -#endif - return tem; - } - } - - /* For the following tests, ensure const0_rtx is op1. */ - if (swap_commutative_operands_p (op0, op1) - || (op0 == const0_rtx && op1 != const0_rtx)) - tem = op0, op0 = op1, op1 = tem, code = swap_condition (code); - - /* If op0 is a compare, extract the comparison arguments from it. */ - if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) - return simplify_gen_relational (code, mode, VOIDmode, - XEXP (op0, 0), XEXP (op0, 1)); - - /* If op0 is a comparison, extract the comparison arguments form it. */ - if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && op1 == const0_rtx) - { - if (code == NE) - { - if (GET_MODE (op0) == mode) - return op0; - return simplify_gen_relational (GET_CODE (op0), mode, VOIDmode, - XEXP (op0, 0), XEXP (op0, 1)); - } - else if (code == EQ) - { - enum rtx_code new = reversed_comparison_code (op0, NULL_RTX); - if (new != UNKNOWN) - return simplify_gen_relational (new, mode, VOIDmode, - XEXP (op0, 0), XEXP (op0, 1)); - } - } + if (0 != (tem = simplify_relational_operation (code, mode, cmp_mode, + op0, op1))) + return tem; return gen_rtx_fmt_ee (code, mode, op0, op1); } -/* Replace all occurrences of OLD in X with NEW and try to simplify the +/* Replace all occurrences of OLD_RTX in X with NEW_RTX and try to simplify the resulting RTX. Return a new RTX which is as simplified as possible. */ rtx -simplify_replace_rtx (rtx x, rtx old, rtx new) +simplify_replace_rtx (rtx x, rtx old_rtx, rtx new_rtx) { enum rtx_code code = GET_CODE (x); enum machine_mode mode = GET_MODE (x); enum machine_mode op_mode; rtx op0, op1, op2; - /* If X is OLD, return NEW. Otherwise, if this is an expression, try + /* If X is OLD_RTX, return NEW_RTX. Otherwise, if this is an expression, try to build a new expression substituting recursively. If we can't do anything, return our input. */ - if (x == old) - return new; + if (x == old_rtx) + return new_rtx; switch (GET_RTX_CLASS (code)) { - case '1': + case RTX_UNARY: op0 = XEXP (x, 0); op_mode = GET_MODE (op0); - op0 = simplify_replace_rtx (op0, old, new); + op0 = simplify_replace_rtx (op0, old_rtx, new_rtx); if (op0 == XEXP (x, 0)) return x; return simplify_gen_unary (code, mode, op0, op_mode); - case '2': - case 'c': - op0 = simplify_replace_rtx (XEXP (x, 0), old, new); - op1 = simplify_replace_rtx (XEXP (x, 1), old, new); + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: + op0 = simplify_replace_rtx (XEXP (x, 0), old_rtx, new_rtx); + op1 = simplify_replace_rtx (XEXP (x, 1), old_rtx, new_rtx); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return x; return simplify_gen_binary (code, mode, op0, op1); - case '<': + case RTX_COMPARE: + case RTX_COMM_COMPARE: op0 = XEXP (x, 0); op1 = XEXP (x, 1); op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1); - op0 = simplify_replace_rtx (op0, old, new); - op1 = simplify_replace_rtx (op1, old, new); + op0 = simplify_replace_rtx (op0, old_rtx, new_rtx); + op1 = simplify_replace_rtx (op1, old_rtx, new_rtx); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return x; return simplify_gen_relational (code, mode, op_mode, op0, op1); - case '3': - case 'b': + case RTX_TERNARY: + case RTX_BITFIELD_OPS: op0 = XEXP (x, 0); op_mode = GET_MODE (op0); - op0 = simplify_replace_rtx (op0, old, new); - op1 = simplify_replace_rtx (XEXP (x, 1), old, new); - op2 = simplify_replace_rtx (XEXP (x, 2), old, new); + op0 = simplify_replace_rtx (op0, old_rtx, new_rtx); + op1 = simplify_replace_rtx (XEXP (x, 1), old_rtx, new_rtx); + op2 = simplify_replace_rtx (XEXP (x, 2), old_rtx, new_rtx); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2)) return x; if (op_mode == VOIDmode) op_mode = GET_MODE (op0); return simplify_gen_ternary (code, mode, op_mode, op0, op1, op2); - case 'x': + case RTX_EXTRA: /* The only case we try to handle is a SUBREG. */ if (code == SUBREG) { - op0 = simplify_replace_rtx (SUBREG_REG (x), old, new); + op0 = simplify_replace_rtx (SUBREG_REG (x), old_rtx, new_rtx); if (op0 == SUBREG_REG (x)) return x; op0 = simplify_gen_subreg (GET_MODE (x), op0, @@ -329,18 +317,18 @@ simplify_replace_rtx (rtx x, rtx old, rtx new) } break; - case 'o': + case RTX_OBJ: if (code == MEM) { - op0 = simplify_replace_rtx (XEXP (x, 0), old, new); + op0 = simplify_replace_rtx (XEXP (x, 0), old_rtx, new_rtx); if (op0 == XEXP (x, 0)) return x; return replace_equiv_address_nv (x, op0); } else if (code == LO_SUM) { - op0 = simplify_replace_rtx (XEXP (x, 0), old, new); - op1 = simplify_replace_rtx (XEXP (x, 1), old, new); + op0 = simplify_replace_rtx (XEXP (x, 0), old_rtx, new_rtx); + op1 = simplify_replace_rtx (XEXP (x, 1), old_rtx, new_rtx); /* (lo_sum (high x) x) -> x */ if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1)) @@ -352,8 +340,8 @@ simplify_replace_rtx (rtx x, rtx old, rtx new) } else if (code == REG) { - if (REG_P (old) && REGNO (x) == REGNO (old)) - return new; + if (rtx_equal_p (x, old_rtx)) + return new_rtx; } break; @@ -370,66 +358,302 @@ rtx simplify_unary_operation (enum rtx_code code, enum machine_mode mode, rtx op, enum machine_mode op_mode) { + rtx trueop, tem; + + if (GET_CODE (op) == CONST) + op = XEXP (op, 0); + + trueop = avoid_constant_pool_reference (op); + + tem = simplify_const_unary_operation (code, mode, trueop, op_mode); + if (tem) + return tem; + + return simplify_unary_operation_1 (code, mode, op); +} + +/* Perform some simplifications we can do even if the operands + aren't constant. */ +static rtx +simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op) +{ + enum rtx_code reversed; + rtx temp; + + switch (code) + { + case NOT: + /* (not (not X)) == X. */ + if (GET_CODE (op) == NOT) + return XEXP (op, 0); + + /* (not (eq X Y)) == (ne X Y), etc. */ + if (COMPARISON_P (op) + && (mode == BImode || STORE_FLAG_VALUE == -1) + && ((reversed = reversed_comparison_code (op, NULL_RTX)) != UNKNOWN)) + return simplify_gen_relational (reversed, mode, VOIDmode, + XEXP (op, 0), XEXP (op, 1)); + + /* (not (plus X -1)) can become (neg X). */ + if (GET_CODE (op) == PLUS + && XEXP (op, 1) == constm1_rtx) + return simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); + + /* Similarly, (not (neg X)) is (plus X -1). */ + if (GET_CODE (op) == NEG) + return plus_constant (XEXP (op, 0), -1); + + /* (not (xor X C)) for C constant is (xor X D) with D = ~C. */ + if (GET_CODE (op) == XOR + && GET_CODE (XEXP (op, 1)) == CONST_INT + && (temp = simplify_unary_operation (NOT, mode, + XEXP (op, 1), mode)) != 0) + return simplify_gen_binary (XOR, mode, XEXP (op, 0), temp); + + /* (not (plus X C)) for signbit C is (xor X D) with D = ~C. */ + if (GET_CODE (op) == PLUS + && GET_CODE (XEXP (op, 1)) == CONST_INT + && mode_signbit_p (mode, XEXP (op, 1)) + && (temp = simplify_unary_operation (NOT, mode, + XEXP (op, 1), mode)) != 0) + return simplify_gen_binary (XOR, mode, XEXP (op, 0), temp); + + + /* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for + operands other than 1, but that is not valid. We could do a + similar simplification for (not (lshiftrt C X)) where C is + just the sign bit, but this doesn't seem common enough to + bother with. */ + if (GET_CODE (op) == ASHIFT + && XEXP (op, 0) == const1_rtx) + { + temp = simplify_gen_unary (NOT, mode, const1_rtx, mode); + return simplify_gen_binary (ROTATE, mode, temp, XEXP (op, 1)); + } + + /* If STORE_FLAG_VALUE is -1, (not (comparison X Y)) can be done + by reversing the comparison code if valid. */ + if (STORE_FLAG_VALUE == -1 + && COMPARISON_P (op) + && (reversed = reversed_comparison_code (op, NULL_RTX)) != UNKNOWN) + return simplify_gen_relational (reversed, mode, VOIDmode, + XEXP (op, 0), XEXP (op, 1)); + + /* (not (ashiftrt foo C)) where C is the number of bits in FOO + minus 1 is (ge foo (const_int 0)) if STORE_FLAG_VALUE is -1, + so we can perform the above simplification. */ + + if (STORE_FLAG_VALUE == -1 + && GET_CODE (op) == ASHIFTRT + && GET_CODE (XEXP (op, 1)) == CONST_INT + && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1) + return simplify_gen_relational (GE, mode, VOIDmode, + XEXP (op, 0), const0_rtx); + + break; + + case NEG: + /* (neg (neg X)) == X. */ + if (GET_CODE (op) == NEG) + return XEXP (op, 0); + + /* (neg (plus X 1)) can become (not X). */ + if (GET_CODE (op) == PLUS + && XEXP (op, 1) == const1_rtx) + return simplify_gen_unary (NOT, mode, XEXP (op, 0), mode); + + /* Similarly, (neg (not X)) is (plus X 1). */ + if (GET_CODE (op) == NOT) + return plus_constant (XEXP (op, 0), 1); + + /* (neg (minus X Y)) can become (minus Y X). This transformation + isn't safe for modes with signed zeros, since if X and Y are + both +0, (minus Y X) is the same as (minus X Y). If the + rounding mode is towards +infinity (or -infinity) then the two + expressions will be rounded differently. */ + if (GET_CODE (op) == MINUS + && !HONOR_SIGNED_ZEROS (mode) + && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) + return simplify_gen_binary (MINUS, mode, XEXP (op, 1), XEXP (op, 0)); + + if (GET_CODE (op) == PLUS + && !HONOR_SIGNED_ZEROS (mode) + && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) + { + /* (neg (plus A C)) is simplified to (minus -C A). */ + if (GET_CODE (XEXP (op, 1)) == CONST_INT + || GET_CODE (XEXP (op, 1)) == CONST_DOUBLE) + { + temp = simplify_unary_operation (NEG, mode, XEXP (op, 1), mode); + if (temp) + return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 0)); + } + + /* (neg (plus A B)) is canonicalized to (minus (neg A) B). */ + temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); + return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 1)); + } + + /* (neg (mult A B)) becomes (mult (neg A) B). + This works even for floating-point values. */ + if (GET_CODE (op) == MULT + && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) + { + temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); + return simplify_gen_binary (MULT, mode, temp, XEXP (op, 1)); + } + + /* NEG commutes with ASHIFT since it is multiplication. Only do + this if we can then eliminate the NEG (e.g., if the operand + is a constant). */ + if (GET_CODE (op) == ASHIFT) + { + temp = simplify_unary_operation (NEG, mode, XEXP (op, 0), mode); + if (temp) + return simplify_gen_binary (ASHIFT, mode, temp, XEXP (op, 1)); + } + + /* (neg (ashiftrt X C)) can be replaced by (lshiftrt X C) when + C is equal to the width of MODE minus 1. */ + if (GET_CODE (op) == ASHIFTRT + && GET_CODE (XEXP (op, 1)) == CONST_INT + && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1) + return simplify_gen_binary (LSHIFTRT, mode, + XEXP (op, 0), XEXP (op, 1)); + + /* (neg (lshiftrt X C)) can be replaced by (ashiftrt X C) when + C is equal to the width of MODE minus 1. */ + if (GET_CODE (op) == LSHIFTRT + && GET_CODE (XEXP (op, 1)) == CONST_INT + && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1) + return simplify_gen_binary (ASHIFTRT, mode, + XEXP (op, 0), XEXP (op, 1)); + + break; + + case SIGN_EXTEND: + /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2)))) + becomes just the MINUS if its mode is MODE. This allows + folding switch statements on machines using casesi (such as + the VAX). */ + if (GET_CODE (op) == TRUNCATE + && GET_MODE (XEXP (op, 0)) == mode + && GET_CODE (XEXP (op, 0)) == MINUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF + && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF) + return XEXP (op, 0); + + /* Check for a sign extension of a subreg of a promoted + variable, where the promotion is sign-extended, and the + target mode is the same as the variable's promotion. */ + if (GET_CODE (op) == SUBREG + && SUBREG_PROMOTED_VAR_P (op) + && ! SUBREG_PROMOTED_UNSIGNED_P (op) + && GET_MODE (XEXP (op, 0)) == mode) + return XEXP (op, 0); + +#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend) + if (! POINTERS_EXTEND_UNSIGNED + && mode == Pmode && GET_MODE (op) == ptr_mode + && (CONSTANT_P (op) + || (GET_CODE (op) == SUBREG + && REG_P (SUBREG_REG (op)) + && REG_POINTER (SUBREG_REG (op)) + && GET_MODE (SUBREG_REG (op)) == Pmode))) + return convert_memory_address (Pmode, op); +#endif + break; + + case ZERO_EXTEND: + /* Check for a zero extension of a subreg of a promoted + variable, where the promotion is zero-extended, and the + target mode is the same as the variable's promotion. */ + if (GET_CODE (op) == SUBREG + && SUBREG_PROMOTED_VAR_P (op) + && SUBREG_PROMOTED_UNSIGNED_P (op) + && GET_MODE (XEXP (op, 0)) == mode) + return XEXP (op, 0); + +#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend) + if (POINTERS_EXTEND_UNSIGNED > 0 + && mode == Pmode && GET_MODE (op) == ptr_mode + && (CONSTANT_P (op) + || (GET_CODE (op) == SUBREG + && REG_P (SUBREG_REG (op)) + && REG_POINTER (SUBREG_REG (op)) + && GET_MODE (SUBREG_REG (op)) == Pmode))) + return convert_memory_address (Pmode, op); +#endif + break; + + default: + break; + } + + return 0; +} + +/* Try to compute the value of a unary operation CODE whose output mode is to + be MODE with input operand OP whose mode was originally OP_MODE. + Return zero if the value cannot be computed. */ +rtx +simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode, + rtx op, enum machine_mode op_mode) +{ unsigned int width = GET_MODE_BITSIZE (mode); - rtx trueop = avoid_constant_pool_reference (op); if (code == VEC_DUPLICATE) { - if (!VECTOR_MODE_P (mode)) - abort (); - if (GET_MODE (trueop) != VOIDmode - && !VECTOR_MODE_P (GET_MODE (trueop)) - && GET_MODE_INNER (mode) != GET_MODE (trueop)) - abort (); - if (GET_MODE (trueop) != VOIDmode - && VECTOR_MODE_P (GET_MODE (trueop)) - && GET_MODE_INNER (mode) != GET_MODE_INNER (GET_MODE (trueop))) - abort (); - if (GET_CODE (trueop) == CONST_INT || GET_CODE (trueop) == CONST_DOUBLE - || GET_CODE (trueop) == CONST_VECTOR) + gcc_assert (VECTOR_MODE_P (mode)); + if (GET_MODE (op) != VOIDmode) + { + if (!VECTOR_MODE_P (GET_MODE (op))) + gcc_assert (GET_MODE_INNER (mode) == GET_MODE (op)); + else + gcc_assert (GET_MODE_INNER (mode) == GET_MODE_INNER + (GET_MODE (op))); + } + if (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE + || GET_CODE (op) == CONST_VECTOR) { int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); rtvec v = rtvec_alloc (n_elts); unsigned int i; - if (GET_CODE (trueop) != CONST_VECTOR) + if (GET_CODE (op) != CONST_VECTOR) for (i = 0; i < n_elts; i++) - RTVEC_ELT (v, i) = trueop; + RTVEC_ELT (v, i) = op; else { - enum machine_mode inmode = GET_MODE (trueop); + enum machine_mode inmode = GET_MODE (op); int in_elt_size = GET_MODE_SIZE (GET_MODE_INNER (inmode)); unsigned in_n_elts = (GET_MODE_SIZE (inmode) / in_elt_size); - if (in_n_elts >= n_elts || n_elts % in_n_elts) - abort (); + gcc_assert (in_n_elts < n_elts); + gcc_assert ((n_elts % in_n_elts) == 0); for (i = 0; i < n_elts; i++) - RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop, i % in_n_elts); + RTVEC_ELT (v, i) = CONST_VECTOR_ELT (op, i % in_n_elts); } return gen_rtx_CONST_VECTOR (mode, v); } } - else if (GET_CODE (op) == CONST) - return simplify_unary_operation (code, mode, XEXP (op, 0), op_mode); - if (VECTOR_MODE_P (mode) && GET_CODE (trueop) == CONST_VECTOR) + if (VECTOR_MODE_P (mode) && GET_CODE (op) == CONST_VECTOR) { int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); - enum machine_mode opmode = GET_MODE (trueop); + enum machine_mode opmode = GET_MODE (op); int op_elt_size = GET_MODE_SIZE (GET_MODE_INNER (opmode)); unsigned op_n_elts = (GET_MODE_SIZE (opmode) / op_elt_size); rtvec v = rtvec_alloc (n_elts); unsigned int i; - if (op_n_elts != n_elts) - abort (); - + gcc_assert (op_n_elts == n_elts); for (i = 0; i < n_elts; i++) { rtx x = simplify_unary_operation (code, GET_MODE_INNER (mode), - CONST_VECTOR_ELT (trueop, i), + CONST_VECTOR_ELT (op, i), GET_MODE_INNER (opmode)); if (!x) return 0; @@ -442,32 +666,32 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, check the wrong mode (input vs. output) for a conversion operation, such as FIX. At some point, this should be simplified. */ - if (code == FLOAT && GET_MODE (trueop) == VOIDmode - && (GET_CODE (trueop) == CONST_DOUBLE || GET_CODE (trueop) == CONST_INT)) + if (code == FLOAT && GET_MODE (op) == VOIDmode + && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)) { HOST_WIDE_INT hv, lv; REAL_VALUE_TYPE d; - if (GET_CODE (trueop) == CONST_INT) - lv = INTVAL (trueop), hv = HWI_SIGN_EXTEND (lv); + if (GET_CODE (op) == CONST_INT) + lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv); else - lv = CONST_DOUBLE_LOW (trueop), hv = CONST_DOUBLE_HIGH (trueop); + lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op); REAL_VALUE_FROM_INT (d, lv, hv, mode); d = real_value_truncate (mode, d); return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); } - else if (code == UNSIGNED_FLOAT && GET_MODE (trueop) == VOIDmode - && (GET_CODE (trueop) == CONST_DOUBLE - || GET_CODE (trueop) == CONST_INT)) + else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode + && (GET_CODE (op) == CONST_DOUBLE + || GET_CODE (op) == CONST_INT)) { HOST_WIDE_INT hv, lv; REAL_VALUE_TYPE d; - if (GET_CODE (trueop) == CONST_INT) - lv = INTVAL (trueop), hv = HWI_SIGN_EXTEND (lv); + if (GET_CODE (op) == CONST_INT) + lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv); else - lv = CONST_DOUBLE_LOW (trueop), hv = CONST_DOUBLE_HIGH (trueop); + lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op); if (op_mode == VOIDmode) { @@ -486,10 +710,10 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); } - if (GET_CODE (trueop) == CONST_INT + if (GET_CODE (op) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT && width > 0) { - HOST_WIDE_INT arg0 = INTVAL (trueop); + HOST_WIDE_INT arg0 = INTVAL (op); HOST_WIDE_INT val; switch (code) @@ -556,15 +780,13 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, case ZERO_EXTEND: /* When zero-extending a CONST_INT, we need to know its original mode. */ - if (op_mode == VOIDmode) - abort (); + gcc_assert (op_mode != VOIDmode); if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT) { /* If we were really extending the mode, we would have to distinguish between zero-extension and sign-extension. */ - if (width != GET_MODE_BITSIZE (op_mode)) - abort (); + gcc_assert (width == GET_MODE_BITSIZE (op_mode)); val = arg0; } else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) @@ -581,8 +803,7 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, /* If we were really extending the mode, we would have to distinguish between zero-extension and sign-extension. */ - if (width != GET_MODE_BITSIZE (op_mode)) - abort (); + gcc_assert (width == GET_MODE_BITSIZE (op_mode)); val = arg0; } else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT) @@ -605,28 +826,26 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, return 0; default: - abort (); + gcc_unreachable (); } - val = trunc_int_for_mode (val, mode); - - return GEN_INT (val); + return gen_int_mode (val, mode); } /* We can do some operations on integer CONST_DOUBLEs. Also allow for a DImode operation on a CONST_INT. */ - else if (GET_MODE (trueop) == VOIDmode + else if (GET_MODE (op) == VOIDmode && width <= HOST_BITS_PER_WIDE_INT * 2 - && (GET_CODE (trueop) == CONST_DOUBLE - || GET_CODE (trueop) == CONST_INT)) + && (GET_CODE (op) == CONST_DOUBLE + || GET_CODE (op) == CONST_INT)) { unsigned HOST_WIDE_INT l1, lv; HOST_WIDE_INT h1, hv; - if (GET_CODE (trueop) == CONST_DOUBLE) - l1 = CONST_DOUBLE_LOW (trueop), h1 = CONST_DOUBLE_HIGH (trueop); + if (GET_CODE (op) == CONST_DOUBLE) + l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op); else - l1 = INTVAL (trueop), h1 = HWI_SIGN_EXTEND (l1); + l1 = INTVAL (op), h1 = HWI_SIGN_EXTEND (l1); switch (code) { @@ -705,8 +924,7 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, break; case ZERO_EXTEND: - if (op_mode == VOIDmode) - abort (); + gcc_assert (op_mode != VOIDmode); if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT) return 0; @@ -741,11 +959,11 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, return immed_double_const (lv, hv, mode); } - else if (GET_CODE (trueop) == CONST_DOUBLE + else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE_CLASS (mode) == MODE_FLOAT) { REAL_VALUE_TYPE d, t; - REAL_VALUE_FROM_CONST_DOUBLE (d, trueop); + REAL_VALUE_FROM_CONST_DOUBLE (d, op); switch (code) { @@ -770,15 +988,25 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, case FIX: real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL); break; + case NOT: + { + long tmp[4]; + int i; + real_to_target (tmp, &d, GET_MODE (op)); + for (i = 0; i < 4; i++) + tmp[i] = ~tmp[i]; + real_from_target (&d, tmp, mode); + break; + } default: - abort (); + gcc_unreachable (); } return CONST_DOUBLE_FROM_REAL_VALUE (d, mode); } - else if (GET_CODE (trueop) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (trueop)) == MODE_FLOAT + else if (GET_CODE (op) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT && GET_MODE_CLASS (mode) == MODE_INT && width <= 2*HOST_BITS_PER_WIDE_INT && width > 0) { @@ -787,9 +1015,11 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, by target backends), for consistency, this routine implements the same semantics for constant folding as used by the middle-end. */ + /* This was formerly used only for non-IEEE float. + eggert@twinsun.com says it is safe for IEEE also. */ HOST_WIDE_INT xh, xl, th, tl; REAL_VALUE_TYPE x, t; - REAL_VALUE_FROM_CONST_DOUBLE (x, trueop); + REAL_VALUE_FROM_CONST_DOUBLE (x, op); switch (code) { case FIX: @@ -870,284 +1100,73 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode, break; default: - abort (); + gcc_unreachable (); } return immed_double_const (xl, xh, mode); } - /* This was formerly used only for non-IEEE float. - eggert@twinsun.com says it is safe for IEEE also. */ - else - { - enum rtx_code reversed; - rtx temp; - - /* There are some simplifications we can do even if the operands - aren't constant. */ - switch (code) - { - case NOT: - /* (not (not X)) == X. */ - if (GET_CODE (op) == NOT) - return XEXP (op, 0); - - /* (not (eq X Y)) == (ne X Y), etc. */ - if (GET_RTX_CLASS (GET_CODE (op)) == '<' - && (mode == BImode || STORE_FLAG_VALUE == -1) - && ((reversed = reversed_comparison_code (op, NULL_RTX)) - != UNKNOWN)) - return simplify_gen_relational (reversed, mode, VOIDmode, - XEXP (op, 0), XEXP (op, 1)); - - /* (not (plus X -1)) can become (neg X). */ - if (GET_CODE (op) == PLUS - && XEXP (op, 1) == constm1_rtx) - return simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); - - /* Similarly, (not (neg X)) is (plus X -1). */ - if (GET_CODE (op) == NEG) - return plus_constant (XEXP (op, 0), -1); - - /* (not (xor X C)) for C constant is (xor X D) with D = ~C. */ - if (GET_CODE (op) == XOR - && GET_CODE (XEXP (op, 1)) == CONST_INT - && (temp = simplify_unary_operation (NOT, mode, - XEXP (op, 1), - mode)) != 0) - return simplify_gen_binary (XOR, mode, XEXP (op, 0), temp); - - - /* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for - operands other than 1, but that is not valid. We could do a - similar simplification for (not (lshiftrt C X)) where C is - just the sign bit, but this doesn't seem common enough to - bother with. */ - if (GET_CODE (op) == ASHIFT - && XEXP (op, 0) == const1_rtx) - { - temp = simplify_gen_unary (NOT, mode, const1_rtx, mode); - return simplify_gen_binary (ROTATE, mode, temp, XEXP (op, 1)); - } - - /* If STORE_FLAG_VALUE is -1, (not (comparison X Y)) can be done - by reversing the comparison code if valid. */ - if (STORE_FLAG_VALUE == -1 - && GET_RTX_CLASS (GET_CODE (op)) == '<' - && (reversed = reversed_comparison_code (op, NULL_RTX)) - != UNKNOWN) - return simplify_gen_relational (reversed, mode, VOIDmode, - XEXP (op, 0), XEXP (op, 1)); - - /* (not (ashiftrt foo C)) where C is the number of bits in FOO - minus 1 is (ge foo (const_int 0)) if STORE_FLAG_VALUE is -1, - so we can perform the above simplification. */ - - if (STORE_FLAG_VALUE == -1 - && GET_CODE (op) == ASHIFTRT - && GET_CODE (XEXP (op, 1)) == CONST_INT - && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1) - return simplify_gen_relational (GE, mode, VOIDmode, - XEXP (op, 0), const0_rtx); - - break; - - case NEG: - /* (neg (neg X)) == X. */ - if (GET_CODE (op) == NEG) - return XEXP (op, 0); - - /* (neg (plus X 1)) can become (not X). */ - if (GET_CODE (op) == PLUS - && XEXP (op, 1) == const1_rtx) - return simplify_gen_unary (NOT, mode, XEXP (op, 0), mode); - - /* Similarly, (neg (not X)) is (plus X 1). */ - if (GET_CODE (op) == NOT) - return plus_constant (XEXP (op, 0), 1); - - /* (neg (minus X Y)) can become (minus Y X). This transformation - isn't safe for modes with signed zeros, since if X and Y are - both +0, (minus Y X) is the same as (minus X Y). If the - rounding mode is towards +infinity (or -infinity) then the two - expressions will be rounded differently. */ - if (GET_CODE (op) == MINUS - && !HONOR_SIGNED_ZEROS (mode) - && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) - return simplify_gen_binary (MINUS, mode, XEXP (op, 1), - XEXP (op, 0)); - - if (GET_CODE (op) == PLUS - && !HONOR_SIGNED_ZEROS (mode) - && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) - { - /* (neg (plus A C)) is simplified to (minus -C A). */ - if (GET_CODE (XEXP (op, 1)) == CONST_INT - || GET_CODE (XEXP (op, 1)) == CONST_DOUBLE) - { - temp = simplify_unary_operation (NEG, mode, XEXP (op, 1), - mode); - if (temp) - return simplify_gen_binary (MINUS, mode, temp, - XEXP (op, 0)); - } - - /* (neg (plus A B)) is canonicalized to (minus (neg A) B). */ - temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); - return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 1)); - } - - /* (neg (mult A B)) becomes (mult (neg A) B). - This works even for floating-point values. */ - if (GET_CODE (op) == MULT - && !HONOR_SIGN_DEPENDENT_ROUNDING (mode)) - { - temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode); - return simplify_gen_binary (MULT, mode, temp, XEXP (op, 1)); - } - - /* NEG commutes with ASHIFT since it is multiplication. Only do - this if we can then eliminate the NEG (e.g., if the operand - is a constant). */ - if (GET_CODE (op) == ASHIFT) - { - temp = simplify_unary_operation (NEG, mode, XEXP (op, 0), - mode); - if (temp) - return simplify_gen_binary (ASHIFT, mode, temp, - XEXP (op, 1)); - } - - break; - - case SIGN_EXTEND: - /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2)))) - becomes just the MINUS if its mode is MODE. This allows - folding switch statements on machines using casesi (such as - the VAX). */ - if (GET_CODE (op) == TRUNCATE - && GET_MODE (XEXP (op, 0)) == mode - && GET_CODE (XEXP (op, 0)) == MINUS - && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF - && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF) - return XEXP (op, 0); - - /* Check for a sign extension of a subreg of a promoted - variable, where the promotion is sign-extended, and the - target mode is the same as the variable's promotion. */ - if (GET_CODE (op) == SUBREG - && SUBREG_PROMOTED_VAR_P (op) - && ! SUBREG_PROMOTED_UNSIGNED_P (op) - && GET_MODE (XEXP (op, 0)) == mode) - return XEXP (op, 0); - -#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend) - if (! POINTERS_EXTEND_UNSIGNED - && mode == Pmode && GET_MODE (op) == ptr_mode - && (CONSTANT_P (op) - || (GET_CODE (op) == SUBREG - && GET_CODE (SUBREG_REG (op)) == REG - && REG_POINTER (SUBREG_REG (op)) - && GET_MODE (SUBREG_REG (op)) == Pmode))) - return convert_memory_address (Pmode, op); -#endif - break; - - case ZERO_EXTEND: - /* Check for a zero extension of a subreg of a promoted - variable, where the promotion is zero-extended, and the - target mode is the same as the variable's promotion. */ - if (GET_CODE (op) == SUBREG - && SUBREG_PROMOTED_VAR_P (op) - && SUBREG_PROMOTED_UNSIGNED_P (op) - && GET_MODE (XEXP (op, 0)) == mode) - return XEXP (op, 0); - -#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend) - if (POINTERS_EXTEND_UNSIGNED > 0 - && mode == Pmode && GET_MODE (op) == ptr_mode - && (CONSTANT_P (op) - || (GET_CODE (op) == SUBREG - && GET_CODE (SUBREG_REG (op)) == REG - && REG_POINTER (SUBREG_REG (op)) - && GET_MODE (SUBREG_REG (op)) == Pmode))) - return convert_memory_address (Pmode, op); -#endif - break; - - default: - break; - } - - return 0; - } + return NULL_RTX; } -/* Subroutine of simplify_associative_operation. Return true if rtx OP - is a suitable integer or floating point immediate constant. */ -static bool -associative_constant_p (rtx op) -{ - if (GET_CODE (op) == CONST_INT - || GET_CODE (op) == CONST_DOUBLE) - return true; - op = avoid_constant_pool_reference (op); - return GET_CODE (op) == CONST_INT - || GET_CODE (op) == CONST_DOUBLE; -} +/* Subroutine of simplify_binary_operation to simplify a commutative, + associative binary operation CODE with result mode MODE, operating + on OP0 and OP1. CODE is currently one of PLUS, MULT, AND, IOR, XOR, + SMIN, SMAX, UMIN or UMAX. Return zero if no simplification or + canonicalization is possible. */ -/* Subroutine of simplify_binary_operation to simplify an associative - binary operation CODE with result mode MODE, operating on OP0 and OP1. - Return 0 if no simplification is possible. */ static rtx simplify_associative_operation (enum rtx_code code, enum machine_mode mode, rtx op0, rtx op1) { rtx tem; - /* Simplify (x op c1) op c2 as x op (c1 op c2). */ - if (GET_CODE (op0) == code - && associative_constant_p (op1) - && associative_constant_p (XEXP (op0, 1))) + /* Linearize the operator to the left. */ + if (GET_CODE (op1) == code) { - tem = simplify_binary_operation (code, mode, XEXP (op0, 1), op1); - if (! tem) - return tem; - return simplify_gen_binary (code, mode, XEXP (op0, 0), tem); - } + /* "(a op b) op (c op d)" becomes "((a op b) op c) op d)". */ + if (GET_CODE (op0) == code) + { + tem = simplify_gen_binary (code, mode, op0, XEXP (op1, 0)); + return simplify_gen_binary (code, mode, tem, XEXP (op1, 1)); + } - /* Simplify (x op c1) op (y op c2) as (x op y) op (c1 op c2). */ - if (GET_CODE (op0) == code - && GET_CODE (op1) == code - && associative_constant_p (XEXP (op0, 1)) - && associative_constant_p (XEXP (op1, 1))) - { - rtx c = simplify_binary_operation (code, mode, - XEXP (op0, 1), XEXP (op1, 1)); - if (! c) - return 0; - tem = simplify_gen_binary (code, mode, XEXP (op0, 0), XEXP (op1, 0)); - return simplify_gen_binary (code, mode, tem, c); - } + /* "a op (b op c)" becomes "(b op c) op a". */ + if (! swap_commutative_operands_p (op1, op0)) + return simplify_gen_binary (code, mode, op1, op0); - /* Canonicalize (x op c) op y as (x op y) op c. */ - if (GET_CODE (op0) == code - && associative_constant_p (XEXP (op0, 1))) - { - tem = simplify_gen_binary (code, mode, XEXP (op0, 0), op1); - return simplify_gen_binary (code, mode, tem, XEXP (op0, 1)); + tem = op0; + op0 = op1; + op1 = tem; } - /* Canonicalize x op (y op c) as (x op y) op c. */ - if (GET_CODE (op1) == code - && associative_constant_p (XEXP (op1, 1))) + if (GET_CODE (op0) == code) { - tem = simplify_gen_binary (code, mode, op0, XEXP (op1, 0)); - return simplify_gen_binary (code, mode, tem, XEXP (op1, 1)); + /* Canonicalize "(x op c) op y" as "(x op y) op c". */ + if (swap_commutative_operands_p (XEXP (op0, 1), op1)) + { + tem = simplify_gen_binary (code, mode, XEXP (op0, 0), op1); + return simplify_gen_binary (code, mode, tem, XEXP (op0, 1)); + } + + /* Attempt to simplify "(a op b) op c" as "a op (b op c)". */ + tem = swap_commutative_operands_p (XEXP (op0, 1), op1) + ? simplify_binary_operation (code, mode, op1, XEXP (op0, 1)) + : simplify_binary_operation (code, mode, XEXP (op0, 1), op1); + if (tem != 0) + return simplify_gen_binary (code, mode, XEXP (op0, 0), tem); + + /* Attempt to simplify "(a op b) op c" as "(a op c) op b". */ + tem = swap_commutative_operands_p (XEXP (op0, 0), op1) + ? simplify_binary_operation (code, mode, op1, XEXP (op0, 0)) + : simplify_binary_operation (code, mode, XEXP (op0, 0), op1); + if (tem != 0) + return simplify_gen_binary (code, mode, tem, XEXP (op0, 1)); } return 0; } + /* Simplify a binary operation CODE with result mode MODE, operating on OP0 and OP1. Return 0 if no simplification is possible. @@ -1157,1061 +1176,1326 @@ rtx simplify_binary_operation (enum rtx_code code, enum machine_mode mode, rtx op0, rtx op1) { - HOST_WIDE_INT arg0, arg1, arg0s, arg1s; - HOST_WIDE_INT val; - unsigned int width = GET_MODE_BITSIZE (mode); + rtx trueop0, trueop1; rtx tem; - rtx trueop0 = avoid_constant_pool_reference (op0); - rtx trueop1 = avoid_constant_pool_reference (op1); /* Relational operations don't work here. We must know the mode of the operands in order to do the comparison correctly. Assuming a full word can give incorrect results. Consider comparing 128 with -128 in QImode. */ - - if (GET_RTX_CLASS (code) == '<') - abort (); + gcc_assert (GET_RTX_CLASS (code) != RTX_COMPARE); + gcc_assert (GET_RTX_CLASS (code) != RTX_COMM_COMPARE); /* Make sure the constant is second. */ - if (GET_RTX_CLASS (code) == 'c' - && swap_commutative_operands_p (trueop0, trueop1)) + if (GET_RTX_CLASS (code) == RTX_COMM_ARITH + && swap_commutative_operands_p (op0, op1)) { tem = op0, op0 = op1, op1 = tem; - tem = trueop0, trueop0 = trueop1, trueop1 = tem; } - if (VECTOR_MODE_P (mode) - && GET_CODE (trueop0) == CONST_VECTOR - && GET_CODE (trueop1) == CONST_VECTOR) - { - int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); - unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); - enum machine_mode op0mode = GET_MODE (trueop0); - int op0_elt_size = GET_MODE_SIZE (GET_MODE_INNER (op0mode)); - unsigned op0_n_elts = (GET_MODE_SIZE (op0mode) / op0_elt_size); - enum machine_mode op1mode = GET_MODE (trueop1); - int op1_elt_size = GET_MODE_SIZE (GET_MODE_INNER (op1mode)); - unsigned op1_n_elts = (GET_MODE_SIZE (op1mode) / op1_elt_size); - rtvec v = rtvec_alloc (n_elts); - unsigned int i; + trueop0 = avoid_constant_pool_reference (op0); + trueop1 = avoid_constant_pool_reference (op1); - if (op0_n_elts != n_elts || op1_n_elts != n_elts) - abort (); + tem = simplify_const_binary_operation (code, mode, trueop0, trueop1); + if (tem) + return tem; + return simplify_binary_operation_1 (code, mode, op0, op1, trueop0, trueop1); +} - for (i = 0; i < n_elts; i++) - { - rtx x = simplify_binary_operation (code, GET_MODE_INNER (mode), - CONST_VECTOR_ELT (trueop0, i), - CONST_VECTOR_ELT (trueop1, i)); - if (!x) - return 0; - RTVEC_ELT (v, i) = x; - } +static rtx +simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode, + rtx op0, rtx op1, rtx trueop0, rtx trueop1) +{ + rtx tem; + HOST_WIDE_INT val; + unsigned int width = GET_MODE_BITSIZE (mode); - return gen_rtx_CONST_VECTOR (mode, v); - } + /* Even if we can't compute a constant result, + there are some cases worth simplifying. */ - if (GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_CODE (trueop0) == CONST_DOUBLE - && GET_CODE (trueop1) == CONST_DOUBLE - && mode == GET_MODE (op0) && mode == GET_MODE (op1)) + switch (code) { - REAL_VALUE_TYPE f0, f1, value; - - REAL_VALUE_FROM_CONST_DOUBLE (f0, trueop0); - REAL_VALUE_FROM_CONST_DOUBLE (f1, trueop1); - f0 = real_value_truncate (mode, f0); - f1 = real_value_truncate (mode, f1); + case PLUS: + /* Maybe simplify x + 0 to x. The two expressions are equivalent + when x is NaN, infinite, or finite and nonzero. They aren't + when x is -0 and the rounding mode is not towards -infinity, + since (-0) + 0 is then 0. */ + if (!HONOR_SIGNED_ZEROS (mode) && trueop1 == CONST0_RTX (mode)) + return op0; + + /* ((-a) + b) -> (b - a) and similarly for (a + (-b)). These + transformations are safe even for IEEE. */ + if (GET_CODE (op0) == NEG) + return simplify_gen_binary (MINUS, mode, op1, XEXP (op0, 0)); + else if (GET_CODE (op1) == NEG) + return simplify_gen_binary (MINUS, mode, op0, XEXP (op1, 0)); + + /* (~a) + 1 -> -a */ + if (INTEGRAL_MODE_P (mode) + && GET_CODE (op0) == NOT + && trueop1 == const1_rtx) + return simplify_gen_unary (NEG, mode, XEXP (op0, 0), mode); + + /* Handle both-operands-constant cases. We can only add + CONST_INTs to constants since the sum of relocatable symbols + can't be handled by most assemblers. Don't add CONST_INT + to CONST_INT since overflow won't be computed properly if wider + than HOST_BITS_PER_WIDE_INT. */ + + if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode + && GET_CODE (op1) == CONST_INT) + return plus_constant (op0, INTVAL (op1)); + else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode + && GET_CODE (op0) == CONST_INT) + return plus_constant (op1, INTVAL (op0)); + + /* See if this is something like X * C - X or vice versa or + if the multiplication is written as a shift. If so, we can + distribute and make a new multiply, shift, or maybe just + have X (if C is 2 in the example above). But don't make + something more expensive than we had before. */ + + if (! FLOAT_MODE_P (mode)) + { + HOST_WIDE_INT coeff0 = 1, coeff1 = 1; + rtx lhs = op0, rhs = op1; + + if (GET_CODE (lhs) == NEG) + coeff0 = -1, lhs = XEXP (lhs, 0); + else if (GET_CODE (lhs) == MULT + && GET_CODE (XEXP (lhs, 1)) == CONST_INT) + coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0); + else if (GET_CODE (lhs) == ASHIFT + && GET_CODE (XEXP (lhs, 1)) == CONST_INT + && INTVAL (XEXP (lhs, 1)) >= 0 + && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT) + { + coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1)); + lhs = XEXP (lhs, 0); + } - if (HONOR_SNANS (mode) - && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1))) - return 0; + if (GET_CODE (rhs) == NEG) + coeff1 = -1, rhs = XEXP (rhs, 0); + else if (GET_CODE (rhs) == MULT + && GET_CODE (XEXP (rhs, 1)) == CONST_INT) + { + coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0); + } + else if (GET_CODE (rhs) == ASHIFT + && GET_CODE (XEXP (rhs, 1)) == CONST_INT + && INTVAL (XEXP (rhs, 1)) >= 0 + && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT) + { + coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1)); + rhs = XEXP (rhs, 0); + } - if (code == DIV - && REAL_VALUES_EQUAL (f1, dconst0) - && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode))) - return 0; + if (rtx_equal_p (lhs, rhs)) + { + rtx orig = gen_rtx_PLUS (mode, op0, op1); + tem = simplify_gen_binary (MULT, mode, lhs, + GEN_INT (coeff0 + coeff1)); + return rtx_cost (tem, SET) <= rtx_cost (orig, SET) + ? tem : 0; + } + } - REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1); + /* (plus (xor X C1) C2) is (xor X (C1^C2)) if C2 is signbit. */ + if ((GET_CODE (op1) == CONST_INT + || GET_CODE (op1) == CONST_DOUBLE) + && GET_CODE (op0) == XOR + && (GET_CODE (XEXP (op0, 1)) == CONST_INT + || GET_CODE (XEXP (op0, 1)) == CONST_DOUBLE) + && mode_signbit_p (mode, op1)) + return simplify_gen_binary (XOR, mode, XEXP (op0, 0), + simplify_gen_binary (XOR, mode, op1, + XEXP (op0, 1))); + + /* If one of the operands is a PLUS or a MINUS, see if we can + simplify this by the associative law. + Don't use the associative law for floating point. + The inaccuracy makes it nonassociative, + and subtle programs can break if operations are associated. */ + + if (INTEGRAL_MODE_P (mode) + && (plus_minus_operand_p (op0) + || plus_minus_operand_p (op1)) + && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0) + return tem; - value = real_value_truncate (mode, value); - return CONST_DOUBLE_FROM_REAL_VALUE (value, mode); - } + /* Reassociate floating point addition only when the user + specifies unsafe math optimizations. */ + if (FLOAT_MODE_P (mode) + && flag_unsafe_math_optimizations) + { + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + } + break; - /* We can fold some multi-word operations. */ - if (GET_MODE_CLASS (mode) == MODE_INT - && width == HOST_BITS_PER_WIDE_INT * 2 - && (GET_CODE (trueop0) == CONST_DOUBLE - || GET_CODE (trueop0) == CONST_INT) - && (GET_CODE (trueop1) == CONST_DOUBLE - || GET_CODE (trueop1) == CONST_INT)) - { - unsigned HOST_WIDE_INT l1, l2, lv; - HOST_WIDE_INT h1, h2, hv; + case COMPARE: +#ifdef HAVE_cc0 + /* Convert (compare FOO (const_int 0)) to FOO unless we aren't + using cc0, in which case we want to leave it as a COMPARE + so we can distinguish it from a register-register-copy. - if (GET_CODE (trueop0) == CONST_DOUBLE) - l1 = CONST_DOUBLE_LOW (trueop0), h1 = CONST_DOUBLE_HIGH (trueop0); - else - l1 = INTVAL (trueop0), h1 = HWI_SIGN_EXTEND (l1); + In IEEE floating point, x-0 is not the same as x. */ - if (GET_CODE (trueop1) == CONST_DOUBLE) - l2 = CONST_DOUBLE_LOW (trueop1), h2 = CONST_DOUBLE_HIGH (trueop1); - else - l2 = INTVAL (trueop1), h2 = HWI_SIGN_EXTEND (l2); + if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT + || ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations) + && trueop1 == CONST0_RTX (mode)) + return op0; +#endif - switch (code) + /* Convert (compare (gt (flags) 0) (lt (flags) 0)) to (flags). */ + if (((GET_CODE (op0) == GT && GET_CODE (op1) == LT) + || (GET_CODE (op0) == GTU && GET_CODE (op1) == LTU)) + && XEXP (op0, 1) == const0_rtx && XEXP (op1, 1) == const0_rtx) { - case MINUS: - /* A - B == A + (-B). */ - neg_double (l2, h2, &lv, &hv); - l2 = lv, h2 = hv; - - /* Fall through.... */ + rtx xop00 = XEXP (op0, 0); + rtx xop10 = XEXP (op1, 0); - case PLUS: - add_double (l1, h1, l2, h2, &lv, &hv); - break; - - case MULT: - mul_double (l1, h1, l2, h2, &lv, &hv); - break; - - case DIV: case MOD: case UDIV: case UMOD: - /* We'd need to include tree.h to do this and it doesn't seem worth - it. */ - return 0; - - case AND: - lv = l1 & l2, hv = h1 & h2; - break; - - case IOR: - lv = l1 | l2, hv = h1 | h2; - break; - - case XOR: - lv = l1 ^ l2, hv = h1 ^ h2; - break; - - case SMIN: - if (h1 < h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - < (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case SMAX: - if (h1 > h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - > (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case UMIN: - if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - < (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case UMAX: - if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2 - || (h1 == h2 - && ((unsigned HOST_WIDE_INT) l1 - > (unsigned HOST_WIDE_INT) l2))) - lv = l1, hv = h1; - else - lv = l2, hv = h2; - break; - - case LSHIFTRT: case ASHIFTRT: - case ASHIFT: - case ROTATE: case ROTATERT: -#ifdef SHIFT_COUNT_TRUNCATED - if (SHIFT_COUNT_TRUNCATED) - l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0; +#ifdef HAVE_cc0 + if (GET_CODE (xop00) == CC0 && GET_CODE (xop10) == CC0) +#else + if (REG_P (xop00) && REG_P (xop10) + && GET_MODE (xop00) == GET_MODE (xop10) + && REGNO (xop00) == REGNO (xop10) + && GET_MODE_CLASS (GET_MODE (xop00)) == MODE_CC + && GET_MODE_CLASS (GET_MODE (xop10)) == MODE_CC) #endif - - if (h2 != 0 || l2 >= GET_MODE_BITSIZE (mode)) - return 0; - - if (code == LSHIFTRT || code == ASHIFTRT) - rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, - code == ASHIFTRT); - else if (code == ASHIFT) - lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1); - else if (code == ROTATE) - lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); - else /* code == ROTATERT */ - rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); - break; - - default: - return 0; + return xop00; } + break; - return immed_double_const (lv, hv, mode); - } - - if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT - || width > HOST_BITS_PER_WIDE_INT || width == 0) - { - /* Even if we can't compute a constant result, - there are some cases worth simplifying. */ - - switch (code) + case MINUS: + /* We can't assume x-x is 0 even with non-IEEE floating point, + but since it is zero except in very strange circumstances, we + will treat it as zero with -funsafe-math-optimizations. */ + if (rtx_equal_p (trueop0, trueop1) + && ! side_effects_p (op0) + && (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)) + return CONST0_RTX (mode); + + /* Change subtraction from zero into negation. (0 - x) is the + same as -x when x is NaN, infinite, or finite and nonzero. + But if the mode has signed zeros, and does not round towards + -infinity, then 0 - 0 is 0, not -0. */ + if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode)) + return simplify_gen_unary (NEG, mode, op1, mode); + + /* (-1 - a) is ~a. */ + if (trueop0 == constm1_rtx) + return simplify_gen_unary (NOT, mode, op1, mode); + + /* Subtracting 0 has no effect unless the mode has signed zeros + and supports rounding towards -infinity. In such a case, + 0 - 0 is -0. */ + if (!(HONOR_SIGNED_ZEROS (mode) + && HONOR_SIGN_DEPENDENT_ROUNDING (mode)) + && trueop1 == CONST0_RTX (mode)) + return op0; + + /* See if this is something like X * C - X or vice versa or + if the multiplication is written as a shift. If so, we can + distribute and make a new multiply, shift, or maybe just + have X (if C is 2 in the example above). But don't make + something more expensive than we had before. */ + + if (! FLOAT_MODE_P (mode)) { - case PLUS: - /* Maybe simplify x + 0 to x. The two expressions are equivalent - when x is NaN, infinite, or finite and nonzero. They aren't - when x is -0 and the rounding mode is not towards -infinity, - since (-0) + 0 is then 0. */ - if (!HONOR_SIGNED_ZEROS (mode) && trueop1 == CONST0_RTX (mode)) - return op0; + HOST_WIDE_INT coeff0 = 1, coeff1 = 1; + rtx lhs = op0, rhs = op1; - /* ((-a) + b) -> (b - a) and similarly for (a + (-b)). These - transformations are safe even for IEEE. */ - if (GET_CODE (op0) == NEG) - return simplify_gen_binary (MINUS, mode, op1, XEXP (op0, 0)); - else if (GET_CODE (op1) == NEG) - return simplify_gen_binary (MINUS, mode, op0, XEXP (op1, 0)); - - /* (~a) + 1 -> -a */ - if (INTEGRAL_MODE_P (mode) - && GET_CODE (op0) == NOT - && trueop1 == const1_rtx) - return simplify_gen_unary (NEG, mode, XEXP (op0, 0), mode); - - /* Handle both-operands-constant cases. We can only add - CONST_INTs to constants since the sum of relocatable symbols - can't be handled by most assemblers. Don't add CONST_INT - to CONST_INT since overflow won't be computed properly if wider - than HOST_BITS_PER_WIDE_INT. */ - - if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode - && GET_CODE (op1) == CONST_INT) - return plus_constant (op0, INTVAL (op1)); - else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode - && GET_CODE (op0) == CONST_INT) - return plus_constant (op1, INTVAL (op0)); - - /* See if this is something like X * C - X or vice versa or - if the multiplication is written as a shift. If so, we can - distribute and make a new multiply, shift, or maybe just - have X (if C is 2 in the example above). But don't make - real multiply if we didn't have one before. */ - - if (! FLOAT_MODE_P (mode)) + if (GET_CODE (lhs) == NEG) + coeff0 = -1, lhs = XEXP (lhs, 0); + else if (GET_CODE (lhs) == MULT + && GET_CODE (XEXP (lhs, 1)) == CONST_INT) { - HOST_WIDE_INT coeff0 = 1, coeff1 = 1; - rtx lhs = op0, rhs = op1; - int had_mult = 0; - - if (GET_CODE (lhs) == NEG) - coeff0 = -1, lhs = XEXP (lhs, 0); - else if (GET_CODE (lhs) == MULT - && GET_CODE (XEXP (lhs, 1)) == CONST_INT) - { - coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0); - had_mult = 1; - } - else if (GET_CODE (lhs) == ASHIFT - && GET_CODE (XEXP (lhs, 1)) == CONST_INT - && INTVAL (XEXP (lhs, 1)) >= 0 - && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT) - { - coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1)); - lhs = XEXP (lhs, 0); - } - - if (GET_CODE (rhs) == NEG) - coeff1 = -1, rhs = XEXP (rhs, 0); - else if (GET_CODE (rhs) == MULT - && GET_CODE (XEXP (rhs, 1)) == CONST_INT) - { - coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0); - had_mult = 1; - } - else if (GET_CODE (rhs) == ASHIFT - && GET_CODE (XEXP (rhs, 1)) == CONST_INT - && INTVAL (XEXP (rhs, 1)) >= 0 - && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT) - { - coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1)); - rhs = XEXP (rhs, 0); - } - - if (rtx_equal_p (lhs, rhs)) - { - tem = simplify_gen_binary (MULT, mode, lhs, - GEN_INT (coeff0 + coeff1)); - return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem; - } + coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0); } - - /* If one of the operands is a PLUS or a MINUS, see if we can - simplify this by the associative law. - Don't use the associative law for floating point. - The inaccuracy makes it nonassociative, - and subtle programs can break if operations are associated. */ - - if (INTEGRAL_MODE_P (mode) - && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS - || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS - || (GET_CODE (op0) == CONST - && GET_CODE (XEXP (op0, 0)) == PLUS) - || (GET_CODE (op1) == CONST - && GET_CODE (XEXP (op1, 0)) == PLUS)) - && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0) - return tem; - - /* Reassociate floating point addition only when the user - specifies unsafe math optimizations. */ - if (FLOAT_MODE_P (mode) - && flag_unsafe_math_optimizations) + else if (GET_CODE (lhs) == ASHIFT + && GET_CODE (XEXP (lhs, 1)) == CONST_INT + && INTVAL (XEXP (lhs, 1)) >= 0 + && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT) { - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; + coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1)); + lhs = XEXP (lhs, 0); } - break; - case COMPARE: -#ifdef HAVE_cc0 - /* Convert (compare FOO (const_int 0)) to FOO unless we aren't - using cc0, in which case we want to leave it as a COMPARE - so we can distinguish it from a register-register-copy. - - In IEEE floating point, x-0 is not the same as x. */ - - if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT - || ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations) - && trueop1 == CONST0_RTX (mode)) - return op0; -#endif - - /* Convert (compare (gt (flags) 0) (lt (flags) 0)) to (flags). */ - if (((GET_CODE (op0) == GT && GET_CODE (op1) == LT) - || (GET_CODE (op0) == GTU && GET_CODE (op1) == LTU)) - && XEXP (op0, 1) == const0_rtx && XEXP (op1, 1) == const0_rtx) + if (GET_CODE (rhs) == NEG) + coeff1 = - 1, rhs = XEXP (rhs, 0); + else if (GET_CODE (rhs) == MULT + && GET_CODE (XEXP (rhs, 1)) == CONST_INT) + { + coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0); + } + else if (GET_CODE (rhs) == ASHIFT + && GET_CODE (XEXP (rhs, 1)) == CONST_INT + && INTVAL (XEXP (rhs, 1)) >= 0 + && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT) { - rtx xop00 = XEXP (op0, 0); - rtx xop10 = XEXP (op1, 0); + coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1)); + rhs = XEXP (rhs, 0); + } -#ifdef HAVE_cc0 - if (GET_CODE (xop00) == CC0 && GET_CODE (xop10) == CC0) -#else - if (GET_CODE (xop00) == REG && GET_CODE (xop10) == REG - && GET_MODE (xop00) == GET_MODE (xop10) - && REGNO (xop00) == REGNO (xop10) - && GET_MODE_CLASS (GET_MODE (xop00)) == MODE_CC - && GET_MODE_CLASS (GET_MODE (xop10)) == MODE_CC) -#endif - return xop00; + if (rtx_equal_p (lhs, rhs)) + { + rtx orig = gen_rtx_MINUS (mode, op0, op1); + tem = simplify_gen_binary (MULT, mode, lhs, + GEN_INT (coeff0 - coeff1)); + return rtx_cost (tem, SET) <= rtx_cost (orig, SET) + ? tem : 0; } - break; + } - case MINUS: - /* We can't assume x-x is 0 even with non-IEEE floating point, - but since it is zero except in very strange circumstances, we - will treat it as zero with -funsafe-math-optimizations. */ - if (rtx_equal_p (trueop0, trueop1) - && ! side_effects_p (op0) - && (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)) - return CONST0_RTX (mode); + /* (a - (-b)) -> (a + b). True even for IEEE. */ + if (GET_CODE (op1) == NEG) + return simplify_gen_binary (PLUS, mode, op0, XEXP (op1, 0)); - /* Change subtraction from zero into negation. (0 - x) is the - same as -x when x is NaN, infinite, or finite and nonzero. - But if the mode has signed zeros, and does not round towards - -infinity, then 0 - 0 is 0, not -0. */ - if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode)) - return simplify_gen_unary (NEG, mode, op1, mode); - - /* (-1 - a) is ~a. */ - if (trueop0 == constm1_rtx) - return simplify_gen_unary (NOT, mode, op1, mode); - - /* Subtracting 0 has no effect unless the mode has signed zeros - and supports rounding towards -infinity. In such a case, - 0 - 0 is -0. */ - if (!(HONOR_SIGNED_ZEROS (mode) - && HONOR_SIGN_DEPENDENT_ROUNDING (mode)) - && trueop1 == CONST0_RTX (mode)) - return op0; + /* (-x - c) may be simplified as (-c - x). */ + if (GET_CODE (op0) == NEG + && (GET_CODE (op1) == CONST_INT + || GET_CODE (op1) == CONST_DOUBLE)) + { + tem = simplify_unary_operation (NEG, mode, op1, mode); + if (tem) + return simplify_gen_binary (MINUS, mode, tem, XEXP (op0, 0)); + } - /* See if this is something like X * C - X or vice versa or - if the multiplication is written as a shift. If so, we can - distribute and make a new multiply, shift, or maybe just - have X (if C is 2 in the example above). But don't make - real multiply if we didn't have one before. */ + /* If one of the operands is a PLUS or a MINUS, see if we can + simplify this by the associative law. + Don't use the associative law for floating point. + The inaccuracy makes it nonassociative, + and subtle programs can break if operations are associated. */ - if (! FLOAT_MODE_P (mode)) - { - HOST_WIDE_INT coeff0 = 1, coeff1 = 1; - rtx lhs = op0, rhs = op1; - int had_mult = 0; - - if (GET_CODE (lhs) == NEG) - coeff0 = -1, lhs = XEXP (lhs, 0); - else if (GET_CODE (lhs) == MULT - && GET_CODE (XEXP (lhs, 1)) == CONST_INT) - { - coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0); - had_mult = 1; - } - else if (GET_CODE (lhs) == ASHIFT - && GET_CODE (XEXP (lhs, 1)) == CONST_INT - && INTVAL (XEXP (lhs, 1)) >= 0 - && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT) - { - coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1)); - lhs = XEXP (lhs, 0); - } + if (INTEGRAL_MODE_P (mode) + && (plus_minus_operand_p (op0) + || plus_minus_operand_p (op1)) + && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0) + return tem; - if (GET_CODE (rhs) == NEG) - coeff1 = - 1, rhs = XEXP (rhs, 0); - else if (GET_CODE (rhs) == MULT - && GET_CODE (XEXP (rhs, 1)) == CONST_INT) - { - coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0); - had_mult = 1; - } - else if (GET_CODE (rhs) == ASHIFT - && GET_CODE (XEXP (rhs, 1)) == CONST_INT - && INTVAL (XEXP (rhs, 1)) >= 0 - && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT) - { - coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1)); - rhs = XEXP (rhs, 0); - } + /* Don't let a relocatable value get a negative coeff. */ + if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode) + return simplify_gen_binary (PLUS, mode, + op0, + neg_const_int (mode, op1)); - if (rtx_equal_p (lhs, rhs)) - { - tem = simplify_gen_binary (MULT, mode, lhs, - GEN_INT (coeff0 - coeff1)); - return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem; - } + /* (x - (x & y)) -> (x & ~y) */ + if (GET_CODE (op1) == AND) + { + if (rtx_equal_p (op0, XEXP (op1, 0))) + { + tem = simplify_gen_unary (NOT, mode, XEXP (op1, 1), + GET_MODE (XEXP (op1, 1))); + return simplify_gen_binary (AND, mode, op0, tem); } - - /* (a - (-b)) -> (a + b). True even for IEEE. */ - if (GET_CODE (op1) == NEG) - return simplify_gen_binary (PLUS, mode, op0, XEXP (op1, 0)); - - /* (-x - c) may be simplified as (-c - x). */ - if (GET_CODE (op0) == NEG - && (GET_CODE (op1) == CONST_INT - || GET_CODE (op1) == CONST_DOUBLE)) + if (rtx_equal_p (op0, XEXP (op1, 1))) { - tem = simplify_unary_operation (NEG, mode, op1, mode); - if (tem) - return simplify_gen_binary (MINUS, mode, tem, XEXP (op0, 0)); + tem = simplify_gen_unary (NOT, mode, XEXP (op1, 0), + GET_MODE (XEXP (op1, 0))); + return simplify_gen_binary (AND, mode, op0, tem); } + } + break; - /* If one of the operands is a PLUS or a MINUS, see if we can - simplify this by the associative law. - Don't use the associative law for floating point. - The inaccuracy makes it nonassociative, - and subtle programs can break if operations are associated. */ - - if (INTEGRAL_MODE_P (mode) - && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS - || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS - || (GET_CODE (op0) == CONST - && GET_CODE (XEXP (op0, 0)) == PLUS) - || (GET_CODE (op1) == CONST - && GET_CODE (XEXP (op1, 0)) == PLUS)) - && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0) - return tem; + case MULT: + if (trueop1 == constm1_rtx) + return simplify_gen_unary (NEG, mode, op0, mode); + + /* Maybe simplify x * 0 to 0. The reduction is not valid if + x is NaN, since x * 0 is then also NaN. Nor is it valid + when the mode has signed zeros, since multiplying a negative + number by 0 will give -0, not 0. */ + if (!HONOR_NANS (mode) + && !HONOR_SIGNED_ZEROS (mode) + && trueop1 == CONST0_RTX (mode) + && ! side_effects_p (op0)) + return op1; - /* Don't let a relocatable value get a negative coeff. */ - if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode) - return simplify_gen_binary (PLUS, mode, - op0, - neg_const_int (mode, op1)); + /* In IEEE floating point, x*1 is not equivalent to x for + signalling NaNs. */ + if (!HONOR_SNANS (mode) + && trueop1 == CONST1_RTX (mode)) + return op0; + + /* Convert multiply by constant power of two into shift unless + we are still generating RTL. This test is a kludge. */ + if (GET_CODE (trueop1) == CONST_INT + && (val = exact_log2 (INTVAL (trueop1))) >= 0 + /* If the mode is larger than the host word size, and the + uppermost bit is set, then this isn't a power of two due + to implicit sign extension. */ + && (width <= HOST_BITS_PER_WIDE_INT + || val != HOST_BITS_PER_WIDE_INT - 1)) + return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val)); + + /* x*2 is x+x and x*(-1) is -x */ + if (GET_CODE (trueop1) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT + && GET_MODE (op0) == mode) + { + REAL_VALUE_TYPE d; + REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1); - /* (x - (x & y)) -> (x & ~y) */ - if (GET_CODE (op1) == AND) - { - if (rtx_equal_p (op0, XEXP (op1, 0))) - { - tem = simplify_gen_unary (NOT, mode, XEXP (op1, 1), - GET_MODE (XEXP (op1, 1))); - return simplify_gen_binary (AND, mode, op0, tem); - } - if (rtx_equal_p (op0, XEXP (op1, 1))) - { - tem = simplify_gen_unary (NOT, mode, XEXP (op1, 0), - GET_MODE (XEXP (op1, 0))); - return simplify_gen_binary (AND, mode, op0, tem); - } - } - break; + if (REAL_VALUES_EQUAL (d, dconst2)) + return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0)); - case MULT: - if (trueop1 == constm1_rtx) + if (REAL_VALUES_EQUAL (d, dconstm1)) return simplify_gen_unary (NEG, mode, op0, mode); + } - /* Maybe simplify x * 0 to 0. The reduction is not valid if - x is NaN, since x * 0 is then also NaN. Nor is it valid - when the mode has signed zeros, since multiplying a negative - number by 0 will give -0, not 0. */ - if (!HONOR_NANS (mode) - && !HONOR_SIGNED_ZEROS (mode) - && trueop1 == CONST0_RTX (mode) - && ! side_effects_p (op0)) - return op1; + /* Reassociate multiplication, but for floating point MULTs + only when the user specifies unsafe math optimizations. */ + if (! FLOAT_MODE_P (mode) + || flag_unsafe_math_optimizations) + { + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + } + break; - /* In IEEE floating point, x*1 is not equivalent to x for - signalling NaNs. */ - if (!HONOR_SNANS (mode) - && trueop1 == CONST1_RTX (mode)) - return op0; + case IOR: + if (trueop1 == const0_rtx) + return op0; + if (GET_CODE (trueop1) == CONST_INT + && ((INTVAL (trueop1) & GET_MODE_MASK (mode)) + == GET_MODE_MASK (mode))) + return op1; + if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) + return op0; + /* A | (~A) -> -1 */ + if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) + || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return constm1_rtx; + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - /* Convert multiply by constant power of two into shift unless - we are still generating RTL. This test is a kludge. */ - if (GET_CODE (trueop1) == CONST_INT - && (val = exact_log2 (INTVAL (trueop1))) >= 0 - /* If the mode is larger than the host word size, and the - uppermost bit is set, then this isn't a power of two due - to implicit sign extension. */ - && (width <= HOST_BITS_PER_WIDE_INT - || val != HOST_BITS_PER_WIDE_INT - 1) - && ! rtx_equal_function_value_matters) - return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val)); - - /* x*2 is x+x and x*(-1) is -x */ - if (GET_CODE (trueop1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT - && GET_MODE (op0) == mode) - { - REAL_VALUE_TYPE d; - REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1); + case XOR: + if (trueop1 == const0_rtx) + return op0; + if (GET_CODE (trueop1) == CONST_INT + && ((INTVAL (trueop1) & GET_MODE_MASK (mode)) + == GET_MODE_MASK (mode))) + return simplify_gen_unary (NOT, mode, op0, mode); + if (trueop0 == trueop1 + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return const0_rtx; + + /* Canonicalize XOR of the most significant bit to PLUS. */ + if ((GET_CODE (op1) == CONST_INT + || GET_CODE (op1) == CONST_DOUBLE) + && mode_signbit_p (mode, op1)) + return simplify_gen_binary (PLUS, mode, op0, op1); + /* (xor (plus X C1) C2) is (xor X (C1^C2)) if C1 is signbit. */ + if ((GET_CODE (op1) == CONST_INT + || GET_CODE (op1) == CONST_DOUBLE) + && GET_CODE (op0) == PLUS + && (GET_CODE (XEXP (op0, 1)) == CONST_INT + || GET_CODE (XEXP (op0, 1)) == CONST_DOUBLE) + && mode_signbit_p (mode, XEXP (op0, 1))) + return simplify_gen_binary (XOR, mode, XEXP (op0, 0), + simplify_gen_binary (XOR, mode, op1, + XEXP (op0, 1))); + + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - if (REAL_VALUES_EQUAL (d, dconst2)) - return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0)); + case AND: + if (trueop1 == const0_rtx && ! side_effects_p (op0)) + return const0_rtx; + /* If we are turning off bits already known off in OP0, we need + not do an AND. */ + if (GET_CODE (trueop1) == CONST_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (nonzero_bits (trueop0, mode) & ~INTVAL (trueop1)) == 0) + return op0; + if (trueop0 == trueop1 && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return op0; + /* A & (~A) -> 0 */ + if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) + || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) + && ! side_effects_p (op0) + && GET_MODE_CLASS (mode) != MODE_CC) + return const0_rtx; + + /* Transform (and (extend X) C) into (zero_extend (and X C)) if + there are no nonzero bits of C outside of X's mode. */ + if ((GET_CODE (op0) == SIGN_EXTEND + || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (trueop1) == CONST_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && (~GET_MODE_MASK (GET_MODE (XEXP (op0, 0))) + & INTVAL (trueop1)) == 0) + { + enum machine_mode imode = GET_MODE (XEXP (op0, 0)); + tem = simplify_gen_binary (AND, imode, XEXP (op0, 0), + gen_int_mode (INTVAL (trueop1), + imode)); + return simplify_gen_unary (ZERO_EXTEND, mode, tem, imode); + } - if (REAL_VALUES_EQUAL (d, dconstm1)) - return simplify_gen_unary (NEG, mode, op0, mode); - } + /* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M, + ((A & N) + B) & M -> (A + B) & M + Similarly if (N & M) == 0, + ((A | N) + B) & M -> (A + B) & M + and for - instead of + and/or ^ instead of |. */ + if (GET_CODE (trueop1) == CONST_INT + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT + && ~INTVAL (trueop1) + && (INTVAL (trueop1) & (INTVAL (trueop1) + 1)) == 0 + && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS)) + { + rtx pmop[2]; + int which; + + pmop[0] = XEXP (op0, 0); + pmop[1] = XEXP (op0, 1); - /* Reassociate multiplication, but for floating point MULTs - only when the user specifies unsafe math optimizations. */ - if (! FLOAT_MODE_P (mode) - || flag_unsafe_math_optimizations) + for (which = 0; which < 2; which++) { - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; + tem = pmop[which]; + switch (GET_CODE (tem)) + { + case AND: + if (GET_CODE (XEXP (tem, 1)) == CONST_INT + && (INTVAL (XEXP (tem, 1)) & INTVAL (trueop1)) + == INTVAL (trueop1)) + pmop[which] = XEXP (tem, 0); + break; + case IOR: + case XOR: + if (GET_CODE (XEXP (tem, 1)) == CONST_INT + && (INTVAL (XEXP (tem, 1)) & INTVAL (trueop1)) == 0) + pmop[which] = XEXP (tem, 0); + break; + default: + break; + } } - break; - - case IOR: - if (trueop1 == const0_rtx) - return op0; - if (GET_CODE (trueop1) == CONST_INT - && ((INTVAL (trueop1) & GET_MODE_MASK (mode)) - == GET_MODE_MASK (mode))) - return op1; - if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) - return op0; - /* A | (~A) -> -1 */ - if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) - || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) - && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return constm1_rtx; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; - - case XOR: - if (trueop1 == const0_rtx) - return op0; - if (GET_CODE (trueop1) == CONST_INT - && ((INTVAL (trueop1) & GET_MODE_MASK (mode)) - == GET_MODE_MASK (mode))) - return simplify_gen_unary (NOT, mode, op0, mode); - if (trueop0 == trueop1 && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return const0_rtx; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; - - case AND: - if (trueop1 == const0_rtx && ! side_effects_p (op0)) - return const0_rtx; - if (GET_CODE (trueop1) == CONST_INT - && ((INTVAL (trueop1) & GET_MODE_MASK (mode)) - == GET_MODE_MASK (mode))) - return op0; - if (trueop0 == trueop1 && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return op0; - /* A & (~A) -> 0 */ - if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1)) - || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0))) - && ! side_effects_p (op0) - && GET_MODE_CLASS (mode) != MODE_CC) - return const0_rtx; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; - - case UDIV: - /* Convert divide by power of two into shift (divide by 1 handled - below). */ - if (GET_CODE (trueop1) == CONST_INT - && (arg1 = exact_log2 (INTVAL (trueop1))) > 0) - return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (arg1)); - /* Fall through.... */ + if (pmop[0] != XEXP (op0, 0) || pmop[1] != XEXP (op0, 1)) + { + tem = simplify_gen_binary (GET_CODE (op0), mode, + pmop[0], pmop[1]); + return simplify_gen_binary (code, mode, tem, op1); + } + } + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - case DIV: - if (trueop1 == CONST1_RTX (mode)) + case UDIV: + /* 0/x is 0 (or x&0 if x has side-effects). */ + if (trueop0 == const0_rtx) + return side_effects_p (op1) + ? simplify_gen_binary (AND, mode, op1, const0_rtx) + : const0_rtx; + /* x/1 is x. */ + if (trueop1 == const1_rtx) { - /* On some platforms DIV uses narrower mode than its - operands. */ + /* Handle narrowing UDIV. */ rtx x = gen_lowpart_common (mode, op0); if (x) return x; - else if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode) + if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode) return gen_lowpart_SUBREG (mode, op0); - else - return op0; + return op0; } + /* Convert divide by power of two into shift. */ + if (GET_CODE (trueop1) == CONST_INT + && (val = exact_log2 (INTVAL (trueop1))) > 0) + return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (val)); + break; - /* Maybe change 0 / x to 0. This transformation isn't safe for - modes with NaNs, since 0 / 0 will then be NaN rather than 0. - Nor is it safe for modes with signed zeros, since dividing - 0 by a negative number gives -0, not 0. */ - if (!HONOR_NANS (mode) + case DIV: + /* Handle floating point and integers separately. */ + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + /* Maybe change 0.0 / x to 0.0. This transformation isn't + safe for modes with NaNs, since 0.0 / 0.0 will then be + NaN rather than 0.0. Nor is it safe for modes with signed + zeros, since dividing 0 by a negative number gives -0.0 */ + if (trueop0 == CONST0_RTX (mode) + && !HONOR_NANS (mode) && !HONOR_SIGNED_ZEROS (mode) - && trueop0 == CONST0_RTX (mode) && ! side_effects_p (op1)) return op0; + /* x/1.0 is x. */ + if (trueop1 == CONST1_RTX (mode) + && !HONOR_SNANS (mode)) + return op0; - /* Change division by a constant into multiplication. Only do - this with -funsafe-math-optimizations. */ - else if (GET_CODE (trueop1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT - && trueop1 != CONST0_RTX (mode) - && flag_unsafe_math_optimizations) + if (GET_CODE (trueop1) == CONST_DOUBLE + && trueop1 != CONST0_RTX (mode)) { REAL_VALUE_TYPE d; REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1); - if (! REAL_VALUES_EQUAL (d, dconst0)) + /* x/-1.0 is -x. */ + if (REAL_VALUES_EQUAL (d, dconstm1) + && !HONOR_SNANS (mode)) + return simplify_gen_unary (NEG, mode, op0, mode); + + /* Change FP division by a constant into multiplication. + Only do this with -funsafe-math-optimizations. */ + if (flag_unsafe_math_optimizations + && !REAL_VALUES_EQUAL (d, dconst0)) { - REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d); + REAL_ARITHMETIC (d, RDIV_EXPR, dconst1, d); tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode); return simplify_gen_binary (MULT, mode, op0, tem); } } - break; - - case UMOD: - /* Handle modulus by power of two (mod with 1 handled below). */ + } + else + { + /* 0/x is 0 (or x&0 if x has side-effects). */ + if (trueop0 == const0_rtx) + return side_effects_p (op1) + ? simplify_gen_binary (AND, mode, op1, const0_rtx) + : const0_rtx; + /* x/1 is x. */ + if (trueop1 == const1_rtx) + { + /* Handle narrowing DIV. */ + rtx x = gen_lowpart_common (mode, op0); + if (x) + return x; + if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode) + return gen_lowpart_SUBREG (mode, op0); + return op0; + } + /* x/-1 is -x. */ + if (trueop1 == constm1_rtx) + { + rtx x = gen_lowpart_common (mode, op0); + if (!x) + x = (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode) + ? gen_lowpart_SUBREG (mode, op0) : op0; + return simplify_gen_unary (NEG, mode, x, mode); + } + } + break; + + case UMOD: + /* 0%x is 0 (or x&0 if x has side-effects). */ + if (trueop0 == const0_rtx) + return side_effects_p (op1) + ? simplify_gen_binary (AND, mode, op1, const0_rtx) + : const0_rtx; + /* x%1 is 0 (of x&0 if x has side-effects). */ + if (trueop1 == const1_rtx) + return side_effects_p (op0) + ? simplify_gen_binary (AND, mode, op0, const0_rtx) + : const0_rtx; + /* Implement modulus by power of two as AND. */ if (GET_CODE (trueop1) == CONST_INT && exact_log2 (INTVAL (trueop1)) > 0) return simplify_gen_binary (AND, mode, op0, GEN_INT (INTVAL (op1) - 1)); + break; - /* Fall through.... */ - - case MOD: - if ((trueop0 == const0_rtx || trueop1 == const1_rtx) - && ! side_effects_p (op0) && ! side_effects_p (op1)) - return const0_rtx; + case MOD: + /* 0%x is 0 (or x&0 if x has side-effects). */ + if (trueop0 == const0_rtx) + return side_effects_p (op1) + ? simplify_gen_binary (AND, mode, op1, const0_rtx) + : const0_rtx; + /* x%1 and x%-1 is 0 (or x&0 if x has side-effects). */ + if (trueop1 == const1_rtx || trueop1 == constm1_rtx) + return side_effects_p (op0) + ? simplify_gen_binary (AND, mode, op0, const0_rtx) + : const0_rtx; break; - case ROTATERT: - case ROTATE: - case ASHIFTRT: - /* Rotating ~0 always results in ~0. */ - if (GET_CODE (trueop0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT - && (unsigned HOST_WIDE_INT) INTVAL (trueop0) == GET_MODE_MASK (mode) - && ! side_effects_p (op1)) - return op0; + case ROTATERT: + case ROTATE: + case ASHIFTRT: + /* Rotating ~0 always results in ~0. */ + if (GET_CODE (trueop0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT + && (unsigned HOST_WIDE_INT) INTVAL (trueop0) == GET_MODE_MASK (mode) + && ! side_effects_p (op1)) + return op0; - /* Fall through.... */ + /* Fall through.... */ - case ASHIFT: - case LSHIFTRT: - if (trueop1 == const0_rtx) - return op0; - if (trueop0 == const0_rtx && ! side_effects_p (op1)) - return op0; - break; + case ASHIFT: + case LSHIFTRT: + if (trueop1 == const0_rtx) + return op0; + if (trueop0 == const0_rtx && ! side_effects_p (op1)) + return op0; + break; - case SMIN: - if (width <= HOST_BITS_PER_WIDE_INT - && GET_CODE (trueop1) == CONST_INT - && INTVAL (trueop1) == (HOST_WIDE_INT) 1 << (width -1) - && ! side_effects_p (op0)) - return op1; - if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) - return op0; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; + case SMIN: + if (width <= HOST_BITS_PER_WIDE_INT + && GET_CODE (trueop1) == CONST_INT + && INTVAL (trueop1) == (HOST_WIDE_INT) 1 << (width -1) + && ! side_effects_p (op0)) + return op1; + if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) + return op0; + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - case SMAX: - if (width <= HOST_BITS_PER_WIDE_INT - && GET_CODE (trueop1) == CONST_INT - && ((unsigned HOST_WIDE_INT) INTVAL (trueop1) - == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1) - && ! side_effects_p (op0)) - return op1; - if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) - return op0; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; + case SMAX: + if (width <= HOST_BITS_PER_WIDE_INT + && GET_CODE (trueop1) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (trueop1) + == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1) + && ! side_effects_p (op0)) + return op1; + if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) + return op0; + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - case UMIN: - if (trueop1 == const0_rtx && ! side_effects_p (op0)) - return op1; - if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) - return op0; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; + case UMIN: + if (trueop1 == const0_rtx && ! side_effects_p (op0)) + return op1; + if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) + return op0; + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - case UMAX: - if (trueop1 == constm1_rtx && ! side_effects_p (op0)) - return op1; - if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) - return op0; - tem = simplify_associative_operation (code, mode, op0, op1); - if (tem) - return tem; - break; + case UMAX: + if (trueop1 == constm1_rtx && ! side_effects_p (op0)) + return op1; + if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0)) + return op0; + tem = simplify_associative_operation (code, mode, op0, op1); + if (tem) + return tem; + break; - case SS_PLUS: - case US_PLUS: - case SS_MINUS: - case US_MINUS: - /* ??? There are simplifications that can be done. */ - return 0; + case SS_PLUS: + case US_PLUS: + case SS_MINUS: + case US_MINUS: + /* ??? There are simplifications that can be done. */ + return 0; - case VEC_SELECT: - if (!VECTOR_MODE_P (mode)) - { - if (!VECTOR_MODE_P (GET_MODE (trueop0)) - || (mode - != GET_MODE_INNER (GET_MODE (trueop0))) - || GET_CODE (trueop1) != PARALLEL - || XVECLEN (trueop1, 0) != 1 - || GET_CODE (XVECEXP (trueop1, 0, 0)) != CONST_INT) - abort (); - - if (GET_CODE (trueop0) == CONST_VECTOR) - return CONST_VECTOR_ELT (trueop0, INTVAL (XVECEXP (trueop1, 0, 0))); - } - else + case VEC_SELECT: + if (!VECTOR_MODE_P (mode)) + { + gcc_assert (VECTOR_MODE_P (GET_MODE (trueop0))); + gcc_assert (mode == GET_MODE_INNER (GET_MODE (trueop0))); + gcc_assert (GET_CODE (trueop1) == PARALLEL); + gcc_assert (XVECLEN (trueop1, 0) == 1); + gcc_assert (GET_CODE (XVECEXP (trueop1, 0, 0)) == CONST_INT); + + if (GET_CODE (trueop0) == CONST_VECTOR) + return CONST_VECTOR_ELT (trueop0, INTVAL (XVECEXP + (trueop1, 0, 0))); + } + else + { + gcc_assert (VECTOR_MODE_P (GET_MODE (trueop0))); + gcc_assert (GET_MODE_INNER (mode) + == GET_MODE_INNER (GET_MODE (trueop0))); + gcc_assert (GET_CODE (trueop1) == PARALLEL); + + if (GET_CODE (trueop0) == CONST_VECTOR) { - if (!VECTOR_MODE_P (GET_MODE (trueop0)) - || (GET_MODE_INNER (mode) - != GET_MODE_INNER (GET_MODE (trueop0))) - || GET_CODE (trueop1) != PARALLEL) - abort (); + int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); + unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); + rtvec v = rtvec_alloc (n_elts); + unsigned int i; - if (GET_CODE (trueop0) == CONST_VECTOR) + gcc_assert (XVECLEN (trueop1, 0) == (int) n_elts); + for (i = 0; i < n_elts; i++) { - int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); - unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); - rtvec v = rtvec_alloc (n_elts); - unsigned int i; - - if (XVECLEN (trueop1, 0) != (int) n_elts) - abort (); - for (i = 0; i < n_elts; i++) - { - rtx x = XVECEXP (trueop1, 0, i); - - if (GET_CODE (x) != CONST_INT) - abort (); - RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, INTVAL (x)); - } - - return gen_rtx_CONST_VECTOR (mode, v); + rtx x = XVECEXP (trueop1, 0, i); + + gcc_assert (GET_CODE (x) == CONST_INT); + RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, + INTVAL (x)); } + + return gen_rtx_CONST_VECTOR (mode, v); } - return 0; - case VEC_CONCAT: + } + return 0; + case VEC_CONCAT: + { + enum machine_mode op0_mode = (GET_MODE (trueop0) != VOIDmode + ? GET_MODE (trueop0) + : GET_MODE_INNER (mode)); + enum machine_mode op1_mode = (GET_MODE (trueop1) != VOIDmode + ? GET_MODE (trueop1) + : GET_MODE_INNER (mode)); + + gcc_assert (VECTOR_MODE_P (mode)); + gcc_assert (GET_MODE_SIZE (op0_mode) + GET_MODE_SIZE (op1_mode) + == GET_MODE_SIZE (mode)); + + if (VECTOR_MODE_P (op0_mode)) + gcc_assert (GET_MODE_INNER (mode) + == GET_MODE_INNER (op0_mode)); + else + gcc_assert (GET_MODE_INNER (mode) == op0_mode); + + if (VECTOR_MODE_P (op1_mode)) + gcc_assert (GET_MODE_INNER (mode) + == GET_MODE_INNER (op1_mode)); + else + gcc_assert (GET_MODE_INNER (mode) == op1_mode); + + if ((GET_CODE (trueop0) == CONST_VECTOR + || GET_CODE (trueop0) == CONST_INT + || GET_CODE (trueop0) == CONST_DOUBLE) + && (GET_CODE (trueop1) == CONST_VECTOR + || GET_CODE (trueop1) == CONST_INT + || GET_CODE (trueop1) == CONST_DOUBLE)) { - enum machine_mode op0_mode = (GET_MODE (trueop0) != VOIDmode - ? GET_MODE (trueop0) - : GET_MODE_INNER (mode)); - enum machine_mode op1_mode = (GET_MODE (trueop1) != VOIDmode - ? GET_MODE (trueop1) - : GET_MODE_INNER (mode)); - - if (!VECTOR_MODE_P (mode) - || (GET_MODE_SIZE (op0_mode) + GET_MODE_SIZE (op1_mode) - != GET_MODE_SIZE (mode))) - abort (); - - if ((VECTOR_MODE_P (op0_mode) - && (GET_MODE_INNER (mode) - != GET_MODE_INNER (op0_mode))) - || (!VECTOR_MODE_P (op0_mode) - && GET_MODE_INNER (mode) != op0_mode)) - abort (); - - if ((VECTOR_MODE_P (op1_mode) - && (GET_MODE_INNER (mode) - != GET_MODE_INNER (op1_mode))) - || (!VECTOR_MODE_P (op1_mode) - && GET_MODE_INNER (mode) != op1_mode)) - abort (); - - if ((GET_CODE (trueop0) == CONST_VECTOR - || GET_CODE (trueop0) == CONST_INT - || GET_CODE (trueop0) == CONST_DOUBLE) - && (GET_CODE (trueop1) == CONST_VECTOR - || GET_CODE (trueop1) == CONST_INT - || GET_CODE (trueop1) == CONST_DOUBLE)) + int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); + unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); + rtvec v = rtvec_alloc (n_elts); + unsigned int i; + unsigned in_n_elts = 1; + + if (VECTOR_MODE_P (op0_mode)) + in_n_elts = (GET_MODE_SIZE (op0_mode) / elt_size); + for (i = 0; i < n_elts; i++) { - int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode)); - unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size); - rtvec v = rtvec_alloc (n_elts); - unsigned int i; - unsigned in_n_elts = 1; - - if (VECTOR_MODE_P (op0_mode)) - in_n_elts = (GET_MODE_SIZE (op0_mode) / elt_size); - for (i = 0; i < n_elts; i++) + if (i < in_n_elts) { - if (i < in_n_elts) - { - if (!VECTOR_MODE_P (op0_mode)) - RTVEC_ELT (v, i) = trueop0; - else - RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, i); - } + if (!VECTOR_MODE_P (op0_mode)) + RTVEC_ELT (v, i) = trueop0; else - { - if (!VECTOR_MODE_P (op1_mode)) - RTVEC_ELT (v, i) = trueop1; - else - RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop1, - i - in_n_elts); - } + RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, i); + } + else + { + if (!VECTOR_MODE_P (op1_mode)) + RTVEC_ELT (v, i) = trueop1; + else + RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop1, + i - in_n_elts); } - - return gen_rtx_CONST_VECTOR (mode, v); } - } - return 0; - - default: - abort (); - } + return gen_rtx_CONST_VECTOR (mode, v); + } + } return 0; + + default: + gcc_unreachable (); } - /* Get the integer argument values in two forms: - zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ + return 0; +} - arg0 = INTVAL (trueop0); - arg1 = INTVAL (trueop1); +rtx +simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode, + rtx op0, rtx op1) +{ + HOST_WIDE_INT arg0, arg1, arg0s, arg1s; + HOST_WIDE_INT val; + unsigned int width = GET_MODE_BITSIZE (mode); - if (width < HOST_BITS_PER_WIDE_INT) + if (VECTOR_MODE_P (mode) + && code != VEC_CONCAT + && GET_CODE (op0) == CONST_VECTOR + && GET_CODE (op1) == CONST_VECTOR) { - arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; - arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; + unsigned n_elts = GET_MODE_NUNITS (mode); + enum machine_mode op0mode = GET_MODE (op0); + unsigned op0_n_elts = GET_MODE_NUNITS (op0mode); + enum machine_mode op1mode = GET_MODE (op1); + unsigned op1_n_elts = GET_MODE_NUNITS (op1mode); + rtvec v = rtvec_alloc (n_elts); + unsigned int i; - arg0s = arg0; - if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg0s |= ((HOST_WIDE_INT) (-1) << width); + gcc_assert (op0_n_elts == n_elts); + gcc_assert (op1_n_elts == n_elts); + for (i = 0; i < n_elts; i++) + { + rtx x = simplify_binary_operation (code, GET_MODE_INNER (mode), + CONST_VECTOR_ELT (op0, i), + CONST_VECTOR_ELT (op1, i)); + if (!x) + return 0; + RTVEC_ELT (v, i) = x; + } - arg1s = arg1; - if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) - arg1s |= ((HOST_WIDE_INT) (-1) << width); + return gen_rtx_CONST_VECTOR (mode, v); } - else + + if (VECTOR_MODE_P (mode) + && code == VEC_CONCAT + && CONSTANT_P (op0) && CONSTANT_P (op1)) { - arg0s = arg0; - arg1s = arg1; - } + unsigned n_elts = GET_MODE_NUNITS (mode); + rtvec v = rtvec_alloc (n_elts); - /* Compute the value of the arithmetic. */ + gcc_assert (n_elts >= 2); + if (n_elts == 2) + { + gcc_assert (GET_CODE (op0) != CONST_VECTOR); + gcc_assert (GET_CODE (op1) != CONST_VECTOR); - switch (code) + RTVEC_ELT (v, 0) = op0; + RTVEC_ELT (v, 1) = op1; + } + else + { + unsigned op0_n_elts = GET_MODE_NUNITS (GET_MODE (op0)); + unsigned op1_n_elts = GET_MODE_NUNITS (GET_MODE (op1)); + unsigned i; + + gcc_assert (GET_CODE (op0) == CONST_VECTOR); + gcc_assert (GET_CODE (op1) == CONST_VECTOR); + gcc_assert (op0_n_elts + op1_n_elts == n_elts); + + for (i = 0; i < op0_n_elts; ++i) + RTVEC_ELT (v, i) = XVECEXP (op0, 0, i); + for (i = 0; i < op1_n_elts; ++i) + RTVEC_ELT (v, op0_n_elts+i) = XVECEXP (op1, 0, i); + } + + return gen_rtx_CONST_VECTOR (mode, v); + } + + if (GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_CODE (op0) == CONST_DOUBLE + && GET_CODE (op1) == CONST_DOUBLE + && mode == GET_MODE (op0) && mode == GET_MODE (op1)) { - case PLUS: - val = arg0s + arg1s; - break; + if (code == AND + || code == IOR + || code == XOR) + { + long tmp0[4]; + long tmp1[4]; + REAL_VALUE_TYPE r; + int i; + + real_to_target (tmp0, CONST_DOUBLE_REAL_VALUE (op0), + GET_MODE (op0)); + real_to_target (tmp1, CONST_DOUBLE_REAL_VALUE (op1), + GET_MODE (op1)); + for (i = 0; i < 4; i++) + { + switch (code) + { + case AND: + tmp0[i] &= tmp1[i]; + break; + case IOR: + tmp0[i] |= tmp1[i]; + break; + case XOR: + tmp0[i] ^= tmp1[i]; + break; + default: + gcc_unreachable (); + } + } + real_from_target (&r, tmp0, mode); + return CONST_DOUBLE_FROM_REAL_VALUE (r, mode); + } + else + { + REAL_VALUE_TYPE f0, f1, value, result; + bool inexact; - case MINUS: - val = arg0s - arg1s; - break; + REAL_VALUE_FROM_CONST_DOUBLE (f0, op0); + REAL_VALUE_FROM_CONST_DOUBLE (f1, op1); + real_convert (&f0, mode, &f0); + real_convert (&f1, mode, &f1); - case MULT: - val = arg0s * arg1s; - break; + if (HONOR_SNANS (mode) + && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1))) + return 0; - case DIV: - if (arg1s == 0 - || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) - && arg1s == -1)) - return 0; - val = arg0s / arg1s; - break; + if (code == DIV + && REAL_VALUES_EQUAL (f1, dconst0) + && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode))) + return 0; - case MOD: - if (arg1s == 0 - || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) - && arg1s == -1)) - return 0; - val = arg0s % arg1s; - break; + if (MODE_HAS_INFINITIES (mode) && HONOR_NANS (mode) + && flag_trapping_math + && REAL_VALUE_ISINF (f0) && REAL_VALUE_ISINF (f1)) + { + int s0 = REAL_VALUE_NEGATIVE (f0); + int s1 = REAL_VALUE_NEGATIVE (f1); - case UDIV: - if (arg1 == 0 - || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) - && arg1s == -1)) - return 0; - val = (unsigned HOST_WIDE_INT) arg0 / arg1; - break; + switch (code) + { + case PLUS: + /* Inf + -Inf = NaN plus exception. */ + if (s0 != s1) + return 0; + break; + case MINUS: + /* Inf - Inf = NaN plus exception. */ + if (s0 == s1) + return 0; + break; + case DIV: + /* Inf / Inf = NaN plus exception. */ + return 0; + default: + break; + } + } - case UMOD: - if (arg1 == 0 - || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) - && arg1s == -1)) - return 0; - val = (unsigned HOST_WIDE_INT) arg0 % arg1; - break; + if (code == MULT && MODE_HAS_INFINITIES (mode) && HONOR_NANS (mode) + && flag_trapping_math + && ((REAL_VALUE_ISINF (f0) && REAL_VALUES_EQUAL (f1, dconst0)) + || (REAL_VALUE_ISINF (f1) + && REAL_VALUES_EQUAL (f0, dconst0)))) + /* Inf * 0 = NaN plus exception. */ + return 0; - case AND: - val = arg0 & arg1; - break; + inexact = real_arithmetic (&value, rtx_to_tree_code (code), + &f0, &f1); + real_convert (&result, mode, &value); - case IOR: - val = arg0 | arg1; - break; + /* Don't constant fold this floating point operation if the + result may dependent upon the run-time rounding mode and + flag_rounding_math is set, or if GCC's software emulation + is unable to accurately represent the result. */ - case XOR: - val = arg0 ^ arg1; - break; + if ((flag_rounding_math + || (REAL_MODE_FORMAT_COMPOSITE_P (mode) + && !flag_unsafe_math_optimizations)) + && (inexact || !real_identical (&result, &value))) + return NULL_RTX; - case LSHIFTRT: - /* If shift count is undefined, don't fold it; let the machine do - what it wants. But truncate it if the machine will do that. */ - if (arg1 < 0) - return 0; - -#ifdef SHIFT_COUNT_TRUNCATED - if (SHIFT_COUNT_TRUNCATED) - arg1 %= width; -#endif + return CONST_DOUBLE_FROM_REAL_VALUE (result, mode); + } + } - val = ((unsigned HOST_WIDE_INT) arg0) >> arg1; - break; + /* We can fold some multi-word operations. */ + if (GET_MODE_CLASS (mode) == MODE_INT + && width == HOST_BITS_PER_WIDE_INT * 2 + && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT) + && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT)) + { + unsigned HOST_WIDE_INT l1, l2, lv, lt; + HOST_WIDE_INT h1, h2, hv, ht; - case ASHIFT: - if (arg1 < 0) - return 0; + if (GET_CODE (op0) == CONST_DOUBLE) + l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0); + else + l1 = INTVAL (op0), h1 = HWI_SIGN_EXTEND (l1); -#ifdef SHIFT_COUNT_TRUNCATED - if (SHIFT_COUNT_TRUNCATED) - arg1 %= width; -#endif + if (GET_CODE (op1) == CONST_DOUBLE) + l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1); + else + l2 = INTVAL (op1), h2 = HWI_SIGN_EXTEND (l2); - val = ((unsigned HOST_WIDE_INT) arg0) << arg1; - break; + switch (code) + { + case MINUS: + /* A - B == A + (-B). */ + neg_double (l2, h2, &lv, &hv); + l2 = lv, h2 = hv; - case ASHIFTRT: - if (arg1 < 0) - return 0; + /* Fall through.... */ -#ifdef SHIFT_COUNT_TRUNCATED - if (SHIFT_COUNT_TRUNCATED) - arg1 %= width; -#endif + case PLUS: + add_double (l1, h1, l2, h2, &lv, &hv); + break; - val = arg0s >> arg1; + case MULT: + mul_double (l1, h1, l2, h2, &lv, &hv); + break; - /* Bootstrap compiler may not have sign extended the right shift. - Manually extend the sign to insure bootstrap cc matches gcc. */ - if (arg0s < 0 && arg1 > 0) - val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1); + case DIV: + if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2, + &lv, &hv, <, &ht)) + return 0; + break; - break; + case MOD: + if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2, + <, &ht, &lv, &hv)) + return 0; + break; - case ROTATERT: - if (arg1 < 0) - return 0; + case UDIV: + if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2, + &lv, &hv, <, &ht)) + return 0; + break; - arg1 %= width; - val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1)) - | (((unsigned HOST_WIDE_INT) arg0) >> arg1)); - break; + case UMOD: + if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2, + <, &ht, &lv, &hv)) + return 0; + break; - case ROTATE: - if (arg1 < 0) - return 0; + case AND: + lv = l1 & l2, hv = h1 & h2; + break; - arg1 %= width; - val = ((((unsigned HOST_WIDE_INT) arg0) << arg1) - | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1))); - break; + case IOR: + lv = l1 | l2, hv = h1 | h2; + break; - case COMPARE: - /* Do nothing here. */ - return 0; + case XOR: + lv = l1 ^ l2, hv = h1 ^ h2; + break; - case SMIN: - val = arg0s <= arg1s ? arg0s : arg1s; - break; + case SMIN: + if (h1 < h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + < (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; - case UMIN: - val = ((unsigned HOST_WIDE_INT) arg0 - <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); - break; + case SMAX: + if (h1 > h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + > (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; - case SMAX: - val = arg0s > arg1s ? arg0s : arg1s; - break; + case UMIN: + if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + < (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; - case UMAX: - val = ((unsigned HOST_WIDE_INT) arg0 - > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); - break; + case UMAX: + if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2 + || (h1 == h2 + && ((unsigned HOST_WIDE_INT) l1 + > (unsigned HOST_WIDE_INT) l2))) + lv = l1, hv = h1; + else + lv = l2, hv = h2; + break; - case SS_PLUS: - case US_PLUS: - case SS_MINUS: - case US_MINUS: - /* ??? There are simplifications that can be done. */ - return 0; + case LSHIFTRT: case ASHIFTRT: + case ASHIFT: + case ROTATE: case ROTATERT: + if (SHIFT_COUNT_TRUNCATED) + l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0; - default: - abort (); + if (h2 != 0 || l2 >= GET_MODE_BITSIZE (mode)) + return 0; + + if (code == LSHIFTRT || code == ASHIFTRT) + rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, + code == ASHIFTRT); + else if (code == ASHIFT) + lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1); + else if (code == ROTATE) + lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); + else /* code == ROTATERT */ + rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv); + break; + + default: + return 0; + } + + return immed_double_const (lv, hv, mode); } - val = trunc_int_for_mode (val, mode); + if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT + && width <= HOST_BITS_PER_WIDE_INT && width != 0) + { + /* Get the integer argument values in two forms: + zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */ + + arg0 = INTVAL (op0); + arg1 = INTVAL (op1); + + if (width < HOST_BITS_PER_WIDE_INT) + { + arg0 &= ((HOST_WIDE_INT) 1 << width) - 1; + arg1 &= ((HOST_WIDE_INT) 1 << width) - 1; + + arg0s = arg0; + if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg0s |= ((HOST_WIDE_INT) (-1) << width); + + arg1s = arg1; + if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1))) + arg1s |= ((HOST_WIDE_INT) (-1) << width); + } + else + { + arg0s = arg0; + arg1s = arg1; + } + + /* Compute the value of the arithmetic. */ + + switch (code) + { + case PLUS: + val = arg0s + arg1s; + break; + + case MINUS: + val = arg0s - arg1s; + break; + + case MULT: + val = arg0s * arg1s; + break; + + case DIV: + if (arg1s == 0 + || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) + && arg1s == -1)) + return 0; + val = arg0s / arg1s; + break; + + case MOD: + if (arg1s == 0 + || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) + && arg1s == -1)) + return 0; + val = arg0s % arg1s; + break; + + case UDIV: + if (arg1 == 0 + || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) + && arg1s == -1)) + return 0; + val = (unsigned HOST_WIDE_INT) arg0 / arg1; + break; + + case UMOD: + if (arg1 == 0 + || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1) + && arg1s == -1)) + return 0; + val = (unsigned HOST_WIDE_INT) arg0 % arg1; + break; + + case AND: + val = arg0 & arg1; + break; + + case IOR: + val = arg0 | arg1; + break; + + case XOR: + val = arg0 ^ arg1; + break; + + case LSHIFTRT: + case ASHIFT: + case ASHIFTRT: + /* Truncate the shift if SHIFT_COUNT_TRUNCATED, otherwise make sure + the value is in range. We can't return any old value for + out-of-range arguments because either the middle-end (via + shift_truncation_mask) or the back-end might be relying on + target-specific knowledge. Nor can we rely on + shift_truncation_mask, since the shift might not be part of an + ashlM3, lshrM3 or ashrM3 instruction. */ + if (SHIFT_COUNT_TRUNCATED) + arg1 = (unsigned HOST_WIDE_INT) arg1 % width; + else if (arg1 < 0 || arg1 >= GET_MODE_BITSIZE (mode)) + return 0; + + val = (code == ASHIFT + ? ((unsigned HOST_WIDE_INT) arg0) << arg1 + : ((unsigned HOST_WIDE_INT) arg0) >> arg1); + + /* Sign-extend the result for arithmetic right shifts. */ + if (code == ASHIFTRT && arg0s < 0 && arg1 > 0) + val |= ((HOST_WIDE_INT) -1) << (width - arg1); + break; + + case ROTATERT: + if (arg1 < 0) + return 0; + + arg1 %= width; + val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1)) + | (((unsigned HOST_WIDE_INT) arg0) >> arg1)); + break; + + case ROTATE: + if (arg1 < 0) + return 0; + + arg1 %= width; + val = ((((unsigned HOST_WIDE_INT) arg0) << arg1) + | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1))); + break; + + case COMPARE: + /* Do nothing here. */ + return 0; + + case SMIN: + val = arg0s <= arg1s ? arg0s : arg1s; + break; + + case UMIN: + val = ((unsigned HOST_WIDE_INT) arg0 + <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); + break; + + case SMAX: + val = arg0s > arg1s ? arg0s : arg1s; + break; + + case UMAX: + val = ((unsigned HOST_WIDE_INT) arg0 + > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1); + break; + + case SS_PLUS: + case US_PLUS: + case SS_MINUS: + case US_MINUS: + /* ??? There are simplifications that can be done. */ + return 0; + + default: + gcc_unreachable (); + } + + return gen_int_mode (val, mode); + } - return GEN_INT (val); + return NULL_RTX; } + + /* Simplify a PLUS or MINUS, at least one of whose operands may be another PLUS or MINUS. @@ -2248,7 +2532,7 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0, struct simplify_plus_minus_op_data ops[8]; rtx result, tem; int n_ops = 2, input_ops = 2, input_consts = 0, n_consts; - int first, negate, changed; + int first, changed; int i, j; memset (ops, 0, sizeof ops); @@ -2458,18 +2742,12 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0, || (n_ops + n_consts == input_ops && n_consts <= input_consts))) return NULL_RTX; - /* Put a non-negated operand first. If there aren't any, make all - operands positive and negate the whole thing later. */ + /* Put a non-negated operand first, if possible. */ - negate = 0; for (i = 0; i < n_ops && ops[i].neg; i++) continue; if (i == n_ops) - { - for (i = 0; i < n_ops; i++) - ops[i].neg = 0; - negate = 1; - } + ops[0].op = gen_rtx_NEG (mode, ops[0].op); else if (i != 0) { tem = ops[0].op; @@ -2484,51 +2762,213 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0, result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS, mode, result, ops[i].op); - return negate ? gen_rtx_NEG (mode, result) : result; + return result; } -/* Like simplify_binary_operation except used for relational operators. - MODE is the mode of the operands, not that of the result. If MODE - is VOIDmode, both operands must also be VOIDmode and we compare the - operands in "infinite precision". +/* Check whether an operand is suitable for calling simplify_plus_minus. */ +static bool +plus_minus_operand_p (rtx x) +{ + return GET_CODE (x) == PLUS + || GET_CODE (x) == MINUS + || (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && CONSTANT_P (XEXP (XEXP (x, 0), 0)) + && CONSTANT_P (XEXP (XEXP (x, 0), 1))); +} - If no simplification is possible, this function returns zero. Otherwise, - it returns either const_true_rtx or const0_rtx. */ +/* Like simplify_binary_operation except used for relational operators. + MODE is the mode of the result. If MODE is VOIDmode, both operands must + not also be VOIDmode. + CMP_MODE specifies in which mode the comparison is done in, so it is + the mode of the operands. If CMP_MODE is VOIDmode, it is taken from + the operands or, if both are VOIDmode, the operands are compared in + "infinite precision". */ rtx simplify_relational_operation (enum rtx_code code, enum machine_mode mode, - rtx op0, rtx op1) + enum machine_mode cmp_mode, rtx op0, rtx op1) +{ + rtx tem, trueop0, trueop1; + + if (cmp_mode == VOIDmode) + cmp_mode = GET_MODE (op0); + if (cmp_mode == VOIDmode) + cmp_mode = GET_MODE (op1); + + tem = simplify_const_relational_operation (code, cmp_mode, op0, op1); + if (tem) + { + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + if (tem == const0_rtx) + return CONST0_RTX (mode); +#ifdef FLOAT_STORE_FLAG_VALUE + { + REAL_VALUE_TYPE val; + val = FLOAT_STORE_FLAG_VALUE (mode); + return CONST_DOUBLE_FROM_REAL_VALUE (val, mode); + } +#else + return NULL_RTX; +#endif + } + if (VECTOR_MODE_P (mode)) + { + if (tem == const0_rtx) + return CONST0_RTX (mode); +#ifdef VECTOR_STORE_FLAG_VALUE + { + int i, units; + rtvec v; + + rtx val = VECTOR_STORE_FLAG_VALUE (mode); + if (val == NULL_RTX) + return NULL_RTX; + if (val == const1_rtx) + return CONST1_RTX (mode); + + units = GET_MODE_NUNITS (mode); + v = rtvec_alloc (units); + for (i = 0; i < units; i++) + RTVEC_ELT (v, i) = val; + return gen_rtx_raw_CONST_VECTOR (mode, v); + } +#else + return NULL_RTX; +#endif + } + + return tem; + } + + /* For the following tests, ensure const0_rtx is op1. */ + if (swap_commutative_operands_p (op0, op1) + || (op0 == const0_rtx && op1 != const0_rtx)) + tem = op0, op0 = op1, op1 = tem, code = swap_condition (code); + + /* If op0 is a compare, extract the comparison arguments from it. */ + if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) + return simplify_relational_operation (code, mode, VOIDmode, + XEXP (op0, 0), XEXP (op0, 1)); + + if (mode == VOIDmode + || GET_MODE_CLASS (cmp_mode) == MODE_CC + || CC0_P (op0)) + return NULL_RTX; + + trueop0 = avoid_constant_pool_reference (op0); + trueop1 = avoid_constant_pool_reference (op1); + return simplify_relational_operation_1 (code, mode, cmp_mode, + trueop0, trueop1); +} + +/* This part of simplify_relational_operation is only used when CMP_MODE + is not in class MODE_CC (i.e. it is a real comparison). + + MODE is the mode of the result, while CMP_MODE specifies in which + mode the comparison is done in, so it is the mode of the operands. */ + +static rtx +simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode, + enum machine_mode cmp_mode, rtx op0, rtx op1) +{ + enum rtx_code op0code = GET_CODE (op0); + + if (GET_CODE (op1) == CONST_INT) + { + if (INTVAL (op1) == 0 && COMPARISON_P (op0)) + { + /* If op0 is a comparison, extract the comparison arguments form it. */ + if (code == NE) + { + if (GET_MODE (op0) == mode) + return simplify_rtx (op0); + else + return simplify_gen_relational (GET_CODE (op0), mode, VOIDmode, + XEXP (op0, 0), XEXP (op0, 1)); + } + else if (code == EQ) + { + enum rtx_code new_code = reversed_comparison_code (op0, NULL_RTX); + if (new_code != UNKNOWN) + return simplify_gen_relational (new_code, mode, VOIDmode, + XEXP (op0, 0), XEXP (op0, 1)); + } + } + } + + /* (eq/ne (plus x cst1) cst2) simplifies to (eq/ne x (cst2 - cst1)) */ + if ((code == EQ || code == NE) + && (op0code == PLUS || op0code == MINUS) + && CONSTANT_P (op1) + && CONSTANT_P (XEXP (op0, 1)) + && (INTEGRAL_MODE_P (cmp_mode) || flag_unsafe_math_optimizations)) + { + rtx x = XEXP (op0, 0); + rtx c = XEXP (op0, 1); + + c = simplify_gen_binary (op0code == PLUS ? MINUS : PLUS, + cmp_mode, op1, c); + return simplify_gen_relational (code, mode, cmp_mode, x, c); + } + + /* (ne:SI (zero_extract:SI FOO (const_int 1) BAR) (const_int 0))) is + the same as (zero_extract:SI FOO (const_int 1) BAR). */ + if (code == NE + && op1 == const0_rtx + && GET_MODE_CLASS (mode) == MODE_INT + && cmp_mode != VOIDmode + /* ??? Work-around BImode bugs in the ia64 backend. */ + && mode != BImode + && cmp_mode != BImode + && nonzero_bits (op0, cmp_mode) == 1 + && STORE_FLAG_VALUE == 1) + return GET_MODE_SIZE (mode) > GET_MODE_SIZE (cmp_mode) + ? simplify_gen_unary (ZERO_EXTEND, mode, op0, cmp_mode) + : lowpart_subreg (mode, op0, cmp_mode); + + return NULL_RTX; +} + +/* Check if the given comparison (done in the given MODE) is actually a + tautology or a contradiction. + If no simplification is possible, this function returns zero. + Otherwise, it returns either const_true_rtx or const0_rtx. */ + +rtx +simplify_const_relational_operation (enum rtx_code code, + enum machine_mode mode, + rtx op0, rtx op1) { int equal, op0lt, op0ltu, op1lt, op1ltu; rtx tem; rtx trueop0; rtx trueop1; - if (mode == VOIDmode - && (GET_MODE (op0) != VOIDmode - || GET_MODE (op1) != VOIDmode)) - abort (); + gcc_assert (mode != VOIDmode + || (GET_MODE (op0) == VOIDmode + && GET_MODE (op1) == VOIDmode)); /* If op0 is a compare, extract the comparison arguments from it. */ if (GET_CODE (op0) == COMPARE && op1 == const0_rtx) op1 = XEXP (op0, 1), op0 = XEXP (op0, 0); - trueop0 = avoid_constant_pool_reference (op0); - trueop1 = avoid_constant_pool_reference (op1); - /* We can't simplify MODE_CC values since we don't know what the actual comparison is. */ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC || CC0_P (op0)) return 0; /* Make sure the constant is second. */ - if (swap_commutative_operands_p (trueop0, trueop1)) + if (swap_commutative_operands_p (op0, op1)) { tem = op0, op0 = op1, op1 = tem; - tem = trueop0, trueop0 = trueop1, trueop1 = tem; code = swap_condition (code); } + trueop0 = avoid_constant_pool_reference (op0); + trueop1 = avoid_constant_pool_reference (op1); + /* For integer comparisons of A and B maybe we can simplify A - B and can then simplify a comparison of that with zero. If A and B are both either a register or a CONST_INT, this can't help; testing for these cases will @@ -2537,15 +2977,18 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode, If CODE is an unsigned comparison, then we can never do this optimization, because it gives an incorrect result if the subtraction wraps around zero. ANSI C defines unsigned operations such that they never overflow, and - thus such cases can not be ignored. */ + thus such cases can not be ignored; but we cannot do it even for + signed comparisons for languages such as Java, so test flag_wrapv. */ - if (INTEGRAL_MODE_P (mode) && trueop1 != const0_rtx - && ! ((GET_CODE (op0) == REG || GET_CODE (trueop0) == CONST_INT) - && (GET_CODE (op1) == REG || GET_CODE (trueop1) == CONST_INT)) + if (!flag_wrapv && INTEGRAL_MODE_P (mode) && trueop1 != const0_rtx + && ! ((REG_P (op0) || GET_CODE (trueop0) == CONST_INT) + && (REG_P (op1) || GET_CODE (trueop1) == CONST_INT)) && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1)) + /* We cannot do this for == or != if tem is a nonzero address. */ + && ((code != EQ && code != NE) || ! nonzero_address_p (tem)) && code != GTU && code != GEU && code != LTU && code != LEU) - return simplify_relational_operation (signed_condition (code), - mode, tem, const0_rtx); + return simplify_const_relational_operation (signed_condition (code), + mode, tem, const0_rtx); if (flag_unsafe_math_optimizations && code == ORDERED) return const_true_rtx; @@ -2660,6 +3103,63 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode, /* Otherwise, there are some code-specific tests we can make. */ else { + /* Optimize comparisons with upper and lower bounds. */ + if (SCALAR_INT_MODE_P (mode) + && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + { + rtx mmin, mmax; + int sign; + + if (code == GEU + || code == LEU + || code == GTU + || code == LTU) + sign = 0; + else + sign = 1; + + get_mode_bounds (mode, sign, mode, &mmin, &mmax); + + tem = NULL_RTX; + switch (code) + { + case GEU: + case GE: + /* x >= min is always true. */ + if (rtx_equal_p (trueop1, mmin)) + tem = const_true_rtx; + else + break; + + case LEU: + case LE: + /* x <= max is always true. */ + if (rtx_equal_p (trueop1, mmax)) + tem = const_true_rtx; + break; + + case GTU: + case GT: + /* x > max is always false. */ + if (rtx_equal_p (trueop1, mmax)) + tem = const0_rtx; + break; + + case LTU: + case LT: + /* x < min is always false. */ + if (rtx_equal_p (trueop1, mmin)) + tem = const0_rtx; + break; + + default: + break; + } + if (tem == const0_rtx + || tem == const_true_rtx) + return tem; + } + switch (code) { case EQ: @@ -2672,33 +3172,6 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode, return const_true_rtx; break; - case GEU: - /* Unsigned values are never negative. */ - if (trueop1 == const0_rtx) - return const_true_rtx; - break; - - case LTU: - if (trueop1 == const0_rtx) - return const0_rtx; - break; - - case LEU: - /* Unsigned values are never greater than the largest - unsigned value. */ - if (GET_CODE (trueop1) == CONST_INT - && (unsigned HOST_WIDE_INT) INTVAL (trueop1) == GET_MODE_MASK (mode) - && INTEGRAL_MODE_P (mode)) - return const_true_rtx; - break; - - case GTU: - if (GET_CODE (trueop1) == CONST_INT - && (unsigned HOST_WIDE_INT) INTVAL (trueop1) == GET_MODE_MASK (mode) - && INTEGRAL_MODE_P (mode)) - return const0_rtx; - break; - case LT: /* Optimize abs(x) < 0.0. */ if (trueop1 == CONST0_RTX (mode) && !HONOR_SNANS (mode)) @@ -2774,7 +3247,7 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode, case UNORDERED: return const0_rtx; default: - abort (); + gcc_unreachable (); } } @@ -2831,7 +3304,7 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode, != ((HOST_WIDE_INT) (-1) << (width - 1)))) val &= ((HOST_WIDE_INT) 1 << width) - 1; - return GEN_INT (val); + return gen_int_mode (val, mode); } break; @@ -2865,24 +3338,12 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode, && rtx_equal_p (XEXP (op0, 1), op1)))) return op2; - if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && ! side_effects_p (op0)) + if (COMPARISON_P (op0) && ! side_effects_p (op0)) { enum machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode ? GET_MODE (XEXP (op0, 1)) : GET_MODE (XEXP (op0, 0))); rtx temp; - if (cmp_mode == VOIDmode) - cmp_mode = op0_mode; - temp = simplify_relational_operation (GET_CODE (op0), cmp_mode, - XEXP (op0, 0), XEXP (op0, 1)); - - /* See if any simplifications were possible. */ - if (temp == const0_rtx) - return op2; - else if (temp == const_true_rtx) - return op1; - else if (temp) - abort (); /* Look for happy constants in op1 and op2. */ if (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT) @@ -2903,16 +3364,31 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode, else break; - return gen_rtx_fmt_ee (code, mode, XEXP (op0, 0), XEXP (op0, 1)); + return simplify_gen_relational (code, mode, cmp_mode, + XEXP (op0, 0), XEXP (op0, 1)); + } + + if (cmp_mode == VOIDmode) + cmp_mode = op0_mode; + temp = simplify_relational_operation (GET_CODE (op0), op0_mode, + cmp_mode, XEXP (op0, 0), + XEXP (op0, 1)); + + /* See if any simplifications were possible. */ + if (temp) + { + if (GET_CODE (temp) == CONST_INT) + return temp == const0_rtx ? op2 : op1; + else if (temp) + return gen_rtx_IF_THEN_ELSE (mode, temp, op1, op2); } } break; case VEC_MERGE: - if (GET_MODE (op0) != mode - || GET_MODE (op1) != mode - || !VECTOR_MODE_P (mode)) - abort (); + gcc_assert (GET_MODE (op0) == mode); + gcc_assert (GET_MODE (op1) == mode); + gcc_assert (VECTOR_MODE_P (mode)); op2 = avoid_constant_pool_reference (op2); if (GET_CODE (op2) == CONST_INT) { @@ -2943,244 +3419,302 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode, break; default: - abort (); + gcc_unreachable (); } return 0; } -/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE) - Return 0 if no simplifications is possible. */ -rtx -simplify_subreg (enum machine_mode outermode, rtx op, - enum machine_mode innermode, unsigned int byte) -{ - /* Little bit of sanity checking. */ - if (innermode == VOIDmode || outermode == VOIDmode - || innermode == BLKmode || outermode == BLKmode) - abort (); - - if (GET_MODE (op) != innermode - && GET_MODE (op) != VOIDmode) - abort (); +/* Evaluate a SUBREG of a CONST_INT or CONST_DOUBLE or CONST_VECTOR, + returning another CONST_INT or CONST_DOUBLE or CONST_VECTOR. - if (byte % GET_MODE_SIZE (outermode) - || byte >= GET_MODE_SIZE (innermode)) - abort (); + Works by unpacking OP into a collection of 8-bit values + represented as a little-endian array of 'unsigned char', selecting by BYTE, + and then repacking them again for OUTERMODE. */ - if (outermode == innermode && !byte) +static rtx +simplify_immed_subreg (enum machine_mode outermode, rtx op, + enum machine_mode innermode, unsigned int byte) +{ + /* We support up to 512-bit values (for V8DFmode). */ + enum { + max_bitsize = 512, + value_bit = 8, + value_mask = (1 << value_bit) - 1 + }; + unsigned char value[max_bitsize / value_bit]; + int value_start; + int i; + int elem; + + int num_elem; + rtx * elems; + int elem_bitsize; + rtx result_s; + rtvec result_v = NULL; + enum mode_class outer_class; + enum machine_mode outer_submode; + + /* Some ports misuse CCmode. */ + if (GET_MODE_CLASS (outermode) == MODE_CC && GET_CODE (op) == CONST_INT) return op; - /* Simplify subregs of vector constants. */ - if (GET_CODE (op) == CONST_VECTOR) - { - int elt_size = GET_MODE_SIZE (GET_MODE_INNER (innermode)); - const unsigned int offset = byte / elt_size; - rtx elt; - - if (GET_MODE_INNER (innermode) == outermode) - { - elt = CONST_VECTOR_ELT (op, offset); + /* We have no way to represent a complex constant at the rtl level. */ + if (COMPLEX_MODE_P (outermode)) + return NULL_RTX; - /* ?? We probably don't need this copy_rtx because constants - can be shared. ?? */ + /* Unpack the value. */ - return copy_rtx (elt); - } - else if (GET_MODE_INNER (innermode) == GET_MODE_INNER (outermode) - && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode)) - { - return (gen_rtx_CONST_VECTOR - (outermode, - gen_rtvec_v (GET_MODE_NUNITS (outermode), - &CONST_VECTOR_ELT (op, offset)))); - } - else if (GET_MODE_CLASS (outermode) == MODE_INT - && (GET_MODE_SIZE (outermode) % elt_size == 0)) + if (GET_CODE (op) == CONST_VECTOR) + { + num_elem = CONST_VECTOR_NUNITS (op); + elems = &CONST_VECTOR_ELT (op, 0); + elem_bitsize = GET_MODE_BITSIZE (GET_MODE_INNER (innermode)); + } + else + { + num_elem = 1; + elems = &op; + elem_bitsize = max_bitsize; + } + /* If this asserts, it is too complicated; reducing value_bit may help. */ + gcc_assert (BITS_PER_UNIT % value_bit == 0); + /* I don't know how to handle endianness of sub-units. */ + gcc_assert (elem_bitsize % BITS_PER_UNIT == 0); + + for (elem = 0; elem < num_elem; elem++) + { + unsigned char * vp; + rtx el = elems[elem]; + + /* Vectors are kept in target memory order. (This is probably + a mistake.) */ + { + unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT; + unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize) + / BITS_PER_UNIT); + unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte; + unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte; + unsigned bytele = (subword_byte % UNITS_PER_WORD + + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD); + vp = value + (bytele * BITS_PER_UNIT) / value_bit; + } + + switch (GET_CODE (el)) { - /* This happens when the target register size is smaller then - the vector mode, and we synthesize operations with vectors - of elements that are smaller than the register size. */ - HOST_WIDE_INT sum = 0, high = 0; - unsigned n_elts = (GET_MODE_SIZE (outermode) / elt_size); - unsigned i = BYTES_BIG_ENDIAN ? offset : offset + n_elts - 1; - unsigned step = BYTES_BIG_ENDIAN ? 1 : -1; - int shift = BITS_PER_UNIT * elt_size; - unsigned HOST_WIDE_INT unit_mask; - - unit_mask = (unsigned HOST_WIDE_INT) -1 - >> (sizeof (HOST_WIDE_INT) * BITS_PER_UNIT - shift); - - for (; n_elts--; i += step) + case CONST_INT: + for (i = 0; + i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize; + i += value_bit) + *vp++ = INTVAL (el) >> i; + /* CONST_INTs are always logically sign-extended. */ + for (; i < elem_bitsize; i += value_bit) + *vp++ = INTVAL (el) < 0 ? -1 : 0; + break; + + case CONST_DOUBLE: + if (GET_MODE (el) == VOIDmode) { - elt = CONST_VECTOR_ELT (op, i); - if (GET_CODE (elt) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (elt)) == MODE_FLOAT) + /* If this triggers, someone should have generated a + CONST_INT instead. */ + gcc_assert (elem_bitsize > HOST_BITS_PER_WIDE_INT); + + for (i = 0; i < HOST_BITS_PER_WIDE_INT; i += value_bit) + *vp++ = CONST_DOUBLE_LOW (el) >> i; + while (i < HOST_BITS_PER_WIDE_INT * 2 && i < elem_bitsize) { - elt = gen_lowpart_common (int_mode_for_mode (GET_MODE (elt)), - elt); - if (! elt) - return NULL_RTX; + *vp++ + = CONST_DOUBLE_HIGH (el) >> (i - HOST_BITS_PER_WIDE_INT); + i += value_bit; } - if (GET_CODE (elt) != CONST_INT) - return NULL_RTX; - /* Avoid overflow. */ - if (high >> (HOST_BITS_PER_WIDE_INT - shift)) - return NULL_RTX; - high = high << shift | sum >> (HOST_BITS_PER_WIDE_INT - shift); - sum = (sum << shift) + (INTVAL (elt) & unit_mask); + /* It shouldn't matter what's done here, so fill it with + zero. */ + for (; i < max_bitsize; i += value_bit) + *vp++ = 0; } - if (GET_MODE_BITSIZE (outermode) <= HOST_BITS_PER_WIDE_INT) - return GEN_INT (trunc_int_for_mode (sum, outermode)); - else if (GET_MODE_BITSIZE (outermode) == 2* HOST_BITS_PER_WIDE_INT) - return immed_double_const (sum, high, outermode); else - return NULL_RTX; - } - else if (GET_MODE_CLASS (outermode) == MODE_INT - && (elt_size % GET_MODE_SIZE (outermode) == 0)) - { - enum machine_mode new_mode - = int_mode_for_mode (GET_MODE_INNER (innermode)); - int subbyte = byte % elt_size; + { + long tmp[max_bitsize / 32]; + int bitsize = GET_MODE_BITSIZE (GET_MODE (el)); - op = simplify_subreg (new_mode, op, innermode, byte - subbyte); - if (! op) - return NULL_RTX; - return simplify_subreg (outermode, op, new_mode, subbyte); + gcc_assert (GET_MODE_CLASS (GET_MODE (el)) == MODE_FLOAT); + gcc_assert (bitsize <= elem_bitsize); + gcc_assert (bitsize % value_bit == 0); + + real_to_target (tmp, CONST_DOUBLE_REAL_VALUE (el), + GET_MODE (el)); + + /* real_to_target produces its result in words affected by + FLOAT_WORDS_BIG_ENDIAN. However, we ignore this, + and use WORDS_BIG_ENDIAN instead; see the documentation + of SUBREG in rtl.texi. */ + for (i = 0; i < bitsize; i += value_bit) + { + int ibase; + if (WORDS_BIG_ENDIAN) + ibase = bitsize - 1 - i; + else + ibase = i; + *vp++ = tmp[ibase / 32] >> i % 32; + } + + /* It shouldn't matter what's done here, so fill it with + zero. */ + for (; i < elem_bitsize; i += value_bit) + *vp++ = 0; + } + break; + + default: + gcc_unreachable (); } - else if (GET_MODE_CLASS (outermode) == MODE_INT) - /* This shouldn't happen, but let's not do anything stupid. */ - return NULL_RTX; } - /* Attempt to simplify constant to non-SUBREG expression. */ - if (CONSTANT_P (op)) + /* Now, pick the right byte to start with. */ + /* Renumber BYTE so that the least-significant byte is byte 0. A special + case is paradoxical SUBREGs, which shouldn't be adjusted since they + will already have offset 0. */ + if (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode)) { - int offset, part; - unsigned HOST_WIDE_INT val = 0; + unsigned ibyte = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode) + - byte); + unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte; + unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte; + byte = (subword_byte % UNITS_PER_WORD + + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD); + } - if (VECTOR_MODE_P (outermode)) - { - /* Construct a CONST_VECTOR from individual subregs. */ - enum machine_mode submode = GET_MODE_INNER (outermode); - int subsize = GET_MODE_UNIT_SIZE (outermode); - int i, elts = GET_MODE_NUNITS (outermode); - rtvec v = rtvec_alloc (elts); - rtx elt; - - for (i = 0; i < elts; i++, byte += subsize) - { - /* This might fail, e.g. if taking a subreg from a SYMBOL_REF. */ - /* ??? It would be nice if we could actually make such subregs - on targets that allow such relocations. */ - if (byte >= GET_MODE_SIZE (innermode)) - elt = CONST0_RTX (submode); - else - elt = simplify_subreg (submode, op, innermode, byte); - if (! elt) - return NULL_RTX; - RTVEC_ELT (v, i) = elt; - } - return gen_rtx_CONST_VECTOR (outermode, v); - } + /* BYTE should still be inside OP. (Note that BYTE is unsigned, + so if it's become negative it will instead be very large.) */ + gcc_assert (byte < GET_MODE_SIZE (innermode)); - /* ??? This code is partly redundant with code below, but can handle - the subregs of floats and similar corner cases. - Later it we should move all simplification code here and rewrite - GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends - using SIMPLIFY_SUBREG. */ - if (subreg_lowpart_offset (outermode, innermode) == byte - && GET_CODE (op) != CONST_VECTOR) - { - rtx new = gen_lowpart_if_possible (outermode, op); - if (new) - return new; - } + /* Convert from bytes to chunks of size value_bit. */ + value_start = byte * (BITS_PER_UNIT / value_bit); - /* Similar comment as above apply here. */ - if (GET_MODE_SIZE (outermode) == UNITS_PER_WORD - && GET_MODE_SIZE (innermode) > UNITS_PER_WORD - && GET_MODE_CLASS (outermode) == MODE_INT) - { - rtx new = constant_subword (op, - (byte / UNITS_PER_WORD), - innermode); - if (new) - return new; - } + /* Re-pack the value. */ + + if (VECTOR_MODE_P (outermode)) + { + num_elem = GET_MODE_NUNITS (outermode); + result_v = rtvec_alloc (num_elem); + elems = &RTVEC_ELT (result_v, 0); + outer_submode = GET_MODE_INNER (outermode); + } + else + { + num_elem = 1; + elems = &result_s; + outer_submode = outermode; + } - if (GET_MODE_CLASS (outermode) != MODE_INT - && GET_MODE_CLASS (outermode) != MODE_CC) - { - enum machine_mode new_mode = int_mode_for_mode (outermode); + outer_class = GET_MODE_CLASS (outer_submode); + elem_bitsize = GET_MODE_BITSIZE (outer_submode); - if (new_mode != innermode || byte != 0) - { - op = simplify_subreg (new_mode, op, innermode, byte); - if (! op) - return NULL_RTX; - return simplify_subreg (outermode, op, new_mode, 0); - } - } + gcc_assert (elem_bitsize % value_bit == 0); + gcc_assert (elem_bitsize + value_start * value_bit <= max_bitsize); - offset = byte * BITS_PER_UNIT; - switch (GET_CODE (op)) + for (elem = 0; elem < num_elem; elem++) + { + unsigned char *vp; + + /* Vectors are stored in target memory order. (This is probably + a mistake.) */ + { + unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT; + unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize) + / BITS_PER_UNIT); + unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte; + unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte; + unsigned bytele = (subword_byte % UNITS_PER_WORD + + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD); + vp = value + value_start + (bytele * BITS_PER_UNIT) / value_bit; + } + + switch (outer_class) { - case CONST_DOUBLE: - if (GET_MODE (op) != VOIDmode) - break; - - /* We can't handle this case yet. */ - if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT) - return NULL_RTX; + case MODE_INT: + case MODE_PARTIAL_INT: + { + unsigned HOST_WIDE_INT hi = 0, lo = 0; + + for (i = 0; + i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize; + i += value_bit) + lo |= (HOST_WIDE_INT)(*vp++ & value_mask) << i; + for (; i < elem_bitsize; i += value_bit) + hi |= ((HOST_WIDE_INT)(*vp++ & value_mask) + << (i - HOST_BITS_PER_WIDE_INT)); + + /* immed_double_const doesn't call trunc_int_for_mode. I don't + know why. */ + if (elem_bitsize <= HOST_BITS_PER_WIDE_INT) + elems[elem] = gen_int_mode (lo, outer_submode); + else + elems[elem] = immed_double_const (lo, hi, outer_submode); + } + break; + + case MODE_FLOAT: + { + REAL_VALUE_TYPE r; + long tmp[max_bitsize / 32]; + + /* real_from_target wants its input in words affected by + FLOAT_WORDS_BIG_ENDIAN. However, we ignore this, + and use WORDS_BIG_ENDIAN instead; see the documentation + of SUBREG in rtl.texi. */ + for (i = 0; i < max_bitsize / 32; i++) + tmp[i] = 0; + for (i = 0; i < elem_bitsize; i += value_bit) + { + int ibase; + if (WORDS_BIG_ENDIAN) + ibase = elem_bitsize - 1 - i; + else + ibase = i; + tmp[ibase / 32] |= (*vp++ & value_mask) << i % 32; + } - part = offset >= HOST_BITS_PER_WIDE_INT; - if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT - && BYTES_BIG_ENDIAN) - || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT - && WORDS_BIG_ENDIAN)) - part = !part; - val = part ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op); - offset %= HOST_BITS_PER_WIDE_INT; + real_from_target (&r, tmp, outer_submode); + elems[elem] = CONST_DOUBLE_FROM_REAL_VALUE (r, outer_submode); + } + break; + + default: + gcc_unreachable (); + } + } + if (VECTOR_MODE_P (outermode)) + return gen_rtx_CONST_VECTOR (outermode, result_v); + else + return result_s; +} - /* We've already picked the word we want from a double, so - pretend this is actually an integer. */ - innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); +/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE) + Return 0 if no simplifications are possible. */ +rtx +simplify_subreg (enum machine_mode outermode, rtx op, + enum machine_mode innermode, unsigned int byte) +{ + /* Little bit of sanity checking. */ + gcc_assert (innermode != VOIDmode); + gcc_assert (outermode != VOIDmode); + gcc_assert (innermode != BLKmode); + gcc_assert (outermode != BLKmode); - /* Fall through. */ - case CONST_INT: - if (GET_CODE (op) == CONST_INT) - val = INTVAL (op); + gcc_assert (GET_MODE (op) == innermode + || GET_MODE (op) == VOIDmode); - /* We don't handle synthesizing of non-integral constants yet. */ - if (GET_MODE_CLASS (outermode) != MODE_INT) - return NULL_RTX; + gcc_assert ((byte % GET_MODE_SIZE (outermode)) == 0); + gcc_assert (byte < GET_MODE_SIZE (innermode)); - if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN) - { - if (WORDS_BIG_ENDIAN) - offset = (GET_MODE_BITSIZE (innermode) - - GET_MODE_BITSIZE (outermode) - offset); - if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN - && GET_MODE_SIZE (outermode) < UNITS_PER_WORD) - offset = (offset + BITS_PER_WORD - GET_MODE_BITSIZE (outermode) - - 2 * (offset % BITS_PER_WORD)); - } + if (outermode == innermode && !byte) + return op; - if (offset >= HOST_BITS_PER_WIDE_INT) - return ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx; - else - { - val >>= offset; - if (GET_MODE_BITSIZE (outermode) < HOST_BITS_PER_WIDE_INT) - val = trunc_int_for_mode (val, outermode); - return GEN_INT (val); - } - default: - break; - } - } + if (GET_CODE (op) == CONST_INT + || GET_CODE (op) == CONST_DOUBLE + || GET_CODE (op) == CONST_VECTOR) + return simplify_immed_subreg (outermode, op, innermode, byte); /* Changing mode twice with SUBREG => just change it once, or not at all if changing back op starting mode. */ @@ -3188,7 +3722,7 @@ simplify_subreg (enum machine_mode outermode, rtx op, { enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op)); int final_offset = byte + SUBREG_BYTE (op); - rtx new; + rtx newx; if (outermode == innermostmode && byte == 0 && SUBREG_BYTE (op) == 0) @@ -3245,12 +3779,14 @@ simplify_subreg (enum machine_mode outermode, rtx op, } /* Recurse for further possible simplifications. */ - new = simplify_subreg (outermode, SUBREG_REG (op), - GET_MODE (SUBREG_REG (op)), - final_offset); - if (new) - return new; - return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset); + newx = simplify_subreg (outermode, SUBREG_REG (op), innermostmode, + final_offset); + if (newx) + return newx; + if (validate_subreg (outermode, innermostmode, + SUBREG_REG (op), final_offset)) + return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset); + return NULL_RTX; } /* SUBREG of a hard register => just change the register number @@ -3259,8 +3795,6 @@ simplify_subreg (enum machine_mode outermode, rtx op, frame, or argument pointer, leave this as a SUBREG. */ if (REG_P (op) - && (! REG_FUNCTION_VALUE_P (op) - || ! rtx_equal_function_value_matters) && REGNO (op) < FIRST_PSEUDO_REGISTER #ifdef CANNOT_CHANGE_MODE_CLASS && ! (REG_CANNOT_CHANGE_MODE_P (REGNO (op), innermode, outermode) @@ -3280,14 +3814,15 @@ simplify_subreg (enum machine_mode outermode, rtx op, && subreg_offset_representable_p (REGNO (op), innermode, byte, outermode)) { - rtx tem = gen_rtx_SUBREG (outermode, op, byte); - int final_regno = subreg_hard_regno (tem, 0); + unsigned int regno = REGNO (op); + unsigned int final_regno + = regno + subreg_regno_offset (regno, innermode, byte, outermode); /* ??? We do allow it if the current REG is not valid for its mode. This is a kludge to work around how float/complex arguments are passed on 32-bit SPARC and should be fixed. */ if (HARD_REGNO_MODE_OK (final_regno, outermode) - || ! HARD_REGNO_MODE_OK (REGNO (op), innermode)) + || ! HARD_REGNO_MODE_OK (regno, innermode)) { rtx x = gen_rtx_REG_offset (op, outermode, final_regno, byte); @@ -3307,7 +3842,7 @@ simplify_subreg (enum machine_mode outermode, rtx op, SUBREG with it. Don't do this if the MEM has a mode-dependent address or if we would be widening it. */ - if (GET_CODE (op) == MEM + if (MEM_P (op) && ! mode_dependent_address_p (XEXP (op, 0)) /* Allow splitting of volatile memory references in case we don't have instruction to move the whole thing. */ @@ -3320,53 +3855,133 @@ simplify_subreg (enum machine_mode outermode, rtx op, of real and imaginary part. */ if (GET_CODE (op) == CONCAT) { - int is_realpart = byte < (unsigned int) GET_MODE_UNIT_SIZE (innermode); - rtx part = is_realpart ? XEXP (op, 0) : XEXP (op, 1); - unsigned int final_offset; - rtx res; + unsigned int inner_size, final_offset; + rtx part, res; + + inner_size = GET_MODE_UNIT_SIZE (innermode); + part = byte < inner_size ? XEXP (op, 0) : XEXP (op, 1); + final_offset = byte % inner_size; + if (final_offset + GET_MODE_SIZE (outermode) > inner_size) + return NULL_RTX; - final_offset = byte % (GET_MODE_UNIT_SIZE (innermode)); res = simplify_subreg (outermode, part, GET_MODE (part), final_offset); if (res) return res; - /* We can at least simplify it by referring directly to the relevant part. */ - return gen_rtx_SUBREG (outermode, part, final_offset); + if (validate_subreg (outermode, GET_MODE (part), part, final_offset)) + return gen_rtx_SUBREG (outermode, part, final_offset); + return NULL_RTX; } + /* Optimize SUBREG truncations of zero and sign extended values. */ + if ((GET_CODE (op) == ZERO_EXTEND + || GET_CODE (op) == SIGN_EXTEND) + && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode)) + { + unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte); + + /* If we're requesting the lowpart of a zero or sign extension, + there are three possibilities. If the outermode is the same + as the origmode, we can omit both the extension and the subreg. + If the outermode is not larger than the origmode, we can apply + the truncation without the extension. Finally, if the outermode + is larger than the origmode, but both are integer modes, we + can just extend to the appropriate mode. */ + if (bitpos == 0) + { + enum machine_mode origmode = GET_MODE (XEXP (op, 0)); + if (outermode == origmode) + return XEXP (op, 0); + if (GET_MODE_BITSIZE (outermode) <= GET_MODE_BITSIZE (origmode)) + return simplify_gen_subreg (outermode, XEXP (op, 0), origmode, + subreg_lowpart_offset (outermode, + origmode)); + if (SCALAR_INT_MODE_P (outermode)) + return simplify_gen_unary (GET_CODE (op), outermode, + XEXP (op, 0), origmode); + } + + /* A SUBREG resulting from a zero extension may fold to zero if + it extracts higher bits that the ZERO_EXTEND's source bits. */ + if (GET_CODE (op) == ZERO_EXTEND + && bitpos >= GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0)))) + return CONST0_RTX (outermode); + } + + /* Simplify (subreg:QI (lshiftrt:SI (sign_extend:SI (x:QI)) C), 0) into + to (ashiftrt:QI (x:QI) C), where C is a suitable small constant and + the outer subreg is effectively a truncation to the original mode. */ + if ((GET_CODE (op) == LSHIFTRT + || GET_CODE (op) == ASHIFTRT) + && SCALAR_INT_MODE_P (outermode) + /* Ensure that OUTERMODE is at least twice as wide as the INNERMODE + to avoid the possibility that an outer LSHIFTRT shifts by more + than the sign extension's sign_bit_copies and introduces zeros + into the high bits of the result. */ + && (2 * GET_MODE_BITSIZE (outermode)) <= GET_MODE_BITSIZE (innermode) + && GET_CODE (XEXP (op, 1)) == CONST_INT + && GET_CODE (XEXP (op, 0)) == SIGN_EXTEND + && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode + && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode) + && subreg_lsb_1 (outermode, innermode, byte) == 0) + return simplify_gen_binary (ASHIFTRT, outermode, + XEXP (XEXP (op, 0), 0), XEXP (op, 1)); + + /* Likewise (subreg:QI (lshiftrt:SI (zero_extend:SI (x:QI)) C), 0) into + to (lshiftrt:QI (x:QI) C), where C is a suitable small constant and + the outer subreg is effectively a truncation to the original mode. */ + if ((GET_CODE (op) == LSHIFTRT + || GET_CODE (op) == ASHIFTRT) + && SCALAR_INT_MODE_P (outermode) + && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode) + && GET_CODE (XEXP (op, 1)) == CONST_INT + && GET_CODE (XEXP (op, 0)) == ZERO_EXTEND + && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode + && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode) + && subreg_lsb_1 (outermode, innermode, byte) == 0) + return simplify_gen_binary (LSHIFTRT, outermode, + XEXP (XEXP (op, 0), 0), XEXP (op, 1)); + + /* Likewise (subreg:QI (ashift:SI (zero_extend:SI (x:QI)) C), 0) into + to (ashift:QI (x:QI) C), where C is a suitable small constant and + the outer subreg is effectively a truncation to the original mode. */ + if (GET_CODE (op) == ASHIFT + && SCALAR_INT_MODE_P (outermode) + && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode) + && GET_CODE (XEXP (op, 1)) == CONST_INT + && (GET_CODE (XEXP (op, 0)) == ZERO_EXTEND + || GET_CODE (XEXP (op, 0)) == SIGN_EXTEND) + && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode + && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode) + && subreg_lsb_1 (outermode, innermode, byte) == 0) + return simplify_gen_binary (ASHIFT, outermode, + XEXP (XEXP (op, 0), 0), XEXP (op, 1)); + return NULL_RTX; } + /* Make a SUBREG operation or equivalent if it folds. */ rtx simplify_gen_subreg (enum machine_mode outermode, rtx op, enum machine_mode innermode, unsigned int byte) { - rtx new; - /* Little bit of sanity checking. */ - if (innermode == VOIDmode || outermode == VOIDmode - || innermode == BLKmode || outermode == BLKmode) - abort (); + rtx newx; - if (GET_MODE (op) != innermode - && GET_MODE (op) != VOIDmode) - abort (); + newx = simplify_subreg (outermode, op, innermode, byte); + if (newx) + return newx; - if (byte % GET_MODE_SIZE (outermode) - || byte >= GET_MODE_SIZE (innermode)) - abort (); - - if (GET_CODE (op) == QUEUED) + if (GET_CODE (op) == SUBREG + || GET_CODE (op) == CONCAT + || GET_MODE (op) == VOIDmode) return NULL_RTX; - new = simplify_subreg (outermode, op, innermode, byte); - if (new) - return new; - - if (GET_CODE (op) == SUBREG || GET_MODE (op) == VOIDmode) - return NULL_RTX; + if (validate_subreg (outermode, innermode, op, byte)) + return gen_rtx_SUBREG (outermode, op, byte); - return gen_rtx_SUBREG (outermode, op, byte); + return NULL_RTX; } + /* Simplify X, an rtx expression. Return the simplified expression or NULL if no simplifications @@ -3412,60 +4027,45 @@ simplify_rtx (rtx x) { enum rtx_code code = GET_CODE (x); enum machine_mode mode = GET_MODE (x); - rtx temp; switch (GET_RTX_CLASS (code)) { - case '1': + case RTX_UNARY: return simplify_unary_operation (code, mode, XEXP (x, 0), GET_MODE (XEXP (x, 0))); - case 'c': + case RTX_COMM_ARITH: if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1))) return simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0)); /* Fall through.... */ - case '2': + case RTX_BIN_ARITH: return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1)); - case '3': - case 'b': + case RTX_TERNARY: + case RTX_BITFIELD_OPS: return simplify_ternary_operation (code, mode, GET_MODE (XEXP (x, 0)), XEXP (x, 0), XEXP (x, 1), XEXP (x, 2)); - case '<': - temp = simplify_relational_operation (code, - ((GET_MODE (XEXP (x, 0)) - != VOIDmode) - ? GET_MODE (XEXP (x, 0)) - : GET_MODE (XEXP (x, 1))), - XEXP (x, 0), XEXP (x, 1)); -#ifdef FLOAT_STORE_FLAG_VALUE - if (temp != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT) - { - if (temp == const0_rtx) - temp = CONST0_RTX (mode); - else - temp = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE (mode), - mode); - } -#endif - return temp; - - case 'x': + case RTX_COMPARE: + case RTX_COMM_COMPARE: + return simplify_relational_operation (code, mode, + ((GET_MODE (XEXP (x, 0)) + != VOIDmode) + ? GET_MODE (XEXP (x, 0)) + : GET_MODE (XEXP (x, 1))), + XEXP (x, 0), + XEXP (x, 1)); + + case RTX_EXTRA: if (code == SUBREG) return simplify_gen_subreg (mode, SUBREG_REG (x), GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); - if (code == CONSTANT_P_RTX) - { - if (CONSTANT_P (XEXP (x, 0))) - return const1_rtx; - } break; - case 'o': + case RTX_OBJ: if (code == LO_SUM) { /* Convert (lo_sum (high FOO) FOO) to FOO. */