/* RTL simplification functions for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
-
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
+#include "tree.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
+#include "target.h"
/* Simplification and canonicalization of RTL. */
-/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
- virtual regs here because the simplify_*_operation routines are called
- by integrate.c, which is called before virtual register instantiation.
-
- ?!? NONZERO_BASE_PLUS_P needs to move into
- a header file so that their definitions can be shared with the
- simplification routines in simplify-rtx.c. Until then, do not
- change this macro without also changing the copy in simplify-rtx.c. */
-
-/* Allows reference to the stack pointer.
-
- This used to include FIXED_BASE_PLUS_P, however, we can't assume that
- arg_pointer_rtx by itself is nonzero, because on at least one machine,
- the i960, the arg pointer is zero when it is unused. */
-
-#define NONZERO_BASE_PLUS_P(X) \
- ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
- || (X) == virtual_stack_vars_rtx \
- || (X) == virtual_incoming_args_rtx \
- || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (XEXP (X, 0) == frame_pointer_rtx \
- || XEXP (X, 0) == hard_frame_pointer_rtx \
- || ((X) == arg_pointer_rtx \
- && fixed_regs[ARG_POINTER_REGNUM]) \
- || XEXP (X, 0) == virtual_stack_vars_rtx \
- || XEXP (X, 0) == virtual_incoming_args_rtx)) \
- || (X) == stack_pointer_rtx \
- || (X) == virtual_stack_dynamic_rtx \
- || (X) == virtual_outgoing_args_rtx \
- || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (XEXP (X, 0) == stack_pointer_rtx \
- || XEXP (X, 0) == virtual_stack_dynamic_rtx \
- || XEXP (X, 0) == virtual_outgoing_args_rtx)) \
- || GET_CODE (X) == ADDRESSOF)
-
/* Much code operates on (low, high) pairs; the low value is an
unsigned wide int, the high value a signed wide int. We
occasionally need to sign extend from low to high as if low were a
#define HWI_SIGN_EXTEND(low) \
((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
-static rtx neg_const_int PARAMS ((enum machine_mode, rtx));
-static int simplify_plus_minus_op_data_cmp PARAMS ((const void *,
- const void *));
-static rtx simplify_plus_minus PARAMS ((enum rtx_code,
- enum machine_mode, rtx,
- rtx, int));
+static rtx neg_const_int (enum machine_mode, 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_associative_operation (enum rtx_code, enum machine_mode,
+ rtx, rtx);
\f
/* Negate a CONST_INT rtx, truncating (because a conversion from a
maximally negative number can overflow). */
static rtx
-neg_const_int (mode, i)
- enum machine_mode mode;
- rtx i;
+neg_const_int (enum machine_mode mode, rtx i)
{
return gen_int_mode (- INTVAL (i), mode);
}
seeing if the expression folds. */
rtx
-simplify_gen_binary (code, mode, op0, op1)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op0, op1;
+simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
+ rtx op1)
{
rtx tem;
/* If X is a MEM referencing the constant pool, return the real value.
Otherwise return X. */
rtx
-avoid_constant_pool_reference (x)
- rtx x;
+avoid_constant_pool_reference (rtx x)
{
- rtx c, addr;
+ rtx c, tmp, addr;
enum machine_mode cmode;
- if (GET_CODE (x) != MEM)
- return x;
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ break;
+
+ case FLOAT_EXTEND:
+ /* Handle float extensions of constant pool references. */
+ tmp = XEXP (x, 0);
+ c = avoid_constant_pool_reference (tmp);
+ if (c != tmp && GET_CODE (c) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE d;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, c);
+ return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (x));
+ }
+ return x;
+
+ default:
+ return x;
+ }
+
addr = XEXP (x, 0);
+ /* Call target hook to avoid the effects of -fpic etc.... */
+ addr = (*targetm.delegitimize_address) (addr);
+
+ if (GET_CODE (addr) == LO_SUM)
+ addr = XEXP (addr, 1);
+
if (GET_CODE (addr) != SYMBOL_REF
|| ! CONSTANT_POOL_ADDRESS_P (addr))
return x;
the specified operation. */
rtx
-simplify_gen_unary (code, mode, op, op_mode)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op;
- enum machine_mode op_mode;
+simplify_gen_unary (enum rtx_code code, enum machine_mode mode, rtx op,
+ enum machine_mode op_mode)
{
rtx tem;
/* Likewise for ternary operations. */
rtx
-simplify_gen_ternary (code, mode, op0_mode, op0, op1, op2)
- enum rtx_code code;
- enum machine_mode mode, op0_mode;
- rtx op0, op1, op2;
+simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
+ enum machine_mode op0_mode, rtx op0, rtx op1, rtx op2)
{
rtx tem;
*/
rtx
-simplify_gen_relational (code, mode, cmp_mode, op0, op1)
- enum rtx_code code;
- enum machine_mode mode;
- enum machine_mode cmp_mode;
- rtx op0, op1;
+simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
+ enum machine_mode cmp_mode, rtx op0, rtx op1)
{
rtx tem;
resulting RTX. Return a new RTX which is as simplified as possible. */
rtx
-simplify_replace_rtx (x, old, new)
- rtx x;
- rtx old;
- rtx new;
+simplify_replace_rtx (rtx x, rtx old, rtx new)
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
: GET_MODE (XEXP (x, 1)));
rtx op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
rtx op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
-
- return
- simplify_gen_relational (code, mode,
- (op_mode != VOIDmode
- ? op_mode
- : GET_MODE (op0) != VOIDmode
- ? GET_MODE (op0)
- : GET_MODE (op1)),
- op0, op1);
+ rtx temp = simplify_gen_relational (code, mode,
+ (op_mode != VOIDmode
+ ? op_mode
+ : GET_MODE (op0) != VOIDmode
+ ? GET_MODE (op0)
+ : GET_MODE (op1)),
+ op0, op1);
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ if (temp == const0_rtx)
+ temp = CONST0_RTX (mode);
+ else if (temp == const_true_rtx)
+ temp = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE (mode),
+ mode);
+ }
+#endif
+ return temp;
}
case '3':
rtx exp;
exp = simplify_gen_subreg (GET_MODE (x),
simplify_replace_rtx (SUBREG_REG (x),
- old, new),
+ old, new),
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
if (exp)
- x = exp;
+ x = exp;
}
return x;
return replace_equiv_address_nv (x,
simplify_replace_rtx (XEXP (x, 0),
old, new));
+ else if (code == LO_SUM)
+ {
+ rtx op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
+ rtx op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
- if (REG_P (x) && REG_P (old) && REGNO (x) == REGNO (old))
- return new;
+ /* (lo_sum (high x) x) -> x */
+ if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1))
+ return op1;
+
+ return gen_rtx_LO_SUM (mode, op0, op1);
+ }
+ else if (code == REG)
+ {
+ if (REG_P (old) && REGNO (x) == REGNO (old))
+ return new;
+ }
return x;
MODE with input operand OP whose mode was originally OP_MODE.
Return zero if no simplification can be made. */
rtx
-simplify_unary_operation (code, mode, op, op_mode)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op;
- enum machine_mode op_mode;
+simplify_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)
+ {
+ 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)
+ for (i = 0; i < n_elts; i++)
+ RTVEC_ELT (v, i) = trueop;
+ else
+ {
+ enum machine_mode inmode = GET_MODE (trueop);
+ 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 ();
+ for (i = 0; i < n_elts; i++)
+ RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop, i % in_n_elts);
+ }
+ return gen_rtx_CONST_VECTOR (mode, v);
+ }
+ }
+
+ if (VECTOR_MODE_P (mode) && GET_CODE (trueop) == 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);
+ 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 ();
+
+ for (i = 0; i < n_elts; i++)
+ {
+ rtx x = simplify_unary_operation (code, GET_MODE_INNER (mode),
+ CONST_VECTOR_ELT (trueop, i),
+ GET_MODE_INNER (opmode));
+ if (!x)
+ return 0;
+ RTVEC_ELT (v, i) = x;
+ }
+ return gen_rtx_CONST_VECTOR (mode, v);
+ }
+
/* The order of these tests is critical so that, for example, we don't
check the wrong mode (input vs. output) for a conversion operation,
such as FIX. At some point, this should be simplified. */
val = exact_log2 (arg0 & (- arg0)) + 1;
break;
+ case CLZ:
+ arg0 &= GET_MODE_MASK (mode);
+ if (arg0 == 0 && CLZ_DEFINED_VALUE_AT_ZERO (mode, val))
+ ;
+ else
+ val = GET_MODE_BITSIZE (mode) - floor_log2 (arg0) - 1;
+ break;
+
+ case CTZ:
+ arg0 &= GET_MODE_MASK (mode);
+ if (arg0 == 0)
+ {
+ /* Even if the value at zero is undefined, we have to come
+ up with some replacement. Seems good enough. */
+ if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, val))
+ val = GET_MODE_BITSIZE (mode);
+ }
+ else
+ val = exact_log2 (arg0 & -arg0);
+ break;
+
+ case POPCOUNT:
+ arg0 &= GET_MODE_MASK (mode);
+ val = 0;
+ while (arg0)
+ val++, arg0 &= arg0 - 1;
+ break;
+
+ case PARITY:
+ arg0 &= GET_MODE_MASK (mode);
+ val = 0;
+ while (arg0)
+ val++, arg0 &= arg0 - 1;
+ val &= 1;
+ break;
+
case TRUNCATE:
val = arg0;
break;
case FFS:
hv = 0;
if (l1 == 0)
- lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1;
+ {
+ if (h1 == 0)
+ lv = 0;
+ else
+ lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & -h1) + 1;
+ }
else
- lv = exact_log2 (l1 & (-l1)) + 1;
+ lv = exact_log2 (l1 & -l1) + 1;
+ break;
+
+ case CLZ:
+ hv = 0;
+ if (h1 != 0)
+ lv = GET_MODE_BITSIZE (mode) - floor_log2 (h1) - 1
+ - HOST_BITS_PER_WIDE_INT;
+ else if (l1 != 0)
+ lv = GET_MODE_BITSIZE (mode) - floor_log2 (l1) - 1;
+ else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, lv))
+ lv = GET_MODE_BITSIZE (mode);
+ break;
+
+ case CTZ:
+ hv = 0;
+ if (l1 != 0)
+ lv = exact_log2 (l1 & -l1);
+ else if (h1 != 0)
+ lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & -h1);
+ else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, lv))
+ lv = GET_MODE_BITSIZE (mode);
+ break;
+
+ case POPCOUNT:
+ hv = 0;
+ lv = 0;
+ while (l1)
+ lv++, l1 &= l1 - 1;
+ while (h1)
+ lv++, h1 &= h1 - 1;
+ break;
+
+ case PARITY:
+ hv = 0;
+ lv = 0;
+ while (l1)
+ lv++, l1 &= l1 - 1;
+ while (h1)
+ lv++, h1 &= h1 - 1;
+ lv &= 1;
break;
case TRUNCATE:
else if (GET_CODE (trueop) == CONST_DOUBLE
&& GET_MODE_CLASS (mode) == MODE_FLOAT)
{
- REAL_VALUE_TYPE d;
+ REAL_VALUE_TYPE d, t;
REAL_VALUE_FROM_CONST_DOUBLE (d, trueop);
switch (code)
{
case SQRT:
- /* We don't attempt to optimize this. */
- return 0;
+ if (HONOR_SNANS (mode) && real_isnan (&d))
+ return 0;
+ real_sqrt (&t, mode, &d);
+ d = t;
+ break;
+ case ABS:
+ d = REAL_VALUE_ABS (d);
+ break;
+ case NEG:
+ d = REAL_VALUE_NEGATE (d);
+ break;
+ case FLOAT_TRUNCATE:
+ d = real_value_truncate (mode, d);
+ break;
+ case FLOAT_EXTEND:
+ /* All this does is change the mode. */
+ break;
+ case FIX:
+ real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL);
+ break;
- case ABS: d = REAL_VALUE_ABS (d); break;
- case NEG: d = REAL_VALUE_NEGATE (d); break;
- case FLOAT_TRUNCATE: d = real_value_truncate (mode, d); break;
- case FLOAT_EXTEND: /* All this does is change the mode. */ break;
- case FIX: d = REAL_VALUE_RNDZINT (d); break;
- case UNSIGNED_FIX: d = REAL_VALUE_UNSIGNED_RNDZINT (d); break;
default:
abort ();
}
else
{
enum rtx_code reversed;
+ rtx temp;
+
/* There are some simplifications we can do even if the operands
aren't constant. */
switch (code)
if (mode == BImode && GET_RTX_CLASS (GET_CODE (op)) == '<'
&& ((reversed = reversed_comparison_code (op, NULL_RTX))
!= UNKNOWN))
- return gen_rtx_fmt_ee (reversed,
- op_mode, XEXP (op, 0), XEXP (op, 1));
+ return simplify_gen_relational (reversed, op_mode, op_mode,
+ 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, op_mode, op_mode,
+ 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, mode, 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));
+
+ /* (neg (plus A B)) is canonicalized to (minus (neg A) B). */
+ if (GET_CODE (op) == PLUS
+ && !HONOR_SIGNED_ZEROS (mode)
+ && !HONOR_SIGN_DEPENDENT_ROUNDING (mode))
+ {
+ 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:
}
}
\f
+/* 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 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)))
+ {
+ tem = simplify_binary_operation (code, mode, XEXP (op0, 1), op1);
+ if (! tem)
+ return tem;
+ return simplify_gen_binary (code, mode, XEXP (op0, 0), tem);
+ }
+
+ /* 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);
+ }
+
+ /* 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));
+ }
+
+ /* Canonicalize x op (y op c) as (x op y) op c. */
+ if (GET_CODE (op1) == code
+ && associative_constant_p (XEXP (op1, 1)))
+ {
+ tem = simplify_gen_binary (code, mode, op0, XEXP (op1, 0));
+ return simplify_gen_binary (code, mode, tem, XEXP (op1, 1));
+ }
+
+ return 0;
+}
+
/* Simplify a binary operation CODE with result mode MODE, operating on OP0
and OP1. Return 0 if no simplification is possible.
Don't use this for relational operations such as EQ or LT.
Use simplify_relational_operation instead. */
rtx
-simplify_binary_operation (code, mode, op0, op1)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op0, op1;
+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;
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;
+
+ if (op0_n_elts != n_elts || op1_n_elts != n_elts)
+ abort ();
+
+ 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;
+ }
+
+ return gen_rtx_CONST_VECTOR (mode, v);
+ }
+
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_CODE (trueop0) == CONST_DOUBLE
&& GET_CODE (trueop1) == CONST_DOUBLE
f0 = real_value_truncate (mode, f0);
f1 = real_value_truncate (mode, f1);
+ if (HONOR_SNANS (mode)
+ && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
+ return 0;
+
if (code == DIV
- && !MODE_HAS_INFINITIES (mode)
- && REAL_VALUES_EQUAL (f1, dconst0))
+ && REAL_VALUES_EQUAL (f1, dconst0)
+ && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
return 0;
REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
neg_double (l2, h2, &lv, &hv);
l2 = lv, h2 = hv;
- /* .. fall through ... */
+ /* Fall through.... */
case PLUS:
add_double (l1, h1, l2, h2, &lv, &hv);
{
case PLUS:
/* Maybe simplify x + 0 to x. The two expressions are equivalent
- when x is NaN, infinite, or finite and non-zero. They aren't
+ 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))
if (INTEGRAL_MODE_P (mode)
&& GET_CODE (op0) == NOT
&& trueop1 == const1_rtx)
- return gen_rtx_NEG (mode, XEXP (op0, 0));
+ 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
&& 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)
+ {
+ tem = simplify_associative_operation (code, mode, op0, op1);
+ if (tem)
+ return tem;
+ }
break;
case COMPARE:
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 non-zero.
+ 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 gen_rtx_NEG (mode, op1);
+ return simplify_gen_unary (NEG, mode, op1, mode);
/* (-1 - a) is ~a. */
if (trueop0 == constm1_rtx)
- return gen_rtx_NOT (mode, op1);
+ 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,
/* (x - (x & y)) -> (x & ~y) */
if (GET_CODE (op1) == AND)
{
- if (rtx_equal_p (op0, XEXP (op1, 0)))
- return simplify_gen_binary (AND, mode, op0,
- gen_rtx_NOT (mode, XEXP (op1, 1)));
- if (rtx_equal_p (op0, XEXP (op1, 1)))
- return simplify_gen_binary (AND, mode, op0,
- gen_rtx_NOT (mode, XEXP (op1, 0)));
- }
+ 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;
case MULT:
if (trueop1 == constm1_rtx)
- {
- tem = simplify_unary_operation (NEG, mode, op0, mode);
-
- return tem ? tem : gen_rtx_NEG (mode, op0);
- }
+ 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
&& ! side_effects_p (op0))
return op1;
- /* In IEEE floating point, x*1 is not equivalent to x for nans.
- However, ANSI says we can drop signals,
- so we can do this anyway. */
- if (trueop1 == CONST1_RTX (mode))
+ /* 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
&& (width <= HOST_BITS_PER_WIDE_INT
|| val != HOST_BITS_PER_WIDE_INT - 1)
&& ! rtx_equal_function_value_matters)
- return gen_rtx_ASHIFT (mode, op0, GEN_INT (val));
+ 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
REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
if (REAL_VALUES_EQUAL (d, dconst2))
- return gen_rtx_PLUS (mode, op0, copy_rtx (op0));
+ return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0));
if (REAL_VALUES_EQUAL (d, dconstm1))
- return gen_rtx_NEG (mode, op0);
+ return simplify_gen_unary (NEG, mode, op0, mode);
+ }
+
+ /* 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;
&& ! 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 (GET_CODE (trueop1) == CONST_INT
&& ((INTVAL (trueop1) & GET_MODE_MASK (mode))
== GET_MODE_MASK (mode)))
- return gen_rtx_NOT (mode, op0);
+ 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:
&& ! 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:
below). */
if (GET_CODE (trueop1) == CONST_INT
&& (arg1 = exact_log2 (INTVAL (trueop1))) > 0)
- return gen_rtx_LSHIFTRT (mode, op0, GEN_INT (arg1));
+ return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (arg1));
- /* ... fall through ... */
+ /* Fall through.... */
case DIV:
if (trueop1 == CONST1_RTX (mode))
if (! REAL_VALUES_EQUAL (d, dconst0))
{
REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
- return gen_rtx_MULT (mode, op0,
- CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+ tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+ return simplify_gen_binary (MULT, mode, op0, tem);
}
}
break;
/* Handle modulus by power of two (mod with 1 handled below). */
if (GET_CODE (trueop1) == CONST_INT
&& exact_log2 (INTVAL (trueop1)) > 0)
- return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
+ return simplify_gen_binary (AND, mode, op0,
+ GEN_INT (INTVAL (op1) - 1));
- /* ... fall through ... */
+ /* Fall through.... */
case MOD:
if ((trueop0 == const0_rtx || trueop1 == const1_rtx)
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 ASHIFTRT:
case LSHIFTRT:
if (trueop1 == const0_rtx)
return op0;
break;
case SMIN:
- if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (trueop1) == CONST_INT
+ 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;
- else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+ 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
+ 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;
- else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+ 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;
- else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+ 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;
- else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+ 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:
/* ??? 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
+ {
+ if (!VECTOR_MODE_P (GET_MODE (trueop0))
+ || (GET_MODE_INNER (mode)
+ != GET_MODE_INNER (GET_MODE (trueop0)))
+ || GET_CODE (trueop1) != PARALLEL)
+ abort ();
+
+ if (GET_CODE (trueop0) == 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 (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);
+ }
+ }
+ 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));
+
+ 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++)
+ {
+ 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);
+ }
+ 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 ();
}
> (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:
abort ();
}
};
static int
-simplify_plus_minus_op_data_cmp (p1, p2)
- const void *p1;
- const void *p2;
+simplify_plus_minus_op_data_cmp (const void *p1, const void *p2)
{
const struct simplify_plus_minus_op_data *d1 = p1;
const struct simplify_plus_minus_op_data *d2 = p2;
}
static rtx
-simplify_plus_minus (code, mode, op0, op1, force)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op0, op1;
- int force;
+simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
+ rtx op1, int force)
{
struct simplify_plus_minus_op_data ops[8];
rtx result, tem;
int first, negate, changed;
int i, j;
- memset ((char *) ops, 0, sizeof ops);
+ memset (ops, 0, sizeof ops);
/* Set up the two operands and then expand them until nothing has been
changed. If we run out of room in our array, give up; this should
it returns either const_true_rtx or const0_rtx. */
rtx
-simplify_relational_operation (code, mode, op0, op1)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op0, op1;
+simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
+ rtx op0, rtx op1)
{
int equal, op0lt, op0ltu, op1lt, op1ltu;
rtx tem;
/* 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
-#ifdef HAVE_cc0
- || op0 == cc0_rtx
-#endif
- )
+ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC || CC0_P (op0))
return 0;
/* Make sure the constant is second. */
return const0_rtx;
/* For modes without NaNs, if the two operands are equal, we know the
- result. */
- if (!HONOR_NANS (GET_MODE (trueop0)) && rtx_equal_p (trueop0, trueop1))
+ result except if they have side-effects. */
+ if (! HONOR_NANS (GET_MODE (trueop0))
+ && rtx_equal_p (trueop0, trueop1)
+ && ! side_effects_p (trueop0))
equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
/* If the operands are floating-point constants, see if we can fold
switch (code)
{
case EQ:
- /* References to the frame plus a constant or labels cannot
- be zero, but a SYMBOL_REF can due to #pragma weak. */
- if (((NONZERO_BASE_PLUS_P (op0) && trueop1 == const0_rtx)
- || GET_CODE (trueop0) == LABEL_REF)
-#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
- /* On some machines, the ap reg can be 0 sometimes. */
- && op0 != arg_pointer_rtx
-#endif
- )
+ if (trueop1 == const0_rtx && nonzero_address_p (op0))
return const0_rtx;
break;
case NE:
- if (((NONZERO_BASE_PLUS_P (op0) && trueop1 == const0_rtx)
- || GET_CODE (trueop0) == LABEL_REF)
-#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
- && op0 != arg_pointer_rtx
-#endif
- )
+ if (trueop1 == const0_rtx && nonzero_address_p (op0))
return const_true_rtx;
break;
case LT:
/* Optimize abs(x) < 0.0. */
- if (trueop1 == CONST0_RTX (mode))
+ if (trueop1 == CONST0_RTX (mode) && !HONOR_SNANS (mode))
{
tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
: trueop0;
tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
: trueop0;
if (GET_CODE (tem) == ABS)
- return const1_rtx;
+ return const_true_rtx;
+ }
+ break;
+
+ case UNGE:
+ /* Optimize ! (abs(x) < 0.0). */
+ if (trueop1 == CONST0_RTX (mode))
+ {
+ tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
+ : trueop0;
+ if (GET_CODE (tem) == ABS)
+ return const_true_rtx;
}
break;
a constant. Return 0 if no simplifications is possible. */
rtx
-simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
- enum rtx_code code;
- enum machine_mode mode, op0_mode;
- rtx op0, op1, op2;
+simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
+ enum machine_mode op0_mode, rtx op0, rtx op1,
+ rtx op2)
{
unsigned int width = GET_MODE_BITSIZE (mode);
/* Convert a == b ? b : a to "a". */
if (GET_CODE (op0) == NE && ! side_effects_p (op0)
- && (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
+ && !HONOR_NANS (mode)
&& rtx_equal_p (XEXP (op0, 0), op1)
&& rtx_equal_p (XEXP (op0, 1), op2))
return op1;
else if (GET_CODE (op0) == EQ && ! side_effects_p (op0)
- && (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
+ && !HONOR_NANS (mode)
&& rtx_equal_p (XEXP (op0, 1), op1)
&& rtx_equal_p (XEXP (op0, 0), op2))
return op2;
}
}
break;
+ case VEC_MERGE:
+ if (GET_MODE (op0) != mode
+ || GET_MODE (op1) != mode
+ || !VECTOR_MODE_P (mode))
+ abort ();
+ op2 = avoid_constant_pool_reference (op2);
+ if (GET_CODE (op2) == CONST_INT)
+ {
+ int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+ unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+ int mask = (1 << n_elts) - 1;
+
+ if (!(INTVAL (op2) & mask))
+ return op1;
+ if ((INTVAL (op2) & mask) == mask)
+ return op0;
+
+ op0 = avoid_constant_pool_reference (op0);
+ op1 = avoid_constant_pool_reference (op1);
+ if (GET_CODE (op0) == CONST_VECTOR
+ && GET_CODE (op1) == CONST_VECTOR)
+ {
+ rtvec v = rtvec_alloc (n_elts);
+ unsigned int i;
+
+ for (i = 0; i < n_elts; i++)
+ RTVEC_ELT (v, i) = (INTVAL (op2) & (1 << i)
+ ? CONST_VECTOR_ELT (op0, i)
+ : CONST_VECTOR_ELT (op1, i));
+ return gen_rtx_CONST_VECTOR (mode, v);
+ }
+ }
+ break;
default:
abort ();
/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
Return 0 if no simplifications is possible. */
rtx
-simplify_subreg (outermode, op, innermode, byte)
- rtx op;
- unsigned int byte;
- enum machine_mode outermode, innermode;
+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
if (GET_CODE (op) == CONST_VECTOR)
{
int elt_size = GET_MODE_SIZE (GET_MODE_INNER (innermode));
- int offset = byte / elt_size;
+ const unsigned int offset = byte / elt_size;
rtx elt;
if (GET_MODE_INNER (innermode) == outermode)
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)
{
}
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);
+ sum = (sum << shift) + (INTVAL (elt) & unit_mask);
}
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 (high, sum, outermode);
+ return immed_double_const (sum, high, outermode);
else
return NULL_RTX;
}
int subbyte = byte % elt_size;
op = simplify_subreg (new_mode, op, innermode, byte - subbyte);
- if (! op)
- return NULL_RTX;
+ if (! op)
+ return NULL_RTX;
return simplify_subreg (outermode, op, new_mode, subbyte);
}
- else if (GET_MODE_CLASS (outermode) != MODE_VECTOR_INT
- && GET_MODE_CLASS (outermode) != MODE_VECTOR_FLOAT)
+ else if (GET_MODE_CLASS (outermode) == MODE_INT)
/* This shouldn't happen, but let's not do anything stupid. */
return NULL_RTX;
}
int offset, part;
unsigned HOST_WIDE_INT val = 0;
- if (GET_MODE_CLASS (outermode) == MODE_VECTOR_INT
- || GET_MODE_CLASS (outermode) == MODE_VECTOR_FLOAT)
+ if (VECTOR_MODE_P (outermode))
{
/* Construct a CONST_VECTOR from individual subregs. */
enum machine_mode submode = GET_MODE_INNER (outermode);
/* 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. */
- elt = simplify_subreg (submode, op, innermode, byte);
+ 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;
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)
+ if (subreg_lowpart_offset (outermode, innermode) == byte
+ && GET_CODE (op) != CONST_VECTOR)
{
rtx new = gen_lowpart_if_possible (outermode, op);
if (new)
return new;
}
+ if (GET_MODE_CLASS (outermode) != MODE_INT
+ && GET_MODE_CLASS (outermode) != MODE_CC)
+ {
+ enum machine_mode new_mode = int_mode_for_mode (outermode);
+
+ 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);
+ }
+ }
+
offset = byte * BITS_PER_UNIT;
switch (GET_CODE (op))
{
if (GET_CODE (op) == CONST_INT)
val = INTVAL (op);
- /* We don't handle synthetizing of non-integral constants yet. */
+ /* We don't handle synthesizing of non-integral constants yet. */
if (GET_MODE_CLASS (outermode) != MODE_INT)
return NULL_RTX;
return NULL_RTX;
}
- /* Recurse for futher possible simplifications. */
+ /* Recurse for further possible simplifications. */
new = simplify_subreg (outermode, SUBREG_REG (op),
GET_MODE (SUBREG_REG (op)),
final_offset);
if (REG_P (op)
&& (! REG_FUNCTION_VALUE_P (op)
|| ! rtx_equal_function_value_matters)
-#ifdef CLASS_CANNOT_CHANGE_MODE
- && ! (CLASS_CANNOT_CHANGE_MODE_P (outermode, innermode)
+ && REGNO (op) < FIRST_PSEUDO_REGISTER
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ && ! (REG_CANNOT_CHANGE_MODE_P (REGNO (op), innermode, outermode)
&& GET_MODE_CLASS (innermode) != MODE_COMPLEX_INT
- && GET_MODE_CLASS (innermode) != MODE_COMPLEX_FLOAT
- && (TEST_HARD_REG_BIT
- (reg_class_contents[(int) CLASS_CANNOT_CHANGE_MODE],
- REGNO (op))))
+ && GET_MODE_CLASS (innermode) != MODE_COMPLEX_FLOAT)
#endif
- && REGNO (op) < FIRST_PSEUDO_REGISTER
&& ((reload_completed && !frame_pointer_needed)
|| (REGNO (op) != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& REGNO (op) != ARG_POINTER_REGNUM
#endif
- && REGNO (op) != STACK_POINTER_REGNUM)
+ && REGNO (op) != STACK_POINTER_REGNUM
+ && subreg_offset_representable_p (REGNO (op), innermode,
+ byte, outermode))
{
- int final_regno = subreg_hard_regno (gen_rtx_SUBREG (outermode, op, byte),
- 0);
+ rtx tem = gen_rtx_SUBREG (outermode, op, byte);
+ int final_regno = subreg_hard_regno (tem, 0);
/* ??? 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. */
+ 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))
{
- rtx x = gen_rtx_REG (outermode, final_regno);
+ rtx x = gen_rtx_REG_offset (op, outermode, final_regno, byte);
/* Propagate original regno. We don't have any way to specify
- the offset inside orignal regno, so do so only for lowpart.
+ the offset inside original regno, so do so only for lowpart.
The information is used only by alias analysis that can not
grog partial register anyway. */
/* Make a SUBREG operation or equivalent if it folds. */
rtx
-simplify_gen_subreg (outermode, op, innermode, byte)
- rtx op;
- unsigned int byte;
- enum machine_mode outermode, innermode;
+simplify_gen_subreg (enum machine_mode outermode, rtx op,
+ enum machine_mode innermode, unsigned int byte)
{
rtx new;
/* Little bit of sanity checking. */
This is the preferred entry point into the simplification routines;
however, we still allow passes to call the more specific routines.
- Right now GCC has three (yes, three) major bodies of RTL simplficiation
+ Right now GCC has three (yes, three) major bodies of RTL simplification
code that need to be unified.
1. fold_rtx in cse.c. This code uses various CSE specific
simplification and 1 for tree simplification. */
rtx
-simplify_rtx (x)
- rtx x;
+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))
{
XEXP (x, 0), GET_MODE (XEXP (x, 0)));
case 'c':
if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
- {
- rtx tem;
+ return simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0));
- tem = XEXP (x, 0);
- XEXP (x, 0) = XEXP (x, 1);
- XEXP (x, 1) = tem;
- return simplify_binary_operation (code, mode,
- XEXP (x, 0), XEXP (x, 1));
- }
+ /* Fall through.... */
case '2':
return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
XEXP (x, 2));
case '<':
- return simplify_relational_operation (code,
+ 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':
- /* The only case we try to handle is a SUBREG. */
if (code == SUBREG)
return simplify_gen_subreg (mode, SUBREG_REG (x),
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
- return NULL;
+ if (code == CONSTANT_P_RTX)
+ {
+ if (CONSTANT_P (XEXP (x, 0)))
+ return const1_rtx;
+ }
+ break;
+
+ case 'o':
+ if (code == LO_SUM)
+ {
+ /* Convert (lo_sum (high FOO) FOO) to FOO. */
+ if (GET_CODE (XEXP (x, 0)) == HIGH
+ && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
+ return XEXP (x, 1);
+ }
+ break;
+
default:
- return NULL;
+ break;
}
+ return NULL;
}