#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 "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "function.h"
#include "expr.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
#include "target.h"
#define HWI_SIGN_EXTEND(low) \
((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
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 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
neg_const_int (enum machine_mode mode, rtx i)
{
return gen_int_mode (- INTVAL (i), 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));
}
rtx
simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
rtx op1)
{
rtx tem;
if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
&& swap_commutative_operands_p (op0, op1))
tem = op0, op0 = op1, op1 = tem;
tem = simplify_binary_operation (code, mode, op0, op1);
if (tem)
return tem;
if (code == PLUS || code == MINUS)
{
tem = simplify_plus_minus (code, mode, op0, op1, 1);
if (tem)
return tem;
}
return gen_rtx_fmt_ee (code, mode, op0, op1);
}
rtx
avoid_constant_pool_reference (rtx x)
{
rtx c, tmp, addr;
enum machine_mode cmode;
switch (GET_CODE (x))
{
case MEM:
break;
case FLOAT_EXTEND:
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);
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;
c = get_pool_constant (addr);
cmode = get_pool_mode (addr);
if (cmode != GET_MODE (x))
{
c = simplify_subreg (GET_MODE (x), c, cmode, 0);
return c ? c : x;
}
return c;
}
rtx
simplify_gen_unary (enum rtx_code code, enum machine_mode mode, rtx op,
enum machine_mode op_mode)
{
rtx tem;
if ((tem = simplify_unary_operation (code, mode, op, op_mode)) != 0)
return tem;
return gen_rtx_fmt_e (code, mode, op);
}
rtx
simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
enum machine_mode op0_mode, rtx op0, rtx op1, rtx op2)
{
rtx tem;
if (0 != (tem = simplify_ternary_operation (code, mode, op0_mode,
op0, op1, op2)))
return tem;
return gen_rtx_fmt_eee (code, mode, op0, op1, op2);
}
rtx
simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
enum machine_mode cmp_mode, rtx op0, rtx op1)
{
rtx tem;
if (0 != (tem = simplify_relational_operation (code, mode, cmp_mode,
op0, op1)))
return tem;
return gen_rtx_fmt_ee (code, mode, op0, op1);
}
rtx
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 == old_rtx)
return new_rtx;
switch (GET_RTX_CLASS (code))
{
case RTX_UNARY:
op0 = XEXP (x, 0);
op_mode = GET_MODE (op0);
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 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 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_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 RTX_TERNARY:
case RTX_BITFIELD_OPS:
op0 = XEXP (x, 0);
op_mode = GET_MODE (op0);
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 RTX_EXTRA:
if (code == SUBREG)
{
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,
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
return op0 ? op0 : x;
}
break;
case RTX_OBJ:
if (code == MEM)
{
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_rtx, new_rtx);
op1 = simplify_replace_rtx (XEXP (x, 1), old_rtx, new_rtx);
if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1))
return op1;
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return x;
return gen_rtx_LO_SUM (mode, op0, op1);
}
else if (code == REG)
{
if (REG_P (old_rtx) && REGNO (x) == REGNO (old_rtx))
return new_rtx;
}
break;
default:
break;
}
return x;
}
rtx
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)
{
gcc_assert (VECTOR_MODE_P (mode));
if (GET_MODE (trueop) != VOIDmode)
{
if (!VECTOR_MODE_P (GET_MODE (trueop)))
gcc_assert (GET_MODE_INNER (mode) == GET_MODE (trueop));
else
gcc_assert (GET_MODE_INNER (mode) == GET_MODE_INNER
(GET_MODE (trueop)));
}
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);
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);
}
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)
{
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;
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),
GET_MODE_INNER (opmode));
if (!x)
return 0;
RTVEC_ELT (v, i) = x;
}
return gen_rtx_CONST_VECTOR (mode, v);
}
if (code == FLOAT && GET_MODE (trueop) == VOIDmode
&& (GET_CODE (trueop) == CONST_DOUBLE || GET_CODE (trueop) == 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);
else
lv = CONST_DOUBLE_LOW (trueop), hv = CONST_DOUBLE_HIGH (trueop);
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))
{
HOST_WIDE_INT hv, lv;
REAL_VALUE_TYPE d;
if (GET_CODE (trueop) == CONST_INT)
lv = INTVAL (trueop), hv = HWI_SIGN_EXTEND (lv);
else
lv = CONST_DOUBLE_LOW (trueop), hv = CONST_DOUBLE_HIGH (trueop);
if (op_mode == VOIDmode)
{
if (hv < 0)
return 0;
}
else if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
;
else
hv = 0, lv &= GET_MODE_MASK (op_mode);
REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
d = real_value_truncate (mode, d);
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
if (GET_CODE (trueop) == CONST_INT
&& width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
HOST_WIDE_INT arg0 = INTVAL (trueop);
HOST_WIDE_INT val;
switch (code)
{
case NOT:
val = ~ arg0;
break;
case NEG:
val = - arg0;
break;
case ABS:
val = (arg0 >= 0 ? arg0 : - arg0);
break;
case FFS:
arg0 &= GET_MODE_MASK (mode);
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)
{
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 ZERO_EXTEND:
gcc_assert (op_mode != VOIDmode);
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
gcc_assert (width == GET_MODE_BITSIZE (op_mode));
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
else
return 0;
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode)
op_mode = mode;
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
gcc_assert (width == GET_MODE_BITSIZE (op_mode));
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
{
val
= arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
if (val
& ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1)))
val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
}
else
return 0;
break;
case SQRT:
case FLOAT_EXTEND:
case FLOAT_TRUNCATE:
case SS_TRUNCATE:
case US_TRUNCATE:
return 0;
default:
gcc_unreachable ();
}
val = trunc_int_for_mode (val, mode);
return GEN_INT (val);
}
else if (GET_MODE (trueop) == VOIDmode
&& width <= HOST_BITS_PER_WIDE_INT * 2
&& (GET_CODE (trueop) == CONST_DOUBLE
|| GET_CODE (trueop) == 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);
else
l1 = INTVAL (trueop), h1 = HWI_SIGN_EXTEND (l1);
switch (code)
{
case NOT:
lv = ~ l1;
hv = ~ h1;
break;
case NEG:
neg_double (l1, h1, &lv, &hv);
break;
case ABS:
if (h1 < 0)
neg_double (l1, h1, &lv, &hv);
else
lv = l1, hv = h1;
break;
case FFS:
hv = 0;
if (l1 == 0)
{
if (h1 == 0)
lv = 0;
else
lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & -h1) + 1;
}
else
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:
lv = l1, hv = h1;
break;
case ZERO_EXTEND:
gcc_assert (op_mode != VOIDmode);
if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
hv = 0;
lv = l1 & GET_MODE_MASK (op_mode);
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode
|| GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
else
{
lv = l1 & GET_MODE_MASK (op_mode);
if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT
&& (lv & ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (op_mode) - 1))) != 0)
lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
hv = HWI_SIGN_EXTEND (lv);
}
break;
case SQRT:
return 0;
default:
return 0;
}
return immed_double_const (lv, hv, mode);
}
else if (GET_CODE (trueop) == CONST_DOUBLE
&& GET_MODE_CLASS (mode) == MODE_FLOAT)
{
REAL_VALUE_TYPE d, t;
REAL_VALUE_FROM_CONST_DOUBLE (d, trueop);
switch (code)
{
case SQRT:
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:
break;
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 (trueop));
for (i = 0; i < 4; i++)
tmp[i] = ~tmp[i];
real_from_target (&d, tmp, mode);
}
default:
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
&& GET_MODE_CLASS (mode) == MODE_INT
&& width <= 2*HOST_BITS_PER_WIDE_INT && width > 0)
{
HOST_WIDE_INT xh, xl, th, tl;
REAL_VALUE_TYPE x, t;
REAL_VALUE_FROM_CONST_DOUBLE (x, trueop);
switch (code)
{
case FIX:
if (REAL_VALUE_ISNAN (x))
return const0_rtx;
if (width > HOST_BITS_PER_WIDE_INT)
{
th = ((unsigned HOST_WIDE_INT) 1
<< (width - HOST_BITS_PER_WIDE_INT - 1)) - 1;
tl = -1;
}
else
{
th = 0;
tl = ((unsigned HOST_WIDE_INT) 1 << (width - 1)) - 1;
}
real_from_integer (&t, VOIDmode, tl, th, 0);
if (REAL_VALUES_LESS (t, x))
{
xh = th;
xl = tl;
break;
}
if (width > HOST_BITS_PER_WIDE_INT)
{
th = (HOST_WIDE_INT) -1 << (width - HOST_BITS_PER_WIDE_INT - 1);
tl = 0;
}
else
{
th = -1;
tl = (HOST_WIDE_INT) -1 << (width - 1);
}
real_from_integer (&t, VOIDmode, tl, th, 0);
if (REAL_VALUES_LESS (x, t))
{
xh = th;
xl = tl;
break;
}
REAL_VALUE_TO_INT (&xl, &xh, x);
break;
case UNSIGNED_FIX:
if (REAL_VALUE_ISNAN (x) || REAL_VALUE_NEGATIVE (x))
return const0_rtx;
if (width == 2*HOST_BITS_PER_WIDE_INT)
{
th = -1;
tl = -1;
}
else if (width >= HOST_BITS_PER_WIDE_INT)
{
th = ((unsigned HOST_WIDE_INT) 1
<< (width - HOST_BITS_PER_WIDE_INT)) - 1;
tl = -1;
}
else
{
th = 0;
tl = ((unsigned HOST_WIDE_INT) 1 << width) - 1;
}
real_from_integer (&t, VOIDmode, tl, th, 1);
if (REAL_VALUES_LESS (t, x))
{
xh = th;
xl = tl;
break;
}
REAL_VALUE_TO_INT (&xl, &xh, x);
break;
default:
gcc_unreachable ();
}
return immed_double_const (xl, xh, mode);
}
else
{
enum rtx_code reversed;
rtx temp;
switch (code)
{
case NOT:
if (GET_CODE (op) == NOT)
return XEXP (op, 0);
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));
if (GET_CODE (op) == PLUS
&& XEXP (op, 1) == constm1_rtx)
return simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
if (GET_CODE (op) == NEG)
return plus_constant (XEXP (op, 0), -1);
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);
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);
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 == -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));
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:
if (GET_CODE (op) == NEG)
return XEXP (op, 0);
if (GET_CODE (op) == PLUS
&& XEXP (op, 1) == const1_rtx
&& GET_CODE (XEXP (op, 0)) != SYMBOL_REF)
return simplify_gen_unary (NOT, mode, XEXP (op, 0), mode);
if (GET_CODE (op) == NOT)
return plus_constant (XEXP (op, 0), 1);
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)
&& GET_CODE (XEXP (op, 0)) != SYMBOL_REF)
{
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));
}
temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 1));
}
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));
}
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));
}
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));
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:
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);
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:
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;
}
}
static rtx
simplify_associative_operation (enum rtx_code code, enum machine_mode mode,
rtx op0, rtx op1)
{
rtx tem;
if (GET_CODE (op1) == code)
{
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));
}
if (! swap_commutative_operands_p (op1, op0))
return simplify_gen_binary (code, mode, op1, op0);
tem = op0;
op0 = op1;
op1 = tem;
}
if (GET_CODE (op0) == code)
{
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));
}
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);
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;
}
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;
gcc_assert (GET_RTX_CLASS (code) != RTX_COMPARE);
gcc_assert (GET_RTX_CLASS (code) != RTX_COMM_COMPARE);
if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
&& swap_commutative_operands_p (op0, op1))
{
tem = op0, op0 = op1, op1 = tem;
}
trueop0 = avoid_constant_pool_reference (op0);
trueop1 = avoid_constant_pool_reference (op1);
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;
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 (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
&& mode == GET_MODE (op0) && mode == GET_MODE (op1))
{
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;
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);
if (HONOR_SNANS (mode)
&& (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
return 0;
if (code == DIV
&& REAL_VALUES_EQUAL (f1, dconst0)
&& (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
return 0;
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);
switch (code)
{
case PLUS:
if (s0 != s1)
return 0;
break;
case MINUS:
if (s0 == s1)
return 0;
break;
case DIV:
return 0;
default:
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))))
return 0;
REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
value = real_value_truncate (mode, value);
return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
}
}
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, lt;
HOST_WIDE_INT h1, h2, hv, ht;
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);
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);
switch (code)
{
case MINUS:
neg_double (l2, h2, &lv, &hv);
l2 = lv, h2 = hv;
case PLUS:
add_double (l1, h1, l2, h2, &lv, &hv);
break;
case MULT:
mul_double (l1, h1, l2, h2, &lv, &hv);
break;
case DIV:
if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2,
&lv, &hv, <, &ht))
return 0;
break;
case MOD:
if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2,
<, &ht, &lv, &hv))
return 0;
break;
case UDIV:
if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2,
&lv, &hv, <, &ht))
return 0;
break;
case UMOD:
if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2,
<, &ht, &lv, &hv))
return 0;
break;
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:
if (SHIFT_COUNT_TRUNCATED)
l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0;
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
rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
break;
default:
return 0;
}
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)
{
switch (code)
{
case PLUS:
if (!HONOR_SIGNED_ZEROS (mode) && trueop1 == CONST0_RTX (mode))
return op0;
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));
if (INTEGRAL_MODE_P (mode)
&& GET_CODE (op0) == NOT
&& trueop1 == const1_rtx)
return simplify_gen_unary (NEG, mode, XEXP (op0, 0), mode);
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));
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 (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 (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;
}
}
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 (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;
if (FLOAT_MODE_P (mode)
&& flag_unsafe_math_optimizations)
{
tem = simplify_associative_operation (code, mode, op0, op1);
if (tem)
return tem;
}
break;
case COMPARE:
#ifdef HAVE_cc0
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
&& trueop1 == CONST0_RTX (mode))
return op0;
#endif
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)
{
rtx xop00 = XEXP (op0, 0);
rtx xop10 = XEXP (op1, 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
return xop00;
}
break;
case MINUS:
if (rtx_equal_p (trueop0, trueop1)
&& ! side_effects_p (op0)
&& (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations))
return CONST0_RTX (mode);
if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode))
return simplify_gen_unary (NEG, mode, op1, mode);
if (trueop0 == constm1_rtx
&& GET_CODE (op1) != SYMBOL_REF )
return simplify_gen_unary (NOT, mode, op1, mode);
if (!(HONOR_SIGNED_ZEROS (mode)
&& HONOR_SIGN_DEPENDENT_ROUNDING (mode))
&& trueop1 == CONST0_RTX (mode))
return op0;
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 (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 (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;
}
}
if (GET_CODE (op1) == NEG)
return simplify_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
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));
}
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;
if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
return simplify_gen_binary (PLUS, mode,
op0,
neg_const_int (mode, op1));
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;
case MULT:
if (trueop1 == constm1_rtx)
return simplify_gen_unary (NEG, mode, op0, mode);
if (!HONOR_NANS (mode)
&& !HONOR_SIGNED_ZEROS (mode)
&& trueop1 == CONST0_RTX (mode)
&& ! side_effects_p (op0))
return op1;
if (!HONOR_SNANS (mode)
&& trueop1 == CONST1_RTX (mode))
return op0;
if (GET_CODE (trueop1) == CONST_INT
&& (val = exact_log2 (INTVAL (trueop1))) >= 0
&& (width <= HOST_BITS_PER_WIDE_INT
|| val != HOST_BITS_PER_WIDE_INT - 1))
return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
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);
if (REAL_VALUES_EQUAL (d, dconst2))
return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0));
if (REAL_VALUES_EQUAL (d, dconstm1))
return simplify_gen_unary (NEG, mode, op0, mode);
}
if (! FLOAT_MODE_P (mode)
|| flag_unsafe_math_optimizations)
{
tem = simplify_associative_operation (code, mode, op0, op1);
if (tem)
return tem;
}
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;
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;
if ((GET_CODE (op1) == CONST_INT
|| GET_CODE (op1) == CONST_DOUBLE)
&& mode_signbit_p (mode, op1))
return simplify_gen_binary (PLUS, mode, op0, op1);
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;
case AND:
if (trueop1 == const0_rtx && ! side_effects_p (op0))
return const0_rtx;
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;
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;
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);
for (which = 0; which < 2; which++)
{
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;
}
}
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 UDIV:
if (trueop0 == const0_rtx)
return side_effects_p (op1)
? simplify_gen_binary (AND, mode, op1, const0_rtx)
: const0_rtx;
if (trueop1 == const1_rtx)
{
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;
}
if (GET_CODE (trueop1) == CONST_INT
&& (arg1 = exact_log2 (INTVAL (trueop1))) > 0)
return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (arg1));
break;
case DIV:
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
if (trueop0 == CONST0_RTX (mode)
&& !HONOR_NANS (mode)
&& !HONOR_SIGNED_ZEROS (mode)
&& ! side_effects_p (op1))
return op0;
if (trueop1 == CONST1_RTX (mode)
&& !HONOR_SNANS (mode))
return op0;
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, dconstm1)
&& !HONOR_SNANS (mode))
return simplify_gen_unary (NEG, mode, op0, mode);
if (flag_unsafe_math_optimizations
&& !REAL_VALUES_EQUAL (d, dconst0))
{
REAL_ARITHMETIC (d, RDIV_EXPR, dconst1, d);
tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
return simplify_gen_binary (MULT, mode, op0, tem);
}
}
}
else
{
if (trueop0 == const0_rtx)
return side_effects_p (op1)
? simplify_gen_binary (AND, mode, op1, const0_rtx)
: const0_rtx;
if (trueop1 == const1_rtx)
{
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;
}
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:
if (trueop0 == const0_rtx)
return side_effects_p (op1)
? simplify_gen_binary (AND, mode, op1, const0_rtx)
: const0_rtx;
if (trueop1 == const1_rtx)
return side_effects_p (op0)
? simplify_gen_binary (AND, mode, op0, const0_rtx)
: const0_rtx;
if (GET_CODE (trueop1) == CONST_INT
&& exact_log2 (INTVAL (trueop1)) > 0)
return simplify_gen_binary (AND, mode, op0,
GEN_INT (INTVAL (op1) - 1));
break;
case MOD:
if (trueop0 == const0_rtx)
return side_effects_p (op1)
? simplify_gen_binary (AND, mode, op1, const0_rtx)
: const0_rtx;
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:
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 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 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 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:
return 0;
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)
{
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;
gcc_assert (XVECLEN (trueop1, 0) == (int) n_elts);
for (i = 0; i < n_elts; i++)
{
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:
{
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))
{
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:
gcc_unreachable ();
}
return 0;
}
arg0 = INTVAL (trueop0);
arg1 = INTVAL (trueop1);
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;
}
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:
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);
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:
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:
return 0;
default:
gcc_unreachable ();
}
val = trunc_int_for_mode (val, mode);
return GEN_INT (val);
}
struct simplify_plus_minus_op_data
{
rtx op;
int neg;
};
static int
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;
return (commutative_operand_precedence (d2->op)
- commutative_operand_precedence (d1->op));
}
static rtx
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 n_ops = 2, input_ops = 2, input_consts = 0, n_consts;
int first, changed;
int i, j;
memset (ops, 0, sizeof ops);
ops[0].op = op0;
ops[0].neg = 0;
ops[1].op = op1;
ops[1].neg = (code == MINUS);
do
{
changed = 0;
for (i = 0; i < n_ops; i++)
{
rtx this_op = ops[i].op;
int this_neg = ops[i].neg;
enum rtx_code this_code = GET_CODE (this_op);
switch (this_code)
{
case PLUS:
case MINUS:
if (n_ops == 7)
return NULL_RTX;
ops[n_ops].op = XEXP (this_op, 1);
ops[n_ops].neg = (this_code == MINUS) ^ this_neg;
n_ops++;
ops[i].op = XEXP (this_op, 0);
input_ops++;
changed = 1;
break;
case NEG:
ops[i].op = XEXP (this_op, 0);
ops[i].neg = ! this_neg;
changed = 1;
break;
case CONST:
if (n_ops < 7
&& GET_CODE (XEXP (this_op, 0)) == PLUS
&& CONSTANT_P (XEXP (XEXP (this_op, 0), 0))
&& CONSTANT_P (XEXP (XEXP (this_op, 0), 1)))
{
ops[i].op = XEXP (XEXP (this_op, 0), 0);
ops[n_ops].op = XEXP (XEXP (this_op, 0), 1);
ops[n_ops].neg = this_neg;
n_ops++;
input_consts++;
changed = 1;
}
break;
case NOT:
if (n_ops != 7)
{
ops[n_ops].op = constm1_rtx;
ops[n_ops++].neg = this_neg;
ops[i].op = XEXP (this_op, 0);
ops[i].neg = !this_neg;
changed = 1;
}
break;
case CONST_INT:
if (this_neg)
{
ops[i].op = neg_const_int (mode, this_op);
ops[i].neg = 0;
changed = 1;
}
break;
default:
break;
}
}
}
while (changed);
if (n_ops <= 2 && !force)
return NULL_RTX;
for (i = 0; i < n_ops; i++)
if (GET_CODE (ops[i].op) == CONST)
input_consts++;
first = 1;
do
{
changed = first;
for (i = 0; i < n_ops - 1; i++)
for (j = i + 1; j < n_ops; j++)
{
rtx lhs = ops[i].op, rhs = ops[j].op;
int lneg = ops[i].neg, rneg = ops[j].neg;
if (lhs != 0 && rhs != 0
&& (! first || (CONSTANT_P (lhs) && CONSTANT_P (rhs))))
{
enum rtx_code ncode = PLUS;
if (lneg != rneg)
{
ncode = MINUS;
if (lneg)
tem = lhs, lhs = rhs, rhs = tem;
}
else if (swap_commutative_operands_p (lhs, rhs))
tem = lhs, lhs = rhs, rhs = tem;
tem = simplify_binary_operation (ncode, mode, lhs, rhs);
if (tem
&& ! (GET_CODE (tem) == CONST
&& GET_CODE (XEXP (tem, 0)) == ncode
&& XEXP (XEXP (tem, 0), 0) == lhs
&& XEXP (XEXP (tem, 0), 1) == rhs)
&& ! (first
&& GET_CODE (tem) == NOT
&& XEXP (tem, 0) == rhs))
{
lneg &= rneg;
if (GET_CODE (tem) == NEG)
tem = XEXP (tem, 0), lneg = !lneg;
if (GET_CODE (tem) == CONST_INT && lneg)
tem = neg_const_int (mode, tem), lneg = 0;
ops[i].op = tem;
ops[i].neg = lneg;
ops[j].op = NULL_RTX;
changed = 1;
}
}
}
first = 0;
}
while (changed);
for (i = 0, j = 0; j < n_ops; j++)
if (ops[j].op)
ops[i++] = ops[j];
n_ops = i;
qsort (ops, n_ops, sizeof (*ops), simplify_plus_minus_op_data_cmp);
if (n_ops == 2
&& GET_CODE (ops[1].op) == CONST_INT
&& CONSTANT_P (ops[0].op)
&& ops[0].neg)
return gen_rtx_fmt_ee (MINUS, mode, ops[1].op, ops[0].op);
if (n_ops > 1
&& GET_CODE (ops[n_ops - 1].op) == CONST_INT
&& CONSTANT_P (ops[n_ops - 2].op))
{
rtx value = ops[n_ops - 1].op;
if (ops[n_ops - 1].neg ^ ops[n_ops - 2].neg)
value = neg_const_int (mode, value);
ops[n_ops - 2].op = plus_constant (ops[n_ops - 2].op, INTVAL (value));
n_ops--;
}
n_consts = 0;
for (i = 0; i < n_ops; i++)
if (GET_CODE (ops[i].op) == CONST)
n_consts++;
if (!force
&& (n_ops + n_consts > input_ops
|| (n_ops + n_consts == input_ops && n_consts <= input_consts)))
return NULL_RTX;
for (i = 0; i < n_ops && ops[i].neg; i++)
continue;
if (i == n_ops)
ops[0].op = gen_rtx_NEG (mode, ops[0].op);
else if (i != 0)
{
tem = ops[0].op;
ops[0] = ops[i];
ops[i].op = tem;
ops[i].neg = 1;
}
result = ops[0].op;
for (i = 1; i < n_ops; i++)
result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS,
mode, result, ops[i].op);
return result;
}
rtx
simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
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;
}
if (swap_commutative_operands_p (op0, op1)
|| (op0 == const0_rtx && op1 != const0_rtx))
tem = op0, op0 = op1, op1 = tem, code = swap_condition (code);
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);
}
rtx
simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
enum machine_mode cmp_mode, rtx op0, rtx op1)
{
if (GET_CODE (op1) == CONST_INT)
{
if (INTVAL (op1) == 0 && COMPARISON_P (op0))
{
if (code == NE)
{
if (GET_MODE (op0) == cmp_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));
}
}
}
return NULL_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;
gcc_assert (mode != VOIDmode
|| (GET_MODE (op0) == VOIDmode
&& GET_MODE (op1) == VOIDmode));
if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC || CC0_P (op0))
return 0;
if (swap_commutative_operands_p (op0, op1))
{
tem = op0, op0 = op1, op1 = tem;
code = swap_condition (code);
}
trueop0 = avoid_constant_pool_reference (op0);
trueop1 = avoid_constant_pool_reference (op1);
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))
&& ((code != EQ && code != NE) || ! nonzero_address_p (tem))
&& code != GTU && code != GEU && code != LTU && code != LEU)
return simplify_const_relational_operation (signed_condition (code),
mode, tem, const0_rtx);
if (flag_unsafe_math_optimizations && code == ORDERED)
return const_true_rtx;
if (flag_unsafe_math_optimizations && code == UNORDERED)
return const0_rtx;
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;
else if (GET_CODE (trueop0) == CONST_DOUBLE
&& GET_CODE (trueop1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (trueop0)) == MODE_FLOAT)
{
REAL_VALUE_TYPE d0, d1;
REAL_VALUE_FROM_CONST_DOUBLE (d0, trueop0);
REAL_VALUE_FROM_CONST_DOUBLE (d1, trueop1);
if (REAL_VALUE_ISNAN (d0) || REAL_VALUE_ISNAN (d1))
switch (code)
{
case UNEQ:
case UNLT:
case UNGT:
case UNLE:
case UNGE:
case NE:
case UNORDERED:
return const_true_rtx;
case EQ:
case LT:
case GT:
case LE:
case GE:
case LTGT:
case ORDERED:
return const0_rtx;
default:
return 0;
}
equal = REAL_VALUES_EQUAL (d0, d1);
op0lt = op0ltu = REAL_VALUES_LESS (d0, d1);
op1lt = op1ltu = REAL_VALUES_LESS (d1, d0);
}
else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
&& (GET_CODE (trueop0) == CONST_DOUBLE
|| GET_CODE (trueop0) == CONST_INT)
&& (GET_CODE (trueop1) == CONST_DOUBLE
|| GET_CODE (trueop1) == CONST_INT))
{
int width = GET_MODE_BITSIZE (mode);
HOST_WIDE_INT l0s, h0s, l1s, h1s;
unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
if (GET_CODE (trueop0) == CONST_DOUBLE)
{
l0u = l0s = CONST_DOUBLE_LOW (trueop0);
h0u = h0s = CONST_DOUBLE_HIGH (trueop0);
}
else
{
l0u = l0s = INTVAL (trueop0);
h0u = h0s = HWI_SIGN_EXTEND (l0s);
}
if (GET_CODE (trueop1) == CONST_DOUBLE)
{
l1u = l1s = CONST_DOUBLE_LOW (trueop1);
h1u = h1s = CONST_DOUBLE_HIGH (trueop1);
}
else
{
l1u = l1s = INTVAL (trueop1);
h1u = h1s = HWI_SIGN_EXTEND (l1s);
}
if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
{
l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
l0s |= ((HOST_WIDE_INT) (-1) << width);
if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
l1s |= ((HOST_WIDE_INT) (-1) << width);
}
if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
h0u = h1u = 0, h0s = HWI_SIGN_EXTEND (l0s), h1s = HWI_SIGN_EXTEND (l1s);
equal = (h0u == h1u && l0u == l1u);
op0lt = (h0s < h1s || (h0s == h1s && l0u < l1u));
op1lt = (h1s < h0s || (h1s == h0s && l1u < l0u));
op0ltu = (h0u < h1u || (h0u == h1u && l0u < l1u));
op1ltu = (h1u < h0u || (h1u == h0u && l1u < l0u));
}
else
{
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:
if (rtx_equal_p (trueop1, mmin))
tem = const_true_rtx;
else
break;
case LEU:
case LE:
if (rtx_equal_p (trueop1, mmax))
tem = const_true_rtx;
break;
case GTU:
case GT:
if (rtx_equal_p (trueop1, mmax))
tem = const0_rtx;
break;
case LTU:
case LT:
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:
if (trueop1 == const0_rtx && nonzero_address_p (op0))
return const0_rtx;
break;
case NE:
if (trueop1 == const0_rtx && nonzero_address_p (op0))
return const_true_rtx;
break;
case LT:
if (trueop1 == CONST0_RTX (mode) && !HONOR_SNANS (mode))
{
tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
: trueop0;
if (GET_CODE (tem) == ABS)
return const0_rtx;
}
break;
case GE:
if (trueop1 == CONST0_RTX (mode) && !HONOR_NANS (mode))
{
tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
: trueop0;
if (GET_CODE (tem) == ABS)
return const_true_rtx;
}
break;
case UNGE:
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;
default:
break;
}
return 0;
}
switch (code)
{
case EQ:
case UNEQ:
return equal ? const_true_rtx : const0_rtx;
case NE:
case LTGT:
return ! equal ? const_true_rtx : const0_rtx;
case LT:
case UNLT:
return op0lt ? const_true_rtx : const0_rtx;
case GT:
case UNGT:
return op1lt ? const_true_rtx : const0_rtx;
case LTU:
return op0ltu ? const_true_rtx : const0_rtx;
case GTU:
return op1ltu ? const_true_rtx : const0_rtx;
case LE:
case UNLE:
return equal || op0lt ? const_true_rtx : const0_rtx;
case GE:
case UNGE:
return equal || op1lt ? const_true_rtx : const0_rtx;
case LEU:
return equal || op0ltu ? const_true_rtx : const0_rtx;
case GEU:
return equal || op1ltu ? const_true_rtx : const0_rtx;
case ORDERED:
return const_true_rtx;
case UNORDERED:
return const0_rtx;
default:
gcc_unreachable ();
}
}
rtx
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);
if (width == 0)
width = HOST_BITS_PER_WIDE_INT;
switch (code)
{
case SIGN_EXTRACT:
case ZERO_EXTRACT:
if (GET_CODE (op0) == CONST_INT
&& GET_CODE (op1) == CONST_INT
&& GET_CODE (op2) == CONST_INT
&& ((unsigned) INTVAL (op1) + (unsigned) INTVAL (op2) <= width)
&& width <= (unsigned) HOST_BITS_PER_WIDE_INT)
{
HOST_WIDE_INT val = INTVAL (op0);
if (BITS_BIG_ENDIAN)
val >>= (GET_MODE_BITSIZE (op0_mode)
- INTVAL (op2) - INTVAL (op1));
else
val >>= INTVAL (op2);
if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
{
val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1;
if (code == SIGN_EXTRACT
&& (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1))))
val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1);
}
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
return GEN_INT (val);
}
break;
case IF_THEN_ELSE:
if (GET_CODE (op0) == CONST_INT)
return op0 != const0_rtx ? op1 : op2;
if (rtx_equal_p (op1, op2) && ! side_effects_p (op0))
return op1;
if (GET_CODE (op0) == NE
&& ! side_effects_p (op0)
&& ! HONOR_NANS (mode)
&& ! HONOR_SIGNED_ZEROS (mode)
&& ((rtx_equal_p (XEXP (op0, 0), op1)
&& rtx_equal_p (XEXP (op0, 1), op2))
|| (rtx_equal_p (XEXP (op0, 0), op2)
&& rtx_equal_p (XEXP (op0, 1), op1))))
return op1;
if (GET_CODE (op0) == EQ
&& ! side_effects_p (op0)
&& ! HONOR_NANS (mode)
&& ! HONOR_SIGNED_ZEROS (mode)
&& ((rtx_equal_p (XEXP (op0, 0), op1)
&& rtx_equal_p (XEXP (op0, 1), op2))
|| (rtx_equal_p (XEXP (op0, 0), op2)
&& rtx_equal_p (XEXP (op0, 1), op1))))
return op2;
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 (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT)
{
HOST_WIDE_INT t = INTVAL (op1);
HOST_WIDE_INT f = INTVAL (op2);
if (t == STORE_FLAG_VALUE && f == 0)
code = GET_CODE (op0);
else if (t == 0 && f == STORE_FLAG_VALUE)
{
enum rtx_code tmp;
tmp = reversed_comparison_code (op0, NULL_RTX);
if (tmp == UNKNOWN)
break;
code = tmp;
}
else
break;
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));
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:
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)
{
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:
gcc_unreachable ();
}
return 0;
}
static rtx
simplify_immed_subreg (enum machine_mode outermode, rtx op,
enum machine_mode innermode, unsigned int byte)
{
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;
if (GET_MODE_CLASS (outermode) == MODE_CC && GET_CODE (op) == CONST_INT)
return op;
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;
}
gcc_assert (BITS_PER_UNIT % value_bit == 0);
gcc_assert (elem_bitsize % BITS_PER_UNIT == 0);
for (elem = 0; elem < num_elem; elem++)
{
unsigned char * vp;
rtx el = elems[elem];
{
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))
{
case CONST_INT:
for (i = 0;
i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
i += value_bit)
*vp++ = INTVAL (el) >> i;
for (; i < elem_bitsize; i += value_bit)
*vp++ = INTVAL (el) < 0 ? -1 : 0;
break;
case CONST_DOUBLE:
if (GET_MODE (el) == VOIDmode)
{
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)
{
*vp++
= CONST_DOUBLE_HIGH (el) >> (i - HOST_BITS_PER_WIDE_INT);
i += value_bit;
}
for (; i < max_bitsize; i += value_bit)
*vp++ = 0;
}
else
{
long tmp[max_bitsize / 32];
int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
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));
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;
}
for (; i < elem_bitsize; i += value_bit)
*vp++ = 0;
}
break;
default:
gcc_unreachable ();
}
}
if (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode))
{
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);
}
gcc_assert (byte < GET_MODE_SIZE (innermode));
value_start = byte * (BITS_PER_UNIT / value_bit);
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;
}
outer_class = GET_MODE_CLASS (outer_submode);
elem_bitsize = GET_MODE_BITSIZE (outer_submode);
gcc_assert (elem_bitsize % value_bit == 0);
gcc_assert (elem_bitsize + value_start * value_bit <= max_bitsize);
for (elem = 0; elem < num_elem; elem++)
{
unsigned char *vp;
{
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 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));
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];
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;
}
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;
}
rtx
simplify_subreg (enum machine_mode outermode, rtx op,
enum machine_mode innermode, unsigned int byte)
{
gcc_assert (innermode != VOIDmode);
gcc_assert (outermode != VOIDmode);
gcc_assert (innermode != BLKmode);
gcc_assert (outermode != BLKmode);
gcc_assert (GET_MODE (op) == innermode
|| GET_MODE (op) == VOIDmode);
gcc_assert ((byte % GET_MODE_SIZE (outermode)) == 0);
gcc_assert (byte < GET_MODE_SIZE (innermode));
if (outermode == innermode && !byte)
return op;
if (GET_CODE (op) == CONST_INT
|| GET_CODE (op) == CONST_DOUBLE
|| GET_CODE (op) == CONST_VECTOR)
return simplify_immed_subreg (outermode, op, innermode, byte);
if (GET_CODE (op) == SUBREG)
{
enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
int final_offset = byte + SUBREG_BYTE (op);
rtx newx;
if (outermode == innermostmode
&& byte == 0 && SUBREG_BYTE (op) == 0)
return SUBREG_REG (op);
if (byte == 0 && GET_MODE_SIZE (innermode) < GET_MODE_SIZE (outermode))
{
int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
if (WORDS_BIG_ENDIAN)
final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
final_offset += difference % UNITS_PER_WORD;
}
if (SUBREG_BYTE (op) == 0
&& GET_MODE_SIZE (innermostmode) < GET_MODE_SIZE (innermode))
{
int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (innermode));
if (WORDS_BIG_ENDIAN)
final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
final_offset += difference % UNITS_PER_WORD;
}
if (GET_MODE_SIZE (innermostmode) > GET_MODE_SIZE (outermode))
{
if (final_offset < 0)
return NULL_RTX;
if (final_offset % GET_MODE_SIZE (outermode)
|| (unsigned) final_offset >= GET_MODE_SIZE (innermostmode))
return NULL_RTX;
}
else
{
int offset = 0;
int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (outermode));
if (WORDS_BIG_ENDIAN)
offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset += difference % UNITS_PER_WORD;
if (offset == final_offset)
final_offset = 0;
else
return NULL_RTX;
}
newx = simplify_subreg (outermode, SUBREG_REG (op),
GET_MODE (SUBREG_REG (op)),
final_offset);
if (newx)
return newx;
return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
}
if (REG_P (op)
&& 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)
#endif
&& ((reload_completed && !frame_pointer_needed)
|| (REGNO (op) != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& REGNO (op) != HARD_FRAME_POINTER_REGNUM
#endif
))
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& REGNO (op) != ARG_POINTER_REGNUM
#endif
&& REGNO (op) != STACK_POINTER_REGNUM
&& 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);
if (HARD_REGNO_MODE_OK (final_regno, outermode)
|| ! HARD_REGNO_MODE_OK (REGNO (op), innermode))
{
rtx x = gen_rtx_REG_offset (op, outermode, final_regno, byte);
if (subreg_lowpart_offset (outermode, innermode) == byte)
ORIGINAL_REGNO (x) = ORIGINAL_REGNO (op);
return x;
}
}
if (MEM_P (op)
&& ! mode_dependent_address_p (XEXP (op, 0))
&& (! MEM_VOLATILE_P (op)
|| ! have_insn_for (SET, innermode))
&& GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
return adjust_address_nv (op, outermode, byte);
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;
final_offset = byte % (GET_MODE_UNIT_SIZE (innermode));
res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
if (res)
return res;
return gen_rtx_SUBREG (outermode, part, final_offset);
}
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 (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);
}
if (GET_CODE (op) == ZERO_EXTEND
&& bitpos >= GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))))
return CONST0_RTX (outermode);
}
return NULL_RTX;
}
rtx
simplify_gen_subreg (enum machine_mode outermode, rtx op,
enum machine_mode innermode, unsigned int byte)
{
rtx newx;
gcc_assert (innermode != VOIDmode);
gcc_assert (outermode != VOIDmode);
gcc_assert (innermode != BLKmode);
gcc_assert (outermode != BLKmode);
gcc_assert (GET_MODE (op) == innermode
|| GET_MODE (op) == VOIDmode);
gcc_assert ((byte % GET_MODE_SIZE (outermode)) == 0);
gcc_assert (byte < GET_MODE_SIZE (innermode));
newx = simplify_subreg (outermode, op, innermode, byte);
if (newx)
return newx;
if (GET_CODE (op) == SUBREG || GET_MODE (op) == VOIDmode
|| (REG_P (op) && REGNO (op) < FIRST_PSEUDO_REGISTER))
return NULL_RTX;
return gen_rtx_SUBREG (outermode, op, byte);
}
rtx
simplify_rtx (rtx x)
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
switch (GET_RTX_CLASS (code))
{
case RTX_UNARY:
return simplify_unary_operation (code, mode,
XEXP (x, 0), GET_MODE (XEXP (x, 0)));
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));
case RTX_BIN_ARITH:
return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
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 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));
break;
case RTX_OBJ:
if (code == LO_SUM)
{
if (GET_CODE (XEXP (x, 0)) == HIGH
&& rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
return XEXP (x, 1);
}
break;
default:
break;
}
return NULL;
}